diff --git a/hyprland-parser/src/lib.rs b/hyprland-parser/src/lib.rs index 67d28b2..ad5f89b 100644 --- a/hyprland-parser/src/lib.rs +++ b/hyprland-parser/src/lib.rs @@ -1,10 +1,9 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; #[derive(Debug, Default)] pub struct HyprlandConfig { content: Vec, - sections: HashMap, - added_entries: HashMap>, + sections: HashMap, } impl HyprlandConfig { @@ -13,55 +12,86 @@ impl HyprlandConfig { } pub fn parse(&mut self, config_str: &str) { - let mut current_section = String::new(); - let mut section_start = 0; + let mut section_stack = Vec::new(); for (i, line) in config_str.lines().enumerate() { let trimmed = line.trim(); if trimmed.ends_with('{') { - current_section = trimmed.trim_end_matches('{').trim().to_string(); - section_start = i; - } else if trimmed == "}" { - if !current_section.is_empty() { - self.sections.insert(current_section.clone(), section_start); - current_section.clear(); - } + let section_name = trimmed.trim_end_matches('{').trim().to_string(); + section_stack.push((section_name, i)); + } else if trimmed == "}" && !section_stack.is_empty() { + let (name, start) = section_stack.pop().unwrap(); + let full_name = section_stack + .iter() + .map(|(n, _)| n.as_str()) + .chain(std::iter::once(name.as_str())) + .collect::>() + .join("."); + self.sections.insert(full_name, (start, i)); } self.content.push(line.to_string()); } } pub fn to_string(&self) -> String { - let mut result = self.content.join("\n"); - for (category, entries) in &self.added_entries { - for entry in entries { - if !self.entry_exists(category, entry) { - result.push_str(&format!("\n{}", entry)); - } - } - } - result - } - - fn entry_exists(&self, category: &str, entry: &str) -> bool { - self.content.iter().any(|line| line.trim() == entry) + self.content.join("\n") } pub fn add_entry(&mut self, category: &str, entry: &str) { - if !self.entry_exists(category, entry) { - if let Some(§ion_start) = self.sections.get(category) { - let insert_position = self.content[section_start..] - .iter() - .position(|line| line.trim() == "}") - .map(|pos| section_start + pos) - .unwrap_or(self.content.len()); - self.content - .insert(insert_position, format!(" {}", entry)); - } else { - self.added_entries - .entry(category.to_string()) - .or_insert_with(HashSet::new) - .insert(entry.to_string()); + let parts: Vec<&str> = category.split('.').collect(); + let mut current_section = String::new(); + let mut depth = 0; + + for (i, part) in parts.iter().enumerate() { + if i > 0 { + current_section.push('.'); } + current_section.push_str(part); + + if let Some(&(start, end)) = self.sections.get(¤t_section) { + if i == parts.len() - 1 { + let key = entry.split('=').next().unwrap().trim(); + let existing_line = self.content[start..=end] + .iter() + .position(|line| line.trim().starts_with(key)) + .map(|pos| start + pos); + + if let Some(line_num) = existing_line { + self.content[line_num] = format!("{}{}", " ".repeat(depth + 1), entry); + } else { + self.content + .insert(end, format!("{}{}", " ".repeat(depth + 1), entry)); + } + return; + } + } else { + let new_section = format!("{}{} {{", " ".repeat(depth), part); + let insert_pos = if let Some(&(_, end)) = self.sections.get(&parts[..i].join(".")) { + end + 1 + } else { + self.content.len() + }; + self.content.insert(insert_pos, new_section); + self.content.insert( + insert_pos + 1, + format!("{}{}", " ".repeat(depth + 1), entry), + ); + self.content + .insert(insert_pos + 2, format!("{}}}", " ".repeat(depth))); + + for (section, &mut (ref mut start, ref mut end)) in self.sections.iter_mut() { + if *start >= insert_pos { + *start += 3; + *end += 3; + } else if *end >= insert_pos { + *end += 3; + } + } + self.sections + .insert(current_section.clone(), (insert_pos, insert_pos + 2)); + return; + } + + depth += 1; } } } diff --git a/src/main.rs b/src/main.rs index 6be0eb8..6ebf9b4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,10 +10,9 @@ fn main() { let mut parsed_config = parse_config(&config_str); - parsed_config.add_entry("general", "new_option = value"); parsed_config.add_entry("decoration", "rounding = 10"); - parsed_config.add_entry("decoration", "blur" "enabled = true"); - parsed_config.add_entry("decoration", "blur" "size = 10"); + parsed_config.add_entry("decoration.blur", "enabled = true"); + parsed_config.add_entry("decoration.blur", "size = 10"); let updated_config_str = parsed_config.to_string();