Skip to content

Commit

Permalink
Added rust/yaml/serde_yaml/internally_tagged.
Browse files Browse the repository at this point in the history
  • Loading branch information
mikoto2000 committed Jul 6, 2024
1 parent be97d56 commit d76fd33
Show file tree
Hide file tree
Showing 5 changed files with 393 additions and 0 deletions.
119 changes: 119 additions & 0 deletions rust/yaml/serde_yaml/internally_tagged/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions rust/yaml/serde_yaml/internally_tagged/Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"
137 changes: 137 additions & 0 deletions rust/yaml/serde_yaml/internally_tagged/README.md
Original file line number Diff line number Diff line change
@@ -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<String>,
// ビットが 0 だった時に表示する値
false_label: Option<String>,
}

// コンフィグの要素は、以下 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<String>,
}

// ビットフラグデータの単位を表す構造体
#[derive(Serialize, Deserialize, Debug)]
struct BitFlagConfigItem {
// 表示名
name: String,
// ファイル先頭からのオフセット
offset: u8,
// オフセットから何バイト読み込むか
size: u8,
// エンディアン
endianness: Option<String>,
// type が FLAGS の時のみ利用されるフィールド
layout: Vec<LayoutItem>,
}

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<ConfigItem> = 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)
98 changes: 98 additions & 0 deletions rust/yaml/serde_yaml/internally_tagged/src/main.rs
Original file line number Diff line number Diff line change
@@ -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<String>,
// ビットが 0 だった時に表示する値
false_label: Option<String>,
}

// コンフィグの要素は、以下 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<String>,
}

// ビットフラグデータの単位を表す構造体
#[derive(Serialize, Deserialize, Debug)]
struct BitFlagConfigItem {
// 表示名
name: String,
// ファイル先頭からのオフセット
offset: u8,
// オフセットから何バイト読み込むか
size: u8,
// エンディアン
endianness: Option<String>,
// type が FLAGS の時のみ利用されるフィールド
layout: Vec<LayoutItem>,
}

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<ConfigItem> = 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()));
}
}
}
}
}
31 changes: 31 additions & 0 deletions rust/yaml/serde_yaml/internally_tagged/yaml/setting.yaml
Original file line number Diff line number Diff line change
@@ -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"

0 comments on commit d76fd33

Please sign in to comment.