Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tagged enums require tag set in both TOML and Env sources #118

Open
RapidPencil opened this issue Jul 9, 2024 · 2 comments
Open

Tagged enums require tag set in both TOML and Env sources #118

RapidPencil opened this issue Jul 9, 2024 · 2 comments
Labels
bug-upstream Bug exists, but only fixable in upstream crate

Comments

@RapidPencil
Copy link

Say you have a serde tagged enum by #[confik(forward_serde(tag = "type"))] in the configuration:

use confik::Configuration;

#[derive(Debug, Clone, Configuration)]
#[confik(forward_serde(tag = "type", rename_all = "snake_case"))]
enum Config {
    Foo(Foo),
    Bar(Bar),
}

#[derive(Debug, Clone, Configuration)]
struct Foo {
    foo: String,
}

#[derive(Debug, Clone, Configuration)]
struct Bar {
    bar: String,
}

fn main() {
    let config = Config::builder()
        .override_with(confik::FileSource::new("config.toml"))
        .override_with(confik::EnvSource::new().allow_secrets())
        .try_build()
        .unwrap();
}

with config.toml

type = "foo"
foo = "hello"

and environment variable

TYPE=foo

The above works.
However, remove either of type = "foo" from config.toml or TYPE=foo from the environment, and this fails with an error:

(...)
missing field `type`
(...)
@tenuous-guidance
Copy link
Contributor

tenuous-guidance commented Jul 26, 2024

There are some limitations to confik that make this harder to avoid, but I think the actual bug is probably in serde. E.g.

use serde::Deserialize;

#[derive(Debug, Clone, Deserialize, Default)]
struct ConfigOuter(#[serde(default)] Config);

#[derive(Debug, Clone, Deserialize, Default)]
#[serde(tag = "type", rename_all = "snake_case")]
enum Config {
    Foo(Foo),
    Bar(Bar),
    #[default]
    Unknown,
}

#[derive(Debug, Clone, Deserialize)]
struct Foo {
    foo: String,
}

#[derive(Debug, Clone, Deserialize)]
struct Bar {
    bar: String,
}

#[test]
fn test() {
    // This fails with:
    //
    // ---- test stdout ----
    // thread 'test' panicked at src/main.rs:27:58:
    // called `Result::unwrap()` on an `Err` value: Error("missing field `type`", line: 1, column: 2)
    // note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    //
    // However it should work and produce `ConfigOuter(Config::Unknown)`

    let config: ConfigOuter = serde_json::from_str("{}").unwrap();
}

@tenuous-guidance
Copy link
Contributor

tenuous-guidance commented Jul 26, 2024

Similarity, serde(other) also fails for your example Config:

use serde::Deserialize;

#[derive(Debug, Clone, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
enum Config {
    Foo(Foo),
    Bar(Bar),
    #[serde(other)]
    Unknown,
}

#[derive(Debug, Clone, Deserialize)]
struct Foo {
    foo: String,
}

#[derive(Debug, Clone, Deserialize)]
struct Bar {
    bar: String,
}

#[test]
fn test() {
    // This fails with:
    //
    // ---- test stdout ----
    // thread 'test' panicked at src/main.rs:27:58:
    // called `Result::unwrap()` on an `Err` value: Error("missing field `type`", line: 1, column: 2)
    // note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
    //
    // However it should work and produce `Config::Unknown`

    let config: Config = serde_json::from_str("{}").unwrap();
}

@robjtede robjtede added the bug-upstream Bug exists, but only fixable in upstream crate label Oct 10, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug-upstream Bug exists, but only fixable in upstream crate
Projects
None yet
Development

No branches or pull requests

3 participants