hcl-rs icon indicating copy to clipboard operation
hcl-rs copied to clipboard

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

Open vlad-ivanov-name opened this issue 1 year ago • 1 comments

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

vlad-ivanov-name avatar May 23 '23 10:05 vlad-ivanov-name