I am trying to read the config toml
file into a config struct defined locally and I was given this basic error however I wasn't able to see what I have actually missed. I am using the 3rd party crate called config-rs
or config
if we go by its crate name. I also tried using toml
package alone to deserialize the string into my config struct, it gave me the same error.
The error is missing field: parent_url
after updating the toml
I've got error of invalid type: map, expected a sequence
.
I understand this error most likely came straight out of serde::deserialize
however I wasn't able to see how and where my code went wrong.
Creating a new cargo project called test_conf
, then copy and paste following files into the project will give us a minimal reproducible (tested). I've just started out in Rust
so the code may not look well written.
You may have noticed that the we have more dependencies than we needed to reproduce the error in the cargo.toml
; I kept them there as it was the context I ran into the error. This project is a partial from a slightly bigger project.
EDIT: Updated toml
file to reflect suggestions in comment.
// src/main.rs
pub mod configuration;
use toml::de::Error;
use configuration::config::Setting;
use configuration::test_model::TestConfig;
fn main() -> Result<(), Error> {
let path = String::from("test.toml");
// let content = std::fs::read_to_string(&path);
// match content {
// Ok(s) => {
// let config: TestConfig = toml::from_str(&s)?;
// },
// Err(e) => {
// panic!("failed: {}", e);
// }
// }
let site = Setting::new(path);
Ok(())
}
// src/configuration/config.rs
use config::{Config, File, FileFormat};
use std::string::String;
use crate::configuration::test_model::TestConfig;
pub struct Setting
{
pub config_path: String,
configs: TestConfig,
}
impl Setting
{
pub fn new(config_file: String) -> Self {
let parent = "src/configuration";
let config_file_path = [parent, config_file.as_str()].join("/");
let config_map: TestConfig = Setting::build_configs(config_file_path.clone());
Setting {
config_path: config_file_path,
configs: config_map,
}
}
// internal functions
fn build_configs(config_path: String) -> TestConfig
// where
// T: Setup,
{
let config_parser = Config::builder()
.add_source(File::new(&config_path, FileFormat::Toml))
.build();
match config_parser {
Ok(parser) => {
let conf = parser.try_deserialize::<TestConfig>();
match conf {
Ok(settings) => return settings,
Err(ce) => {
eprintln!("configuration parsing error: {}", ce);
std::process::exit(1);
}
}
}
Err(ce) => {
eprintln!("Building configuration parser failed: {}", ce);
std::process::exit(1);
}
}
}
}
// src/configuration/test_model.rs
use serde::{Serialize, Deserialize};
use std::vec::Vec;
#[derive(Serialize, Deserialize, Debug)]
pub struct TestConfig {
pub parent_url: String,
pub parent_folder: String,
pub headers: Vec<Header>,
pub things: Vec<Thing>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Header {
pub header_name: String,
pub header_value: String,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Thing {
pub base_name: String,
pub place: String,
pub requests: Vec<Request>,
}
#[derive(Serialize, Deserialize, Debug)]
pub struct Request {
pub name: String,
pub item_name: String,
pub payload: Payload,
}
#[derive(Serialize, Deserialize, Debug)]
#[allow(non_snake_case)]
pub struct Payload {
pub lists: String,
pub fields: String,
pub hasOption: String,
pub limit: String,
}
// src/configuration/test.toml
parent_url = "initial url"
parent_folder = "/home/some/path"
[headers]
[headers.accept]
header_name = "accept"
header_value = "application/json"
[headers.encoding]
header_name = "accept-encoding"
header_value = "gzip, deflate, br, zstd"
[headers.language]
header_name = "accept-language"
header_value = "en-GB;q=0.5"
[headers.referer]
header_name = "referer"
header_value = "www.com"
[headers.agent]
header_name = "user-agent"
header_value = "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/127.0"
[headers.authority]
header_name = "authority"
header_value = "www.com"
[thing]
base_name = "base_url"
place = "test_folder"
[[thing.requests]]
name = "test_name_one"
item_name = "local_test_one_file.json"
[thing.requests.payload]
lists = "test"
fields = "symbol,symbolName"
hasOption = "true"
limit = "100"
[[thing.requests]]
name = "test_name_two"
item_name = "local_test_two_file.json"
[thing.requests.payload]
lists = "test"
fields = "symbol,symbolName"
hasOption = "true"
limit = "100"
// src/configuration/model.rs
pub mod config;
pub mod test_model;
# cargo.toml
[package]
name = "test_conf"
version = "0.1.0"
edition = "2021"
[[bin]]
name = "test_conf"
test = false
bench = false
[dependencies]
reqwest = { version = "0.12.5", features = ["blocking", "gzip", "json", "cookies"] }
tokio = { version = "1.38.0", features = ["full"] }
futures = { version = "0.3.30", features = ["executor"] }
reqwest_cookie_store = "0.8.0"
serde = { version = "1.0.203", features = ["derive"] }
serde_json = { version = "1.0.117" }
urlencoding = { version = "2.1.3" }
config = { version = "0.14.0", features = ["toml", "ini"]}
toml = { version = "0.8.14" }
thanks to @Timsib and @kmdreko, it is indeed due to invalid toml
structure. Following file fixed my error all in one go.
parent_url = "initial url"
parent_folder = "/home/some/path"
[[headers]]
header_name = "accept"
header_value = "application/json"
[[headers]]
header_name = "accept-encoding"
header_value = "gzip, deflate, br, zstd"
[[headers]]
header_name = "accept-language"
header_value = "en-GB;q=0.5"
[[headers]]
header_name = "referer"
header_value = "www.com"
[[headers]]
header_name = "user-agent"
header_value = "Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/127.0"
[[headers]]
header_name = "authority"
header_value = "www.com"
[[things]]
base_name = "base_url"
place = "test_folder"
[[things.requests]]
name = "test_name_one"
item_name = "local_test_one_file.json"
[things.requests.payload]
lists = "test"
fields = "symbol,symbolName"
hasOption = "true"
limit = "100"
[[things.requests]]
name = "test_name_two"
item_name = "local_test_two_file.json"
[things.requests.payload]
lists = "test"
fields = "symbol,symbolName"
hasOption = "true"
limit = "100"