ラベル serde の投稿を表示しています。 すべての投稿を表示
ラベル serde の投稿を表示しています。 すべての投稿を表示

2024年7月6日土曜日

serde_yaml を使って Rust で YAML をパースするやつを Internally tagged で作り直した

やること

serde_yaml を使って Rust で YAML をパースする で YAML のパースを行ったが、 Internally tagged 形式で構造体を定義したほうがその後の処理がやりやすそうだったのでやってみた。

前提

実装

main.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()));
                }
            }
        }
    }
}

以上。

参考資料

2024年7月5日金曜日

serde_yaml を使って Rust で YAML をパースする

やること

設定ファイルとして YAML を採用したので、 Rust での扱い方を確認する。

前提

プロジェクト作成

cargo init
cargo add serde --features="derive"
cargo add serde_yaml

実装

mikoto2000/binp: バイナリファイルからデータを抽出するツール。 の設定ファイルの構造をパースしてみる。

./yaml/setting.yaml:

- name: UINT64_value
  offset: 0
  size: 8
  type: UINT64
  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
  data_type: FLAGS
  layout:
    - name: LED1
      position: 0
    - name: LED2
      position: 1
      true_label: "high"
      false_label: "low"

main.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>,
}

// パースする単位を表す構造体
// 本当は layout あり・なしで型定義を分けるのが良いのだろうが、
// firststep だしまぁいいかという感じで。
#[derive(Serialize, Deserialize, Debug)]
struct ConfigItem {
    // 表示名
    name: String,
    // ファイル先頭からのオフセット
    offset: u8,
    // オフセットから何バイト読み込むか
    size: u8,
    // データ型
    // UINT,INT 8-64 と FLAGS(ビットフラグ)
    #[serde(alias = "type")]
    data_type: String,
    // エンディアン
    endianness: Option<String>,
    // type が FLAGS の時のみ利用されるフィールド
    layout: Option<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 {
        println!("name: {}", ci.name);
        println!("offset: {}", ci.offset);
        println!("type: {}", ci.data_type);
        println!("endianness: {}", ci.endianness.unwrap_or("".to_string()));
        println!("layout:");
        if ci.layout.is_some() {
            for l in ci.layout.unwrap() {
                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()));
            }
        }
    }
}

動作確認

$ cargo run
   Compiling firststep v0.1.0 (/workspaces/TIL/rust/yaml/serde_yaml/firststep)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
     Running `target/debug/firststep`
name: UINT64_value
offset: 0
type: UINT64
endianness: LITTLE
layout:
name: UINT32_value
offset: 8
type: UINT32
endianness: LITTLE
layout:
name: UINT16_value
offset: 12
type: UINT16
endianness: LITTLE
layout:
name: UINT8_value
offset: 14
type: UINT8
endianness: LITTLE
layout:
name: BIT_FLAG
offset: 15
type: FLAGS
endianness: 
layout:
name: LED1
position: 0
true_label: 
false_label: 
name: LED2
position: 1
true_label: high
false_label: low

表示は汚いけどまぁ想定通りにできてはいそう。

以上。

参考資料