diff --git a/Cargo.lock b/Cargo.lock index 956b14a..a7f1ebb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -386,11 +386,16 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hyprland-parser" +version = "0.1.0" + [[package]] name = "hyprlandgui" version = "0.1.0" dependencies = [ "gtk4", + "hyprland-parser", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index a65746a..9dc994e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,4 +16,5 @@ categories = ["gui"] edition = "2021" [dependencies] -gtk = { version = "0.9.2", package = "gtk4", features = ["v4_16"] } \ No newline at end of file +gtk = { version = "0.9.2", package = "gtk4", features = ["v4_16"] } +hyprland-parser = { path = "hyprland-parser" } diff --git a/hyprland-parser/Cargo.toml b/hyprland-parser/Cargo.toml new file mode 100644 index 0000000..ce6ee9f --- /dev/null +++ b/hyprland-parser/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "hyprland-parser" +version = "0.1.0" +edition = "2021" + +[dependencies] diff --git a/hyprland-parser/src/lib.rs b/hyprland-parser/src/lib.rs new file mode 100644 index 0000000..7638bf5 --- /dev/null +++ b/hyprland-parser/src/lib.rs @@ -0,0 +1,179 @@ +use std::collections::HashMap; + +#[derive(Debug, Default)] +pub struct HyprlandConfig { + general: HashMap, + decoration: HashMap, + animations: HashMap, + input: HashMap, + gestures: HashMap, + group: HashMap, + misc: HashMap, + binds: HashMap, + xwayland: HashMap, + opengl: HashMap, + render: HashMap, + cursor: HashMap, + debug: HashMap, + blur: HashMap, + touchpad: HashMap, + touchdevice: HashMap, + tablet: HashMap, + groupbar: HashMap, + exec: Vec, + exec_once: Vec, + monitor: Vec, + windowrule: Vec, + windowrulev2: Vec, + bind: Vec, + bindm: Vec, +} + +impl HyprlandConfig { + pub fn new() -> Self { + Self::default() + } + + pub fn parse(&mut self, config_str: &str) { + let mut section_stack = Vec::new(); + + for line in config_str.lines() { + let trimmed = line.trim(); + if trimmed.is_empty() || trimmed.starts_with('#') { + continue; + } + + if trimmed.ends_with('{') { + let section = trimmed.trim_end_matches('{').trim().to_string(); + section_stack.push(section); + continue; + } + + if trimmed == "}" { + section_stack.pop(); + continue; + } + + if trimmed.starts_with("exec = ") { + self.exec.push(trimmed[6..].to_string()); + } else if trimmed.starts_with("exec-once = ") { + self.exec_once.push(trimmed[11..].to_string()); + } else if trimmed.starts_with("monitor = ") { + self.monitor.push(trimmed[9..].to_string()); + } else if trimmed.starts_with("windowrule = ") { + self.windowrule.push(trimmed[12..].to_string()); + } else if trimmed.starts_with("windowrulev2 = ") { + self.windowrulev2.push(trimmed[14..].to_string()); + } else if trimmed.starts_with("bind = ") { + self.bind.push(trimmed[6..].to_string()); + } else if trimmed.starts_with("bindm = ") { + self.bindm.push(trimmed[7..].to_string()); + } else { + let parts: Vec<&str> = trimmed.splitn(2, '=').collect(); + if parts.len() == 2 { + let key = parts[0].trim(); + let value = parts[1].trim(); + let current_section = section_stack.join(":"); + match current_section.as_str() { + "general" => self.general.insert(key.to_string(), value.to_string()), + "decoration" => self.decoration.insert(key.to_string(), value.to_string()), + "animations" => self.animations.insert(key.to_string(), value.to_string()), + "input" => self.input.insert(key.to_string(), value.to_string()), + "gestures" => self.gestures.insert(key.to_string(), value.to_string()), + "group" => self.group.insert(key.to_string(), value.to_string()), + "misc" => self.misc.insert(key.to_string(), value.to_string()), + "binds" => self.binds.insert(key.to_string(), value.to_string()), + "xwayland" => self.xwayland.insert(key.to_string(), value.to_string()), + "opengl" => self.opengl.insert(key.to_string(), value.to_string()), + "render" => self.render.insert(key.to_string(), value.to_string()), + "cursor" => self.cursor.insert(key.to_string(), value.to_string()), + "debug" => self.debug.insert(key.to_string(), value.to_string()), + "decoration:blur" => self.blur.insert(key.to_string(), value.to_string()), + "input:touchpad" => self.touchpad.insert(key.to_string(), value.to_string()), + "input:touchdevice" => self.touchdevice.insert(key.to_string(), value.to_string()), + "input:tablet" => self.tablet.insert(key.to_string(), value.to_string()), + "group:groupbar" => self.groupbar.insert(key.to_string(), value.to_string()), + _ => None, + }; + } + } + } + } + + pub fn to_string(&self) -> String { + let mut config = String::new(); + + for (key, value) in &self.general { + config.push_str(&format!("{} = {}\n", key, value)); + } + + let sections = [ + ("decoration", &self.decoration), + ("animations", &self.animations), + ("input", &self.input), + ("gestures", &self.gestures), + ("group", &self.group), + ("misc", &self.misc), + ("binds", &self.binds), + ("xwayland", &self.xwayland), + ("opengl", &self.opengl), + ("render", &self.render), + ("cursor", &self.cursor), + ("debug", &self.debug), + ]; + + for (section, map) in sections { + if !map.is_empty() { + config.push_str(&format!("\n{} {{\n", section)); + for (key, value) in map { + config.push_str(&format!(" {} = {}\n", key, value)); + } + config.push_str("}\n"); + } + } + + if !self.blur.is_empty() { + config.push_str("\ndecoration:blur {\n"); + for (key, value) in &self.blur { + config.push_str(&format!(" {} = {}\n", key, value)); + } + config.push_str("}\n"); + } + + let lists = [ + ("exec", &self.exec), + ("exec-once", &self.exec_once), + ("monitor", &self.monitor), + ("windowrule", &self.windowrule), + ("windowrulev2", &self.windowrulev2), + ("bind", &self.bind), + ("bindm", &self.bindm), + ]; + + for (prefix, list) in lists { + for item in list { + config.push_str(&format!("{} = {}\n", prefix, item)); + } + } + + config + } + + pub fn insert_general(&mut self, key: String, value: String) { + self.general.insert(key, value); + } + + pub fn add_exec(&mut self, command: String) { + self.exec.push(command); + } + + pub fn add_bind(&mut self, binding: String) { + self.bind.push(binding); + } +} + +pub fn parse_config(config_str: &str) -> HyprlandConfig { + let mut config = HyprlandConfig::new(); + config.parse(config_str); + config +} diff --git a/src/main.rs b/src/main.rs index e69de29..ab005cb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -0,0 +1,22 @@ +use hyprland_parser::parse_config; +use std::fs; +use std::path::PathBuf; + +fn main() { + let home = std::env::var("HOME").unwrap(); + let config_path = PathBuf::from(home).join(".config/hypr/hyprland.conf"); + + let config_str = fs::read_to_string(&config_path).unwrap(); + + let mut parsed_config = parse_config(&config_str); + + parsed_config.insert_general("new_option".to_string(), "value".to_string()); + parsed_config.add_exec("some_command --with-args".to_string()); + parsed_config.add_bind("$mod, T, exec, kitty".to_string()); + + let updated_config_str = parsed_config.to_string(); + + fs::write(&config_path, updated_config_str).unwrap(); + + println!("Updated hyprland.conf with new configurations."); +}