转换错误类型

在某些情况下,某种格式的值必须包含在另一种格式的数据中。例如,Terraform 中的IAM策略被表示为包含在HCL配置中的JSON字符串。

将内部值视为简单的字符串可能很简单,但是如果我们要操作内部和外部值,则通常将它们一次性序列化和反序列化会更方便。

在这种情况下,偶尔会遇到的绊脚石是正确处理错误。这两种格式(很可能)具有不同的错误类型,因此需要进行一些转换。

此示例显示包含简化IAM策略的简化 HCL 资源。在序列化时,策略文档被表示为 JSON 字符串。

use serde::{Serialize, Deserialize};

#[derive(Serialize, Deserialize)]
struct Resource {
    name: String,

    #[serde(with = "as_json_string")]
    policy: Policy,
}

#[derive(Serialize, Deserialize)]
struct Policy {
    effect: String,
    action: String,
    resource: String,
}

// 用于处理表示为 JSON 字符串的嵌套值的序列化和反序列化逻辑。
mod as_json_string {
    use serde_json;
    use serde::ser::{Serialize, Serializer};
    use serde::de::{Deserialize, DeserializeOwned, Deserializer};

    // 序列化为 JSON 字符串,然后将字符串序列化为输出格式。
    pub fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
    where
        T: Serialize,
        S: Serializer,
    {
        use serde::ser::Error;
        let j = serde_json::to_string(value).map_err(Error::custom)?;
        j.serialize(serializer)
    }

    // 从输入格式中反序列化字符串,然后将该字符串的内容反序列化为 JSON。
    pub fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
    where
        T: DeserializeOwned,
        D: Deserializer<'de>,
    {
        use serde::de::Error;
        let j = String::deserialize(deserializer)?;
        serde_json::from_str(&j).map_err(Error::custom)
    }
}

fn main() {
    let resource = Resource {
        name: "test_policy".to_owned(),
        policy: Policy {
            effect: "Allow".to_owned(),
            action: "s3:ListBucket".to_owned(),
            resource: "arn:aws:s3:::example_bucket".to_owned(),
        },
    };

    let y = serde_yaml::to_string(&resource).unwrap();
    println!("{}", y);
}