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

Feature request: #[flatten] on Option<SomeStruct> should raise error when only some fields are set #2793

Open
koraa opened this issue Aug 10, 2024 · 0 comments

Comments

@koraa
Copy link

koraa commented Aug 10, 2024

Feature request demonstrated through accompanying test cases.

Thanks for maintaining this fantastic library :)

Demonstrating test case

Self-contained, easily executed accompanying material as a gist

use serde::{de::DeserializeOwned, Deserialize};

#[derive(PartialEq, Eq, Deserialize, Debug)]
struct OuterWithOpt {
    alpha: u64,
    beta: u64,
    #[serde(flatten)]
    inner: Option<Inner>,
}

impl OuterWithOpt {
    fn new_with_inner(alpha: u64, beta: u64, gamma: u64, delta: u64) -> Self {
        let inner = Some(Inner { gamma, delta });
        Self { alpha, beta, inner }
    }
    fn new_without_inner(alpha: u64, beta: u64) -> Self {
        let inner = None;
        Self { alpha, beta, inner }
    }
}

#[derive(PartialEq, Eq, Deserialize, Debug)]
struct Inner {
    gamma: u64,
    delta: u64,
}

fn assert_de_error<T: DeserializeOwned + std::fmt::Debug>(toml_src: &str, expexted_error: &str) {
    let err = match toml::from_str::<T>(toml_src) {
        Err(e) => e,
        Ok(val) => {
            panic!("Deserialization should have raised error, but produced a result instead.\n    Expected error message: {expexted_error}\n    Result produced instead: {val:?}");
        }
    };
    assert_eq!(err.message(), expexted_error)
}

fn assert_de_succ<T: DeserializeOwned + std::fmt::Debug + Eq>(toml_src: &str, expexted_value: &T) {
    let res = toml::from_str::<T>(toml_src).unwrap();
    assert_eq!(&res, expexted_value)
}

#[test]
fn test() -> anyhow::Result<()> {
    let t_ = "";
    let t_a = "alpha = 42\n";
    let t_ab = "alpha = 42\n beta = 42\n";
    let t_abg = "alpha = 42\n beta = 42\n gamma = 42";
    let t_abgd = "alpha = 42\n beta = 42\n gamma = 42\n delta = 42";
    let t_ab_d = "alpha = 42\n beta = 42\n delta = 42";

    // OuterWithOpt can be successfully deserialized
    let withopt_set = OuterWithOpt::new_with_inner(42, 42, 42, 42);
    let withopt_unset = OuterWithOpt::new_without_inner(42, 42);
    assert_de_succ(t_abgd, &withopt_set);
    assert_de_succ(t_ab, &withopt_unset);

    // Deserializing OuterNoOpt with missing fields in outer struct produces appropriate errors
    assert_de_error::<OuterWithOpt>(t_, "missing field `alpha`");
    assert_de_error::<OuterWithOpt>(t_a, "missing field `beta`");

    // Deserializing OuterNoOpt with missing fields in inner struct produces appropriate errors
    // when only some but not all of Inner's required fields are set
    assert_de_error::<OuterWithOpt>(t_abg, "missing field `delta`. Field `gamma` must be used togetehr with field `delta`. Either remove field `gamma` or add field `delta`.");
    assert_de_error::<OuterWithOpt>(t_ab_d, "missing field `gamma`. Field `delta` must be used togetehr with field `gamma`. Either remove field `delta` or add field `gamma`.");

    Ok(())
}

Test case error output

thread 'test' panicked at lib.rs:32:13:
Deserialization should have raised error, but produced a result instead.
    Expected error message: missing field `delta`. Field `gamma` must be used togetehr with field `delta`. Either remove field `gamma` or add field `delta`.
    Result produced instead: OuterWithOpt { alpha: 42, beta: 42, inner: None }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

1 participant