diff --git a/rust/yaml/serde_yaml/internally_tagged/Cargo.lock b/rust/yaml/serde_yaml/internally_tagged/Cargo.lock new file mode 100644 index 00000000..3f741847 --- /dev/null +++ b/rust/yaml/serde_yaml/internally_tagged/Cargo.lock @@ -0,0 +1,119 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "firststep" +version = "0.1.0" +dependencies = [ + "serde", + "serde_yaml", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "indexmap" +version = "2.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +dependencies = [ + "equivalent", + "hashbrown", +] + +[[package]] +name = "itoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" + +[[package]] +name = "proc-macro2" +version = "1.0.86" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[package]] +name = "serde" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7253ab4de971e72fb7be983802300c30b5a7f0c2e56fab8abfc6a214307c0094" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.203" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "500cbc0ebeb6f46627f50f3f5811ccf6bf00643be300b4c3eabc0ef55dc5b5ba" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "syn" +version = "2.0.68" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "901fa70d88b9d6c98022e23b4136f9f3e54e4662c3bc1bd1d84a42a9a0f0c1e9" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" diff --git a/rust/yaml/serde_yaml/internally_tagged/Cargo.toml b/rust/yaml/serde_yaml/internally_tagged/Cargo.toml new file mode 100644 index 00000000..cf0bf200 --- /dev/null +++ b/rust/yaml/serde_yaml/internally_tagged/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "firststep" +version = "0.1.0" +edition = "2021" + +[dependencies] +serde = { version = "1.0.203", features = ["derive"] } +serde_yaml = "0.9.34" diff --git a/rust/yaml/serde_yaml/internally_tagged/README.md b/rust/yaml/serde_yaml/internally_tagged/README.md new file mode 100644 index 00000000..9e9424ab --- /dev/null +++ b/rust/yaml/serde_yaml/internally_tagged/README.md @@ -0,0 +1,137 @@ +--- +title: serde_yaml を使って Rust で YAML をパースするやつを Internally tagged で作り直した +author: mikoto2000 +date: 2024/7/5 +--- + +# やること + +[firststep](https://github.com/mikoto2000/TIL/tree/master/rust/yaml/serde_yaml/firststep) で YAML のパースを行ったが、 Internally tagged 形式で構造体を定義したほうがその後の処理がやりやすそうだったのでやってみた。 + + +# 前提 + +- OS: Windows 11 Pro 23H2 ビルド 22631.3737 +- Docker Desktop: Version 4.31.1 (153621) +- rust の環境が構築済みであること。 + - See: [docker-images/rust/Dockerfile at master · mikoto2000/docker-images](https://github.com/mikoto2000/docker-images/blob/master/rust/Dockerfile) + - rustup: 1.27.0 + - cargo: 1.78.0 + - rustc: 1.78.0 +- [firststep](https://github.com/mikoto2000/TIL/tree/master/rust/yaml/serde_yaml/firststep) から修正を行ったので、差分を取るとわかりやすいかもしれません。 + + +# 実装 + +`main.rs`: + +```rs +use std::{fs::File, io::BufReader}; + +use serde::{Deserialize, Serialize}; + +// ビットフラグの 1 ビットを表す構造体 +#[derive(Serialize, Deserialize, Debug)] +struct LayoutItem { + // 表示名 + name: String, + // ビットフラグのビット位置 + position: u8, + // ビットが 1 だった時に表示する値 + true_label: Option, + // ビットが 0 だった時に表示する値 + false_label: Option, +} + +// コンフィグの要素は、以下 5 種類のどれかとなる +// - UINT8 +// - UINT16 +// - UINT32 +// - UINT64 +// - FLAGS +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "type")] +enum ConfigItem { + UINT8(BasicConfigItem), + UINT16(BasicConfigItem), + UINT32(BasicConfigItem), + UINT64(BasicConfigItem), + FLAGS(BitFlagConfigItem), +} + +// 数値データの単位を表す構造体 +#[derive(Serialize, Deserialize, Debug)] +struct BasicConfigItem { + // 表示名 + name: String, + // ファイル先頭からのオフセット + offset: u8, + // オフセットから何バイト読み込むか + size: u8, + // エンディアン + endianness: Option, +} + +// ビットフラグデータの単位を表す構造体 +#[derive(Serialize, Deserialize, Debug)] +struct BitFlagConfigItem { + // 表示名 + name: String, + // ファイル先頭からのオフセット + offset: u8, + // オフセットから何バイト読み込むか + size: u8, + // エンディアン + endianness: Option, + // type が FLAGS の時のみ利用されるフィールド + layout: Vec, +} + +fn main() { + // yaml ファイルを読み込み、 Reader 化 + let yaml = "./yaml/setting.yaml"; + let yaml_file = File::open(yaml).unwrap(); + let reader = BufReader::new(yaml_file); + + // serde に Reader を渡し、YAML を構造体へデシリアライズ + // 構造体の定義さえできてしまえば 1 行で完了。 + let config: Vec = serde_yaml::from_reader(reader).unwrap(); + + // デシリアライズされた構造体を走査して表示 + for ci in config { + match ci { + ConfigItem::UINT8(i) + | ConfigItem::UINT16(i) + | ConfigItem::UINT32(i) + | ConfigItem::UINT64(i) => { + println!("name: {}", i.name); + println!("offset: {}", i.offset); + println!("endianness: {}", i.endianness.unwrap_or("".to_string())); + println!("layout:"); + } + ConfigItem::FLAGS(i) => { + println!("name: {}", i.name); + println!("offset: {}", i.offset); + println!("endianness: {}", i.endianness.unwrap_or("".to_string())); + println!("layout:"); + for l in i.layout { + println!("name: {}", l.name); + println!("position: {}", l.position); + println!("true_label: {}", l.true_label.unwrap_or("".to_string())); + println!("false_label: {}", l.false_label.unwrap_or("".to_string())); + } + } + } + } +} +``` + +以上。 + + +# 参考資料 + +- [serde_yaml を使って Rust で YAML をパースするやつを Internally tagged で作り直した](https://github.com/mikoto2000/TIL/tree/master/rust/yaml/serde_yaml/firststep) +- [Field attributes · Serde](https://serde.rs/field-attrs.html) +- [Enum representations · Serde](https://serde.rs/enum-representations.html) +- [Rust: 構造体 in 構造体 を JSON/YAML パース時に扱う #serde - Qiita](https://qiita.com/takavfx/items/e58481d96f7c62442340) diff --git a/rust/yaml/serde_yaml/internally_tagged/src/main.rs b/rust/yaml/serde_yaml/internally_tagged/src/main.rs new file mode 100644 index 00000000..485a9102 --- /dev/null +++ b/rust/yaml/serde_yaml/internally_tagged/src/main.rs @@ -0,0 +1,98 @@ +use std::{fs::File, io::BufReader}; + +use serde::{Deserialize, Serialize}; + +// ビットフラグの 1 ビットを表す構造体 +#[derive(Serialize, Deserialize, Debug)] +struct LayoutItem { + // 表示名 + name: String, + // ビットフラグのビット位置 + position: u8, + // ビットが 1 だった時に表示する値 + true_label: Option, + // ビットが 0 だった時に表示する値 + false_label: Option, +} + +// コンフィグの要素は、以下 5 種類のどれかとなる +// - UINT8 +// - UINT16 +// - UINT32 +// - UINT64 +// - FLAGS +#[derive(Serialize, Deserialize, Debug)] +#[serde(tag = "type")] +enum ConfigItem { + UINT8(BasicConfigItem), + UINT16(BasicConfigItem), + UINT32(BasicConfigItem), + UINT64(BasicConfigItem), + FLAGS(BitFlagConfigItem), +} + +// 数値データの単位を表す構造体 +#[derive(Serialize, Deserialize, Debug)] +struct BasicConfigItem { + // 表示名 + name: String, + // ファイル先頭からのオフセット + offset: u8, + // オフセットから何バイト読み込むか + size: u8, + // エンディアン + endianness: Option, +} + +// ビットフラグデータの単位を表す構造体 +#[derive(Serialize, Deserialize, Debug)] +struct BitFlagConfigItem { + // 表示名 + name: String, + // ファイル先頭からのオフセット + offset: u8, + // オフセットから何バイト読み込むか + size: u8, + // エンディアン + endianness: Option, + // type が FLAGS の時のみ利用されるフィールド + layout: Vec, +} + +fn main() { + // yaml ファイルを読み込み、 Reader 化 + let yaml = "./yaml/setting.yaml"; + let yaml_file = File::open(yaml).unwrap(); + let reader = BufReader::new(yaml_file); + + // serde に Reader を渡し、YAML を構造体へデシリアライズ + // 構造体の定義さえできてしまえば 1 行で完了。 + let config: Vec = serde_yaml::from_reader(reader).unwrap(); + + // デシリアライズされた構造体を走査して表示 + for ci in config { + match ci { + ConfigItem::UINT8(i) + | ConfigItem::UINT16(i) + | ConfigItem::UINT32(i) + | ConfigItem::UINT64(i) => { + println!("name: {}", i.name); + println!("offset: {}", i.offset); + println!("endianness: {}", i.endianness.unwrap_or("".to_string())); + println!("layout:"); + } + ConfigItem::FLAGS(i) => { + println!("name: {}", i.name); + println!("offset: {}", i.offset); + println!("endianness: {}", i.endianness.unwrap_or("".to_string())); + println!("layout:"); + for l in i.layout { + println!("name: {}", l.name); + println!("position: {}", l.position); + println!("true_label: {}", l.true_label.unwrap_or("".to_string())); + println!("false_label: {}", l.false_label.unwrap_or("".to_string())); + } + } + } + } +} diff --git a/rust/yaml/serde_yaml/internally_tagged/yaml/setting.yaml b/rust/yaml/serde_yaml/internally_tagged/yaml/setting.yaml new file mode 100644 index 00000000..b89a5705 --- /dev/null +++ b/rust/yaml/serde_yaml/internally_tagged/yaml/setting.yaml @@ -0,0 +1,31 @@ +- type: UINT64 + name: UINT64_value + offset: 0 + size: 8 + endianness: LITTLE +- name: UINT32_value + offset: 8 + size: 4 + type: UINT32 + endianness: LITTLE +- name: UINT16_value + offset: 12 + size: 2 + type: UINT16 + endianness: LITTLE +- name: UINT8_value + offset: 14 + size: 1 + type: UINT8 + endianness: LITTLE +- name: BIT_FLAG + offset: 15 + size: 1 + type: FLAGS + layout: + - name: LED1 + position: 0 + - name: LED2 + position: 1 + true_label: "high" + false_label: "low"