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

unknown variant ${identifier.attr.subattr}, expected one of Null, Bool, Number... #233

Open
vlad-ivanov-name opened this issue May 23, 2023 · 1 comment
Labels
bug Something isn't working

Comments

@vlad-ivanov-name
Copy link

vlad-ivanov-name commented May 23, 2023

I ran into a seemingly complex edge case where deserialisation fails when an untagged enum is used together with deserialize_with. In the following example:

#![allow(unused)]

use serde::Deserialize;
use serde::Deserializer;
use serde_json;
use anyhow;
use hcl;

pub fn de_transform<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
    where
        D: Deserializer<'de>,
{
    let expressions: Result<Vec<hcl::Expression>, _> = Deserialize::deserialize(deserializer);

    if let Err(ref e) = expressions {
        eprintln!("{:?}", e)
    }

    Ok(expressions?.iter().map(|expr| format!("Parsed expression: {}", expr.to_string())).collect::<Vec<_>>())
}

#[derive(Deserialize, Debug, Clone)]
struct A {
    #[serde(deserialize_with = "de_transform")]
    pub value_a: Vec<String>
}

#[derive(Deserialize, Debug, Clone)]
struct B {
    #[serde(deserialize_with = "de_transform")]
    pub value_b: Vec<String>
}

#[derive(Deserialize, Debug, Clone)]
#[serde(untagged)]
enum Test {
    A(A),
    B(B),
}

#[derive(Deserialize, Debug, Clone)]
struct AX {
    pub value_a: Vec<String>
}

#[derive(Deserialize, Debug, Clone)]
struct BX {
    pub value_b: Vec<String>
}

#[derive(Deserialize, Debug, Clone)]
#[serde(untagged)]
enum TestX {
    AX(AX),
    BX(BX),
}


fn main() -> anyhow::Result<()> {
    let data = r#"
        value_a = [ident.prop1.subprop1, ident.prop2.subprop2]
    "#;

    let get_body = || {
        let body: Result<hcl::Body, _> = hcl::from_str(&data);
        body
    };

    let body_1 = get_body();
    // Will print body as expected
    println!("{:?}", body_1);

    let de_result_1: Result<A, _> = hcl::from_body(body_1?);
    // Will print struct as expected
    println!("{:?}", de_result_1);

    let de_result_2: Result<TestX, _> = hcl::from_body(get_body()?);
    // Will match enum case AX as expected
    println!("{:?}", de_result_2);

    // Will fail
    let de_result_3: Result<Test, _> = hcl::from_body(get_body()?);
    println!("{:?}", de_result_3);

    Ok(())
}

de_transform function will fail with:

Message { msg: "unknown variant `${ident.prop1.subprop1}`, expected one of `Null`, `Bool`, `Number`, `String`, `Array`, `Object`, `TemplateExpr`, `Variable`, `Traversal`, `FuncCall`, `Parenthesis`, `Conditional`, `Operation`, `ForExpr`, `Raw`", location: None }

I haven't been able to find a workaround unfortunately

@martinohmann
Copy link
Owner

martinohmann commented Jun 2, 2023

Thanks for the detailed reproducer!

Since deserializing A by itself works, but Test doesn't it seems to be related to the enum handling within the hcl deserializer when custom deserialize logic is injected as in your case.

There are some hardcoded assumptions in there right now to ensure correct roundtripping of the hcl-rs types themselves. The error message surfaces that. This needs to be adjusted to handle more "foreign" type layouts passed in.

I'd be happy to get this fixed, but unfortunately I'm currently busy with some personal obligations over then next 2 or 3 months, so I'm not sure I can get my hands (and head) on it anytime soon.

But if you feel like digging deeper into it I'm happy to receive a PR in the meantime 😉

@martinohmann martinohmann added the bug Something isn't working label Jun 2, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

2 participants