From b3651aa5ddd5d9bed53e1326534f710d6b3330d2 Mon Sep 17 00:00:00 2001 From: Nils Pukropp Date: Wed, 18 Mar 2026 17:11:27 +0100 Subject: [PATCH] fixed ordering --- Cargo.lock | 1 + Cargo.toml | 16 +++---- config.toml | 37 ---------------- src/format/properties.rs | 92 +++++++++++++++++++++------------------- 4 files changed, 54 insertions(+), 92 deletions(-) delete mode 100644 config.toml diff --git a/Cargo.lock b/Cargo.lock index 668debc..7979626 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1460,6 +1460,7 @@ version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ + "indexmap", "itoa", "memchr", "serde", diff --git a/Cargo.toml b/Cargo.toml index 2dda6f0..4f958c8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,22 +15,16 @@ dirs = "6.0.0" env_logger = "0.11.9" java-properties = "2.0.0" log = "0.4.29" -quick-xml = { version = "0.39.2", features = ["serde", "serialize"] } ratatui = "0.30.0" rust-ini = "0.21.3" -serde_json = "1.0.149" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = { version = "1.0.149", features = ["preserve_order"] } serde_yaml = "0.9.34" thiserror = "2.0.18" -toml = "1.0.7" +toml = { version = "1.0.7", features = ["preserve_order"] } tui-input = "0.15.0" - -[dependencies.clap] -features = ["derive"] -version = "4.6.0" - -[dependencies.serde] -features = ["derive"] -version = "1.0.228" +clap = { version = "4.6.0", features = ["derive"] } +quick-xml = { version = "0.39.2", features = ["serde", "serialize"] } [dev-dependencies] tempfile = "3.27.0" diff --git a/config.toml b/config.toml deleted file mode 100644 index 3a47008..0000000 --- a/config.toml +++ /dev/null @@ -1,37 +0,0 @@ -[keybinds] -append_item = "o" -delete_item = "dd" -down = "j" -edit = "i" -edit_append = "A" -edit_substitute = "S" -jump_bottom = "G" -jump_top = "gg" -next_match = "n" -normal_mode = "Esc" -prepend_item = "O" -previous_match = "N" -quit = ":q" -save = ":w" -search = "/" -undo = "u" -up = "k" - -[theme] -bg_active = "#a6e3a1" -bg_highlight = "#89b4fa" -bg_normal = "#1e1e2e" -bg_search = "#cba6f7" -border_active = "#a6e3a1" -border_normal = "#45475a" -fg_accent = "#b4befe" -fg_dimmed = "#6c7086" -fg_highlight = "#1e1e2e" -fg_modified = "#fab387" -fg_normal = "#cdd6f4" -fg_warning = "#f38ba8" -transparent = true -tree_depth_1 = "#b4befe" -tree_depth_2 = "#cba6f7" -tree_depth_3 = "#89b4fa" -tree_depth_4 = "#fab387" diff --git a/src/format/properties.rs b/src/format/properties.rs index 31f7179..a521a2f 100644 --- a/src/format/properties.rs +++ b/src/format/properties.rs @@ -1,8 +1,7 @@ use super::{ConfigItem, FormatHandler, ItemStatus, ValueType}; -use java_properties::{read, write}; -use std::collections::HashMap; +use java_properties::{LineContent, PropertiesIter, PropertiesWriter}; use std::fs::File; -use std::io::{self, BufReader}; +use std::io::{self, BufReader, BufWriter}; use std::path::Path; pub struct PropertiesHandler; @@ -11,69 +10,74 @@ impl FormatHandler for PropertiesHandler { fn parse(&self, path: &Path) -> io::Result> { let file = File::open(path)?; let reader = BufReader::new(file); - let props = read(reader) - .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; + let iter = PropertiesIter::new(reader); let mut vars = Vec::new(); let mut groups = std::collections::HashSet::new(); - for (path, value) in &props { - // Add groups based on dot notation - let parts: Vec<&str> = path.split('.').collect(); - let mut current_path = String::new(); + for line_result in iter { + let line = line_result.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?; - for i in 0..parts.len() - 1 { - if !current_path.is_empty() { - current_path.push('.'); - } - current_path.push_str(parts[i]); + if let LineContent::KVPair(path, value) = line.consume_content() { + // Add groups based on dot notation + let parts: Vec<&str> = path.split('.').collect(); + let mut current_path = String::new(); - if groups.insert(current_path.clone()) { - vars.push(ConfigItem { - key: parts[i].to_string(), - path: current_path.clone(), - value: None, - template_value: None, - default_value: None, - depth: i, - is_group: true, - status: ItemStatus::Present, - value_type: ValueType::Null, - }); + for i in 0..parts.len().saturating_sub(1) { + if !current_path.is_empty() { + current_path.push('.'); + } + current_path.push_str(parts[i]); + + if groups.insert(current_path.clone()) { + vars.push(ConfigItem { + key: parts[i].to_string(), + path: current_path.clone(), + value: None, + template_value: None, + default_value: None, + depth: i, + is_group: true, + status: ItemStatus::Present, + value_type: ValueType::Null, + }); + } } - } - vars.push(ConfigItem { - key: parts.last().unwrap().to_string(), - path: path.clone(), - value: Some(value.clone()), - template_value: Some(value.clone()), - default_value: Some(value.clone()), - depth: parts.len() - 1, - is_group: false, - status: ItemStatus::Present, - value_type: ValueType::String, - }); + vars.push(ConfigItem { + key: parts.last().unwrap_or(&"").to_string(), + path: path.clone(), + value: Some(value.clone()), + template_value: Some(value.clone()), + default_value: Some(value.clone()), + depth: parts.len().saturating_sub(1), + is_group: false, + status: ItemStatus::Present, + value_type: ValueType::String, + }); + } } - // Sort by path to keep it organized - vars.sort_by(|a, b| a.path.cmp(&b.path)); + // We don't sort here to preserve the original file order! Ok(vars) } fn write(&self, path: &Path, vars: &[ConfigItem]) -> io::Result<()> { - let mut props = HashMap::new(); + let file = File::create(path)?; + let writer = BufWriter::new(file); + let mut prop_writer = PropertiesWriter::new(writer); + for var in vars { if !var.is_group { let val = var.value.as_deref() .or(var.template_value.as_deref()) .unwrap_or(""); - props.insert(var.path.clone(), val.to_string()); + prop_writer.write(&var.path, val) + .map_err(|e| io::Error::other(e))?; } } - let file = File::create(path)?; - write(file, &props).map_err(|e| io::Error::new(io::ErrorKind::Other, e)) + prop_writer.finish().map_err(|e| io::Error::other(e)) } }