この記事は公開されてから1年以上経過しています。情報が古い可能性がありますので、ご注意ください。
こんにちは、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"