バージョン: 0.6.0 最終更新: 2025-12-11 ステータス: 確定
diffx-core は構造化データの意味的差分を検出するRustライブラリ。
CLIツール (diffx) および言語バインディング (JS, Python) の基盤。
diffx-core/
├── src/
│ ├── lib.rs # 公開API再エクスポート
│ ├── types.rs # 型定義
│ ├── parser/ # パーサーモジュール
│ │ ├── mod.rs
│ │ ├── format.rs # フォーマット検出
│ │ ├── json.rs
│ │ ├── yaml.rs
│ │ ├── toml.rs
│ │ ├── xml.rs
│ │ ├── ini.rs
│ │ └── csv.rs
│ ├── diff/ # 差分検出モジュール
│ │ ├── mod.rs
│ │ ├── core.rs # diff, diff_paths
│ │ ├── recursive.rs
│ │ ├── objects.rs
│ │ └── arrays.rs
│ ├── io/ # ファイル操作
│ │ └── mod.rs
│ └── utils.rs # ユーティリティ
ファイルパスまたはディレクトリパスを指定して差分を検出する。
pub fn diff_paths(
old_path: &str,
new_path: &str,
options: Option<&DiffOptions>,
) -> Result<Vec<DiffResult>>引数:
old_path: 比較元のパス(ファイルまたはディレクトリ)new_path: 比較先のパス(ファイルまたはディレクトリ)options: 比較オプション(省略可)
戻り値: Result<Vec<DiffResult>>
動作:
- ファイル vs ファイル: ファイル内容を比較
- ディレクトリ vs ディレクトリ:
options.recursive = Some(true)が必要 - ファイル vs ディレクトリ: エラー
例:
use diffx_core::{diff_paths, DiffOptions};
let results = diff_paths("old.json", "new.json", None)?;
for result in &results {
println!("{}", result);
}パース済みの serde_json::Value を直接比較する。
pub fn diff(
old: &Value,
new: &Value,
options: Option<&DiffOptions>,
) -> Result<Vec<DiffResult>>引数:
old: 比較元の値new: 比較先の値options: 比較オプション(省略可)
戻り値: Result<Vec<DiffResult>>
例:
use diffx_core::diff;
use serde_json::json;
let old = json!({"name": "Alice", "age": 30});
let new = json!({"name": "Alice", "age": 31});
let results = diff(&old, &new, None)?;
// results: [Modified("age", 30, 31)]各フォーマットの文字列を serde_json::Value にパースする。
pub fn parse_json(content: &str) -> Result<Value>
pub fn parse_yaml(content: &str) -> Result<Value>
pub fn parse_toml(content: &str) -> Result<Value>
pub fn parse_xml(content: &str) -> Result<Value>
pub fn parse_ini(content: &str) -> Result<Value>
pub fn parse_csv(content: &str) -> Result<Value>戻り値: Result<Value> - 全てのフォーマットは serde_json::Value に統一される
ファイルパスの拡張子からフォーマットを検出する。
pub fn detect_format_from_path(path: &Path) -> FileFormatマッピング:
| 拡張子 | FileFormat |
|---|---|
.json |
Json |
.yaml, .yml |
Yaml |
.toml |
Toml |
.xml |
Xml |
.ini, .cfg |
Ini |
.csv |
Csv |
| その他 | Json(デフォルト) |
指定フォーマットでコンテンツをパースする。
pub fn parse_content_by_format(content: &str, format: FileFormat) -> Result<Value>差分の結果を表すenum。
#[derive(Debug, PartialEq, Serialize, Clone)]
pub enum DiffResult {
Added(String, Value), // パス, 追加された値
Removed(String, Value), // パス, 削除された値
Modified(String, Value, Value), // パス, 旧値, 新値
TypeChanged(String, Value, Value), // パス, 旧値, 新値
}Display実装(ライブラリのデフォルト):
+ path: value # Added
- path: value # Removed
~ path: old -> new # Modified
# path: old -> new (type changed) # TypeChanged
注意: CLIでは異なる形式を使用(! 記号と型名表示)。詳細は docs/specs/cli.md 参照。
差分検出のオプション。
#[derive(Debug, Clone, Default)]
pub struct DiffOptions {
// 比較オプション
pub epsilon: Option<f64>, // 浮動小数点許容誤差
pub array_id_key: Option<String>, // 配列要素のIDキー
pub ignore_keys_regex: Option<Regex>, // 無視するキーの正規表現
pub path_filter: Option<String>, // パスフィルタ(部分一致)
// ディレクトリ比較
pub recursive: Option<bool>, // 再帰比較フラグ
// 出力制御
pub output_format: Option<OutputFormat>,
// diffx固有オプション
pub diffx_options: Option<DiffxSpecificOptions>,
}diffx CLI固有のオプション。
#[derive(Debug, Clone, Default)]
pub struct DiffxSpecificOptions {
pub ignore_whitespace: Option<bool>, // 空白を無視
pub ignore_case: Option<bool>, // 大文字小文字を無視
pub brief_mode: Option<bool>, // 簡易モード
pub quiet_mode: Option<bool>, // 静粛モード
}出力フォーマット。
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub enum OutputFormat {
#[default]
Diffx, // 人間可読形式
Json, // JSON配列
Yaml, // YAML配列
}入力ファイルフォーマット。
#[derive(Debug, Clone, Copy)]
pub enum FileFormat {
Json,
Yaml,
Csv,
Toml,
Ini,
Xml,
}メモリ効率の良い差分結果(値をStringで保持)。
#[derive(Debug, PartialEq, Serialize)]
pub enum LightweightDiffResult {
Added(String, String),
Removed(String, String),
Modified(String, String, String),
TypeChanged(String, String, String),
}DiffResult から変換可能:
impl From<&DiffResult> for LightweightDiffResultJSON値の型名を取得する(内部用)。
pub fn value_type_name(value: &Value) -> &str戻り値: "Null", "Boolean", "Number", "String", "Array", "Object"
差分結果を指定フォーマットの文字列に変換する(内部用)。
pub fn format_output<T: Serialize>(results: &[T], format: OutputFormat) -> Result<String>DiffResult を指定フォーマットの文字列に変換する。
pub fn format_diff_output(
results: &[DiffResult],
format: OutputFormat,
options: Option<&DiffOptions>,
) -> Result<String>- 両オブジェクトのキーを列挙
ignore_keys_regexにマッチするキーはスキップ(再帰的に適用)- 片方にのみ存在するキー → Added/Removed
- 両方に存在するキー → 値を再帰比較
デフォルト(インデックスベース):
- 長い方の長さまでインデックスで対応付け
- 各要素を再帰比較
- 片方にのみ存在する要素 → Added/Removed
array_id_key 指定時(IDベース):
- 各要素から指定キーの値を取得
- IDを持つ要素: 同じID値を持つ要素同士を対応付け
- IDを持たない要素: インデックスで対応付け(フォールバック)
- 対応する要素を再帰比較
- 対応しない要素 → Added/Removed
ignore_whitespace 有効時:
old.chars().filter(|c| !c.is_whitespace()).collect()- 全ての空白文字(スペース、タブ、改行等)を削除して比較
ignore_case 有効時:
old.to_lowercase()- 小文字に変換して比較
- キー名には適用されない(値のみ)
epsilon 指定時:
let old_f = old_num.as_f64().unwrap_or(0.0);
let new_f = new_num.as_f64().unwrap_or(0.0);
(old_f - new_f).abs() <= epsilon- 整数も f64 に変換して比較される
- epsilon 未指定時は厳密比較(
old != new)
if value_type_name(old) != value_type_name(new) {
DiffResult::TypeChanged(...)
} else {
DiffResult::Modified(...)
}型名: "Null", "Boolean", "Number", "String", "Array", "Object"
- 同じ型で値が異なる →
Modified - 異なる型 →
TypeChanged
- 両ディレクトリのファイルを再帰的に収集
- 相対パスでマッチング
- 片方にのみ存在 → Added/Removed(ファイル全体を値として)
- 両方に存在 → ファイル内容を比較
- パースできないファイルは静かにスキップ(エラーにならない)
全ての公開関数は anyhow::Result を返す。
主なエラー:
- ファイルが存在しない
- パースエラー(不正なフォーマット)
- ファイルとディレクトリの混在比較
- ディレクトリ比較で
recursive未指定
use diffx_core::{diff, DiffOptions, DiffxSpecificOptions};
use serde_json::json;
let old = json!({"name": "Alice", "age": 30});
let new = json!({"name": "ALICE", "age": 31});
// オプション設定
let options = DiffOptions {
diffx_options: Some(DiffxSpecificOptions {
ignore_case: Some(true),
..Default::default()
}),
..Default::default()
};
let results = diff(&old, &new, Some(&options))?;
// name は ignore_case により同値、age のみ差分use diffx_core::{diff_paths, DiffOptions};
let options = DiffOptions {
epsilon: Some(0.001),
..Default::default()
};
let results = diff_paths("config_v1.json", "config_v2.json", Some(&options))?;use diffx_core::{diff_paths, DiffOptions};
let options = DiffOptions {
recursive: Some(true),
..Default::default()
};
let results = diff_paths("dir1/", "dir2/", Some(&options))?;- 2025-12-11: 初版確定
- lib.rs の再エクスポート構造を文書化
- 全公開API、型、オプションを記載
- 差分検出アルゴリズムの概要を追加