diff --git a/Cargo.lock b/Cargo.lock index 75ada6cb..5206b97b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1619,6 +1619,7 @@ dependencies = [ "paste", "quick-xml", "serde", + "serde-aux", "toml 0.8.19", "tracing", ] @@ -3204,9 +3205,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" [[package]] name = "quick-xml" -version = "0.36.2" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +checksum = "ffbfb3ddf5364c9cfcd65549a1e7b801d0e8d1b14c1a1590a6408aa93cfbfa84" dependencies = [ "memchr", "serde", @@ -3714,6 +3715,17 @@ dependencies = [ "serde_derive", ] +[[package]] +name = "serde-aux" +version = "4.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d2e8bfba469d06512e11e3311d4d051a4a387a5b42d010404fecf3200321c95" +dependencies = [ + "chrono", + "serde", + "serde_json", +] + [[package]] name = "serde_derive" version = "1.0.213" diff --git a/libs/stringtable/Cargo.toml b/libs/stringtable/Cargo.toml index eb8c6e11..04f02442 100644 --- a/libs/stringtable/Cargo.toml +++ b/libs/stringtable/Cargo.toml @@ -15,8 +15,9 @@ hemtt-workspace = { path = "../workspace", version = "1.0.0" } automod = { workspace = true } linkme = { workspace = true } paste = { workspace = true } -quick-xml = { version = "0.36.2", features = ["serialize"] } +quick-xml = { version = "0.37.0", features = ["serialize"] } serde = { workspace = true, features = ["derive"] } +serde-aux = "4.5.0" toml = { workspace = true } tracing = { workspace = true } diff --git a/libs/stringtable/src/lib.rs b/libs/stringtable/src/lib.rs index 020179cb..e1ac0c98 100644 --- a/libs/stringtable/src/lib.rs +++ b/libs/stringtable/src/lib.rs @@ -1,3 +1,5 @@ +use std::io::BufReader; + use quick_xml::se::Serializer; use serde::{Deserialize, Serialize}; @@ -9,6 +11,7 @@ mod totals; pub use key::Key; pub use package::Package; pub use totals::Totals; +use tracing::error; #[derive(Clone, Debug, Deserialize, Serialize)] pub struct Project { @@ -16,6 +19,9 @@ pub struct Project { name: String, #[serde(rename = "Package")] packages: Vec, + + #[serde(skip)] + meta_comments: Vec<(String, String)>, } impl Project { @@ -39,28 +45,76 @@ impl Project { /// Read a Project from a reader /// /// # Errors - /// [`quick_xml::de::DeError`] if the reader is not a valid stringtable + /// [`quick_xml::DeError`] if the reader is not a valid stringtable pub fn from_reader(reader: R) -> Result { - quick_xml::de::from_reader(reader) + let mut buffer = String::new(); + let mut reading_comments = false; + let mut comments = Vec::new(); + let Ok(reader) = reader + .lines() + .map(|l| { + let Ok(l) = l else { + error!("Failed to read line: {:?}", l); + return l; + }; + if !reading_comments && !buffer.is_empty() { + comments.push((buffer.trim().to_string(), l.trim().to_string())); + buffer.clear(); + } + if l.trim().starts_with("") { + reading_comments = false; + } + } + Ok(l) + }) + .collect::, _>>() + else { + return Err(quick_xml::de::DeError::Custom( + "Failed to read lines".to_string(), + )); + }; + comments.sort(); + comments.dedup(); + let mut this: Self = + quick_xml::de::from_reader(BufReader::new(reader.join("\n").as_bytes()))?; + this.meta_comments = comments; + Ok(this) } /// Write a Project to a writer /// /// # Errors - /// [`quick_xml::ser::Error`] if the writer fails to write - pub fn to_writer( - &self, - writer: &mut W, - ) -> Result<(), quick_xml::de::DeError> { - // If this write fails, the serializer will also throw an error - let _ = writer.write_str(r#""#); - let _ = writer.write_char('\n'); + /// [`quick_xml::SeError`] if the writer fails to write + pub fn to_writer(&self, writer: &mut W) -> Result<(), quick_xml::SeError> { + writer.write_str(r#""#)?; + writer.write_char('\n')?; let mut buffer = String::new(); let mut ser = Serializer::new(&mut buffer); ser.indent(' ', 4); self.serialize(ser)?; buffer.push('\n'); - writer.write_str(&buffer)?; + + for line in buffer.lines() { + for (before, after) in &self.meta_comments { + if line.trim().starts_with(after) { + let whitespace = line + .chars() + .take_while(|c| c.is_whitespace()) + .collect::(); + writer.write_str(&whitespace)?; + writer.write_str(before)?; + writer.write_char('\n')?; + } + } + writer.write_str(line)?; + writer.write_char('\n')?; + } + Ok(()) } } diff --git a/libs/stringtable/tests/snapshots/ace_arsenal__sort-2.snap b/libs/stringtable/tests/snapshots/ace_arsenal__sort-2.snap index 25034083..6f860c37 100644 --- a/libs/stringtable/tests/snapshots/ace_arsenal__sort-2.snap +++ b/libs/stringtable/tests/snapshots/ace_arsenal__sort-2.snap @@ -6127,4 +6127,5 @@ Project { containers: [], }, ], + meta_comments: [], } diff --git a/libs/stringtable/tests/snapshots/ace_arsenal__sort.snap b/libs/stringtable/tests/snapshots/ace_arsenal__sort.snap index 1d788e2f..aaeaef2c 100644 --- a/libs/stringtable/tests/snapshots/ace_arsenal__sort.snap +++ b/libs/stringtable/tests/snapshots/ace_arsenal__sort.snap @@ -6127,4 +6127,5 @@ Project { containers: [], }, ], + meta_comments: [], }