config_disassembler/
meta.rs1use std::fs;
8use std::path::Path;
9
10use serde::{Deserialize, Serialize};
11
12use crate::error::Result;
13use crate::format::Format;
14
15pub const META_FILENAME: &str = ".config-disassembler.json";
17
18#[derive(Debug, Clone, Serialize, Deserialize)]
20pub struct Meta {
21 pub source_format: SerdeFormat,
23 pub file_format: SerdeFormat,
25 pub source_filename: Option<String>,
28 pub root: Root,
30}
31
32#[derive(Debug, Clone, Serialize, Deserialize)]
34#[serde(tag = "kind", rename_all = "snake_case")]
35pub enum Root {
36 Object {
38 key_order: Vec<String>,
40 key_files: std::collections::BTreeMap<String, String>,
46 main_file: Option<String>,
49 },
50 Array {
52 files: Vec<String>,
55 },
56}
57
58pub type SerdeFormat = Format;
63
64impl Meta {
65 pub fn write(&self, dir: &Path) -> Result<()> {
67 let path = dir.join(META_FILENAME);
68 let text = serde_json::to_string_pretty(self)?;
69 fs::write(path, text)?;
70 Ok(())
71 }
72
73 pub fn read(dir: &Path) -> Result<Self> {
75 let path = dir.join(META_FILENAME);
76 let text = fs::read_to_string(&path).map_err(|e| {
77 crate::error::Error::Invalid(format!(
78 "could not read metadata file {}: {e}",
79 path.display()
80 ))
81 })?;
82 Ok(serde_json::from_str(&text)?)
83 }
84}
85
86#[cfg(test)]
87mod tests {
88 use super::*;
89
90 #[test]
91 fn serde_format_round_trip() {
92 for &fmt in Format::ALL {
93 let text = serde_json::to_string(&fmt).unwrap();
94 let back: SerdeFormat = serde_json::from_str(&text).unwrap();
95 assert_eq!(fmt, back);
96 }
97 }
98
99 #[test]
100 fn read_returns_invalid_when_missing() {
101 let tmp = tempfile::tempdir().unwrap();
102 let err = Meta::read(tmp.path()).unwrap_err();
103 assert!(err.to_string().contains("metadata"));
104 }
105
106 #[test]
107 fn write_and_read_round_trip_object_root() {
108 let tmp = tempfile::tempdir().unwrap();
109 let meta = Meta {
110 source_format: SerdeFormat::Json,
111 file_format: SerdeFormat::Yaml,
112 source_filename: Some("orig.json".into()),
113 root: Root::Object {
114 key_order: vec!["a".into(), "b".into()],
115 key_files: std::collections::BTreeMap::new(),
116 main_file: Some("_main.yaml".into()),
117 },
118 };
119 meta.write(tmp.path()).unwrap();
120 let back = Meta::read(tmp.path()).unwrap();
121 assert!(matches!(back.root, Root::Object { .. }));
122 }
123
124 #[test]
125 fn write_and_read_round_trip_array_root() {
126 let tmp = tempfile::tempdir().unwrap();
127 let meta = Meta {
128 source_format: SerdeFormat::Yaml,
129 file_format: SerdeFormat::Json5,
130 source_filename: None,
131 root: Root::Array {
132 files: vec!["1.json5".into(), "2.json5".into()],
133 },
134 };
135 meta.write(tmp.path()).unwrap();
136 let back = Meta::read(tmp.path()).unwrap();
137 match back.root {
138 Root::Array { files } => assert_eq!(files.len(), 2),
139 _ => panic!("expected array root"),
140 }
141 }
142}