mehcode / config-rs

⚙️ Layered configuration system for Rust applications (with strong support for 12-factor applications).
Apache License 2.0
2.43k stars 206 forks source link

bug: Using the `serde` attribute `rename` fails since `0.14.0` with a `missing field` error #522

Open gabrielkim13 opened 4 months ago

gabrielkim13 commented 4 months ago

Starting at config@0.14.0, serde renamed fields are no longer supported due to the "missing field" error.


Consider the following minimal reproduceable example:

# Cargo.toml

[package]
name = "config-missing-field"
version = "0.1.0"
edition = "2021"

[dependencies]
config = { version = "0.14", default-features = false, features = ["json"] }
serde = { version = "1", features = ["derive"] }
// main.rs

use config::{Config, File, FileFormat};

#[allow(unused)]
#[derive(serde::Deserialize, Debug)]
struct MyConfig {
    #[serde(rename = "FooBar")]
    foo_bar: String,
}

fn main() {
    const MY_CONFIG: &str = r#"{
        "FooBar": "Hello, world!"
    }"#;

    let cfg = Config::builder()
        .add_source(File::from_str(MY_CONFIG, FileFormat::Json))
        .build()
        .unwrap();

    println!("{cfg:#?}");

    let my_config: MyConfig = cfg.try_deserialize().unwrap();

    // Panics at the previous instruction, with "missing field" error.

    println!("{my_config:#?}");
}

The output is as follows:

❯ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/config-missing-field`
Config {
    defaults: {},
    overrides: {},
    sources: [],
    cache: Value {
        origin: None,
        kind: Table(
            {
                "foobar": Value {
                    origin: None,
                    kind: String(
                        "Hello, world!",
                    ),
                },
            },
        ),
    },
}
thread 'main' panicked at src/main.rs:27:53:
called `Result::unwrap()` on an `Err` value: missing field `FooBar`
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace 

Reverting the dependency back to config@0.13.4 works as expected:

❯ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.02s
     Running `target/debug/config-missing-field`
Config {
    defaults: {},
    overrides: {},
    sources: [],
    cache: Value {
        origin: None,
        kind: Table(
            {
                "FooBar": Value {
                    origin: None,
                    kind: String(
                        "Hello, world!",
                    ),
                },
            },
        ),
    },
}
MyConfig {
    foo_bar: "Hello, world!",
} 
matthiasbeyer commented 4 months ago

Thanks for filing this! Awesome (irony) that our tests did not find this :cry:

gabrielkim13 commented 4 months ago

You're welcome! And thanks for the quick response :)

polarathene commented 4 months ago

Same bug I believe: https://github.com/mehcode/config-rs/issues/531

rename should work presumably if only using lowercase value? Looks like 0.14.0 has some fixes contributed that converted to lowercase that broke serde features.

Lawson-Y commented 2 months ago

I've encountered the same issue. When using config@0.14 in combination with serde, applying #[serde(rename_all = "camelCase")] to handle the yaml configuration file does not work as expected. Below is the configuration file:

dataBase:
  url: "http://www.ss.com.cn"
  userName: "张三"
  password: "1233"
addr: "0.0.0.0:8887"
#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct Conf {
    pub data_base: Option<DataBase>,
    pub addr: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
#[serde(rename_all = "camelCase")]
pub struct DataBase {
    pub url: String,
    pub user_name: String,
    pub password: String,
    #[serde(default)]
    pub data_base_connection_poll: DataBaseConnectionPool,
}

When I reverted the config to version 0.13, this code executed as expected.