serdeを使ってAWSのリージョン名をrusoto_coreのenum Regionにserialize/deserializeする
こんにちは、CX事業本部の夏目です。
RustでAWSのCLIツールを書いているのですが、設定ファイルに書いたリージョン名をrusoto_coreのenumRegion
として扱う方法について共有します。
特に何もしないでserializeしてみる
use rusoto_core::Region; use serde::{Deserialize, Serialize}; fn main() { let data = Bucket { name: "test bucket".to_string(), region: Region::ApNortheast1, }; println!("debug : {:?}", data); let json = serde_json::to_string(&data).unwrap(); println!("serialized : {}", json); let deserialized: Bucket = serde_json::from_str(&json).unwrap(); println!("deserialized: {:?}", deserialized); } #[derive(Debug, Serialize, Deserialize)] struct Bucket { name: String, region: Region, }
/Users/natsume.yuta/.cargo/bin/cargo run --color=always Compiling serde_region v0.1.0 (/Users/natsume.yuta/workspace/rust/serde_region) Finished dev [unoptimized + debuginfo] target(s) in 5.49s Running `target/debug/serde_region` debug : Bucket { name: "test bucket", region: ApNortheast1 } serialized : {"name":"test bucket","region":["ap-northeast-1",null]} deserialized: Bucket { name: "test bucket", region: ApNortheast1 }
rusoto_coreのenum Region
を特に何もせずにserializeしてみる。
Region::ApNortheast1
が["ap-northeast-1", null]
にserializeされている。
deserializeする場合も同様の表現にする必要があるので、このままだと設定ファイルなどに直接使うのは難しい。
"ap-northeast-1"
をRegion::ApNortheast1
として扱うようにしてみる
use rusoto_core::Region; use serde::de::Unexpected; use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer}; use std::str::FromStr; fn main() { let data = Bucket { name: "test bucket".to_string(), region: Region::ApNortheast1, }; println!("debug : {:?}", data); let json = serde_json::to_string(&data).unwrap(); println!("serialized : {}", json); let deserialized: Bucket = serde_json::from_str(&json).unwrap(); println!("deserialized: {:?}", deserialized); } #[derive(Debug, Serialize, Deserialize)] struct Bucket { name: String, #[serde( serialize_with = "custom_serialize", deserialize_with = "custom_deserialize" )] region: Region, } fn custom_serialize<S>(value: &Region, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, { serializer.serialize_str(value.name()) } fn custom_deserialize<'de, D>(deserializer: D) -> Result<Region, D::Error> where D: Deserializer<'de>, { let s = String::deserialize(deserializer)?; match Region::from_str(&s) { Ok(region) => Ok(region), Err(_) => Err(Error::invalid_value( Unexpected::Str(&s), &"invalid region name", )), } }
/Users/natsume.yuta/.cargo/bin/cargo run --color=always Finished dev [unoptimized + debuginfo] target(s) in 0.20s Running `target/debug/serde_region` debug : Bucket { name: "test bucket", region: ApNortheast1 } serialized : {"name":"test bucket","region":"ap-northeast-1"} deserialized: Bucket { name: "test bucket", region: ApNortheast1 }
serializeとdeserializeを自分で実装して、リージョン名を直接enum Region
と解釈するようにしてみた。
crate serde
ではAttributeに#[serde(serialize_with = "$path")]
や#[serde(deserialize_with = "$path")]
を追記することでserializeやdeserializeを任意の関数にすることができます。
渡す関数は次のようにする必要があります (T
はserialize/deserializeしたい値の型)。
- serialize_withでは
fn<S>(value: &T, serializer: S) -> Result<S::Ok, S::Error> where S: serde::Serializer
- deserialize_withでは
fn<'de, D>(deserializer: D) -> Result<T, D::Error> where D: serde::Deserializer
まとめ
以上リージョン名を直接rusoto_coreのenum Region
にserialize/deserializeする方法でした。
なにかの役に立てば幸いです。
今回のコードで使用したCargo.tomlの[dependencies]
[dependencies] rusoto_core = "0.45" serde = {version = "1", features = ["derive"]} serde_json = "1"