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

YAML: incorrect parsing of curly brackets #439

Open odyslam opened 1 year ago

odyslam commented 1 year ago

When parsing curly brackets, even as a string ("${env_var}"), it decodes the curly brackets to it's ASCII code. This is probably a bug of the underlying yaml library (yaml-rust).

I solved it by not using curly brackets.

matthiasbeyer commented 1 year ago

Hi! Thanks for your report. I suspect that this is indeed an issue of the underlying yaml library, since this library does not interfere with parsing AFAIK.

I'll leave this open for discoverability until the yaml lib is fixed. If someone wants to contribute a test for this, I'd love to have that!

polarathene commented 8 months ago

Unable to reproduce (not much of a reproduction example given) with:

More context is required for this to be valid/reproducible.


Yaml file:

key: "${env_var}"

Rust reproduction main.rs:

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

fn main() {
  let sources = ConfigSource::vec_example();

  for s in sources {
    let c = s.get_config();
    let test: Test = c.try_deserialize().unwrap();
    println!("{:#?}", test);
  }
}

#[allow(dead_code)]
#[derive(Debug, Deserialize)]
struct Test {
  key: String,
}

fn make<T>(source: T) -> Config
where
  T: config::Source + Send + Sync + 'static,
{
    Config::builder()
        .add_source(source)
        .build()
        .unwrap()
}

// Overkill:
enum ConfigSource {
  String(File<config::FileSourceString, FileFormat>),
  File(File<config::FileSourceFile, FileFormat>),
}

impl ConfigSource {
  fn vec_example() -> Vec<ConfigSource> {
    vec![
      ConfigSource::String(
        File::from_str(r#"key: "${env_var}""#, FileFormat::Yaml)
      ),
      ConfigSource::File(
        File::new("test.yml", FileFormat::Yaml)
      )
    ]
  }

  fn get_config(self) -> Config {
    match self {
      Self::String(s) => make(s),
      Self::File(f) => make(f),
    }
  }
}

Output:

Test {
    key: "${env_var}",
}
Test {
    key: "${env_var}",
}

Raw Config (not deserialized into Test struct):

Config {
    defaults: {},
    overrides: {},
    sources: [],
    cache: Value {
        origin: None,
        kind: Table(
            {
                "key": Value {
                    origin: None,
                    kind: String(
                        "${env_var}",
                    ),
                },
            },
        ),
    },
}