| Crates.io | hyprlang |
| lib.rs | hyprlang |
| version | 0.4.2 |
| created_at | 2025-11-12 14:07:01.63254+00 |
| updated_at | 2025-12-29 22:31:50.907902+00 |
| description | A scripting language interpreter and parser for Hyprlang and Hyprland configuration files. |
| homepage | https://github.com/spinualexandru/hyprlang-rs |
| repository | https://github.com/spinualexandru/hyprlang-rs |
| max_upload_size | |
| id | 1929403 |
| size | 487,640 |
A Rust reimplementation of Hyprlang, the configuration language used by Hyprland.
Hyprlang is a powerful configuration language featuring variables, nested categories, expressions, custom handlers, and more. This library provides a complete parser and configuration manager with a clean, idiomatic Rust API.
This project is not endorsed by or affiliated with the Hyprland project/HyprWM Organization.
Parity:
{{expr}} syntaxrgba(), rgb(), and hex colors# hyprlang if/endif/noerror support with negation\{{}} or {\{}} for literal bracessource directivesSee BENCHMARKS.md for detailed performance metrics. Quick summary:
Add this to your Cargo.toml:
[dependencies]
hyprlang = "0.4.1"
hyprland FeatureEnable the hyprland feature to get a high-level Hyprland struct with pre-configured handlers and typed access to Hyprland configuration options:
[dependencies]
hyprlang = { version = "0.4.1", features = ["hyprland"] }
This feature provides:
mutation FeatureEnable the mutation feature to modify configuration values and serialize configs back to files:
[dependencies]
hyprlang = { version = "0.4.1", features = ["mutation"] }
This feature provides:
source directivesuse hyprlang::Config;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut config = Config::new();
// Parse a configuration string
config.parse(r#"
general {
gaps_in = 5
gaps_out = 20
border_size = 2
}
"#)?;
// Access values
let gaps_in = config.get_int("general:gaps_in")?;
let gaps_out = config.get_int("general:gaps_out")?;
println!("gaps_in: {}, gaps_out: {}", gaps_in, gaps_out);
Ok(())
}
The hyprland feature provides a high-level, type-safe API specifically designed for working with Hyprland configurations. Instead of manually registering handlers and using string-based key access, you get a convenient Hyprland struct with pre-configured handlers and typed accessor methods.
Without the Hyprland feature (using low-level Config API):
use hyprlang::Config;
let mut config = Config::new();
// Manually register all handlers
config.register_handler_fn("bind", |_| Ok(()));
config.register_handler_fn("monitor", |_| Ok(()));
config.register_handler_fn("windowrule", |_| Ok(()));
// ... register 20+ more handlers
config.register_category_handler_fn("animations", "animation", |_| Ok(()));
config.register_category_handler_fn("animations", "bezier", |_| Ok(()));
// Access values with string keys and manual type conversion
let border_size = config.get_int("general:border_size")?;
let gaps_in = config.get_string("general:gaps_in")?; // Could be int or string
let binds = config.get_handler_calls("bind").unwrap_or(&vec![]);
With the Hyprland feature (using high-level Hyprland API):
use hyprlang::Hyprland;
let mut hypr = Hyprland::new(); // All handlers pre-registered!
// Typed accessor methods
let border_size = hypr.general_border_size()?; // Returns i64
let gaps_in = hypr.general_gaps_in()?; // Returns String (CSS-style)
let active_border = hypr.general_active_border_color()?; // Returns Color
// Convenient array access
let binds = hypr.all_binds(); // Returns Vec<&String>
use hyprlang::Hyprland;
use std::path::Path;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create Hyprland config (handlers auto-registered)
let mut hypr = Hyprland::new();
// Parse your Hyprland config
hypr.parse_file(Path::new("~/.config/hypr/hyprland.conf"))?;
// === Access General Settings ===
println!("Border: {}", hypr.general_border_size()?);
println!("Layout: {}", hypr.general_layout()?);
println!("Gaps: {} / {}", hypr.general_gaps_in()?, hypr.general_gaps_out()?);
let active = hypr.general_active_border_color()?;
println!("Active border: rgba({}, {}, {}, {})",
active.r, active.g, active.b, active.a);
// === Access Decoration Settings ===
println!("\nRounding: {}", hypr.decoration_rounding()?);
println!("Active opacity: {}", hypr.decoration_active_opacity()?);
println!("Blur enabled: {}", hypr.decoration_blur_enabled()?);
println!("Blur size: {}", hypr.decoration_blur_size()?);
// === Access Animation Settings ===
if hypr.animations_enabled()? {
println!("\nAnimations:");
for anim in hypr.all_animations() {
println!(" - {}", anim);
}
println!("\nBezier curves:");
for bezier in hypr.all_beziers() {
println!(" - {}", bezier);
}
}
// === Access Input Settings ===
println!("\nKeyboard layout: {}", hypr.input_kb_layout()?);
println!("Mouse sensitivity: {}", hypr.input_sensitivity()?);
println!("Natural scroll: {}", hypr.input_touchpad_natural_scroll()?);
// === Access Keybindings ===
println!("\nKeybindings ({}):", hypr.all_binds().len());
for (i, bind) in hypr.all_binds().iter().enumerate() {
println!(" [{}] {}", i + 1, bind);
}
// === Access Window Rules ===
println!("\nWindow rules ({}):", hypr.all_windowrules().len());
for rule in hypr.all_windowrules() {
println!(" - {}", rule);
}
// === Access Variables ===
println!("\nVariables:");
for (name, value) in hypr.variables() {
println!(" ${} = {}", name, value);
}
// === Access Monitors ===
for monitor in hypr.all_monitors() {
println!("Monitor: {}", monitor);
}
// === Access Environment Variables ===
for env in hypr.all_env() {
println!("Env: {}", env);
}
// === Access Autostart ===
for exec in hypr.all_exec_once() {
println!("Exec-once: {}", exec);
}
Ok(())
}
hypr.general_border_size() -> Result<i64>
hypr.general_gaps_in() -> Result<String> // CSS-style: "5" or "5 10 15 20"
hypr.general_gaps_out() -> Result<String> // CSS-style: "20" or "5 10 15 20"
hypr.general_layout() -> Result<&str> // "dwindle" or "master"
hypr.general_allow_tearing() -> Result<bool>
hypr.general_active_border_color() -> Result<Color>
hypr.general_inactive_border_color() -> Result<Color>
hypr.general_locale() -> Result<&str> // Locale override (new in 0.53.0)
hypr.decoration_rounding() -> Result<i64>
hypr.decoration_active_opacity() -> Result<f64>
hypr.decoration_inactive_opacity() -> Result<f64>
hypr.decoration_blur_enabled() -> Result<bool>
hypr.decoration_blur_size() -> Result<i64>
hypr.decoration_blur_passes() -> Result<i64>
hypr.animations_enabled() -> Result<bool>
hypr.all_animations() -> Vec<&String> // All animation definitions
hypr.all_beziers() -> Vec<&String> // All bezier curve definitions
hypr.input_kb_layout() -> Result<&str>
hypr.input_follow_mouse() -> Result<i64>
hypr.input_sensitivity() -> Result<f64>
hypr.input_touchpad_natural_scroll() -> Result<bool>
hypr.dwindle_pseudotile() -> Result<bool>
hypr.dwindle_preserve_split() -> Result<bool>
hypr.master_new_status() -> Result<&str>
hypr.misc_disable_hyprland_logo() -> Result<bool>
hypr.misc_force_default_wallpaper() -> Result<i64>
hypr.quirks_prefer_hdr() -> Result<i64> // 0=off, 1=always HDR, 2=gamescope only
hypr.cursor_hide_on_tablet() -> Result<bool> // Hide cursor on tablet input
hypr.group_groupbar_blur() -> Result<bool> // Groupbar blur effect
hypr.all_binds() -> Vec<&String> // All bind definitions
hypr.all_bindu() -> Vec<&String> // All universal submap binds (new in 0.53.0)
hypr.all_bindm() -> Vec<&String> // All mouse bindings
hypr.all_bindel() -> Vec<&String> // All bindel definitions
hypr.all_bindl() -> Vec<&String> // All bindl definitions
hypr.all_windowrules() -> Vec<&String> // All windowrule v1 definitions (DEPRECATED)
hypr.all_windowrulesv2() -> Vec<&String> // All windowrule v2 definitions (DEPRECATED)
hypr.all_layerrules() -> Vec<&String> // All layerrule v1 definitions (DEPRECATED)
hypr.all_workspaces() -> Vec<&String> // All workspace definitions
hypr.all_monitors() -> Vec<&String> // All monitor definitions
hypr.all_env() -> Vec<&String> // All env definitions
hypr.all_exec() -> Vec<&String> // All exec definitions
hypr.all_exec_once() -> Vec<&String> // All exec-once definitions
// New v3 syntax for windowrules
hypr.windowrule_names() -> Vec<String> // All windowrule names
hypr.get_windowrule(name: &str) -> Result<RuleInstance> // Get specific rule
// New v2 syntax for layerrules
hypr.layerrule_names() -> Vec<String> // All layerrule names
hypr.get_layerrule(name: &str) -> Result<RuleInstance> // Get specific rule
// RuleInstance helper provides typed access to properties:
rule.get(key: &str) -> Result<&ConfigValue> // Get any property
rule.get_string(key: &str) -> Result<String> // Get as string
rule.get_int(key: &str) -> Result<i64> // Get as integer
rule.get_float(key: &str) -> Result<f64> // Get as float
rule.get_color(key: &str) -> Result<Color> // Get as color
hypr.variables() -> &HashMap<String, String> // All variables
hypr.get_variable(name: &str) -> Option<&String> // Get specific variable
If you need access to the underlying low-level Config API:
let config: &Config = hypr.config(); // Immutable access
let config: &mut Config = hypr.config_mut(); // Mutable access
// Use all Config methods
let custom_value = config.get("custom:key")?;
config.register_handler_fn("custom", |ctx| { /* ... */ Ok(()) });
The Hyprland struct automatically registers these handlers:
Root-level handlers:
monitor - Monitor configurationenv - Environment variablesbind, bindu, bindm, bindel, bindl, bindr, binde, bindn - Keybindings (bindu for universal submap binds, new in 0.53.0)windowrule, windowrulev2 - Window rules (deprecated, use v3 special category syntax)layerrule - Layer rules (deprecated, use v2 special category syntax)workspace - Workspace configurationexec, exec-once - Commandssource - File inclusionblurls - Blur layer surfaceplugin - Plugin loadingCategory-specific handlers:
animations:animation - Animation definitionsanimations:bezier - Bezier curve definitionsSpecial categories:
device[name] - Per-device input configuration (keyed category)monitor[name] - Per-monitor configuration (keyed category)windowrule[name] - Window rules v3 syntax (keyed category with 80+ properties)layerrule[name] - Layer rules v2 syntax (keyed category with 12 properties)Use the Hyprland API when:
Use the low-level Config API when:
use hyprlang::Config;
let mut config = Config::new();
config.parse(r#"
# Integers and floats
count = 42
opacity = 0.95
# Strings
terminal = kitty
shell = "zsh"
# Booleans
enabled = true
disabled = false
"#)?;
assert_eq!(config.get_int("count")?, 42);
assert_eq!(config.get_float("opacity")?, 0.95);
assert_eq!(config.get_string("terminal")?, "kitty");
use hyprlang::Config;
let mut config = Config::new();
config.parse(r#"
$terminal = kitty
$mod = SUPER
# Variables are expanded when used
my_term = $terminal
modifier = $mod
"#)?;
// Access variables directly
let vars = config.variables();
assert_eq!(vars.get("terminal"), Some(&"kitty".to_string()));
// Or access expanded values
assert_eq!(config.get_string("my_term")?, "kitty");
use hyprlang::Config;
let mut config = Config::new();
config.parse(r#"
color1 = rgba(33ccffee)
color2 = rgb(255, 128, 64)
color3 = 0xff8040ff
"#)?;
let color = config.get_color("color1")?;
println!("R: {}, G: {}, B: {}, A: {}", color.r, color.g, color.b, color.a);
use hyprlang::Config;
let mut config = Config::new();
config.parse(r#"
position1 = 100, 200
position2 = (50, 75)
"#)?;
let pos = config.get_vec2("position1")?;
assert_eq!(pos.x, 100.0);
assert_eq!(pos.y, 200.0);
use hyprlang::Config;
let mut config = Config::new();
config.parse(r#"
$base = 10
# Arithmetic expressions with {{}}
double = {{$base * 2}}
sum = {{5 + 3}}
complex = {{($base + 5) * 2}}
"#)?;
assert_eq!(config.get_int("double")?, 20);
assert_eq!(config.get_int("sum")?, 8);
assert_eq!(config.get_int("complex")?, 30);
use hyprlang::Config;
let mut config = Config::new();
config.parse(r#"
general {
border_size = 2
gaps {
inner = 5
outer = 10
}
}
"#)?;
// Access with colon-separated paths
assert_eq!(config.get_int("general:border_size")?, 2);
assert_eq!(config.get_int("general:gaps:inner")?, 5);
assert_eq!(config.get_int("general:gaps:outer")?, 10);
use hyprlang::Config;
let mut config = Config::new();
// Register a handler for custom keywords
config.register_handler_fn("bind", |ctx| {
println!("Bind: {}", ctx.value);
Ok(())
});
config.parse(r#"
bind = SUPER, Q, exec, kitty
bind = SUPER, C, killactive
"#)?;
// Access handler calls as arrays
let binds = config.get_handler_calls("bind").unwrap();
assert_eq!(binds.len(), 2);
use hyprlang::Config;
let mut config = Config::new();
// Register handlers that only work in specific categories
config.register_category_handler_fn("animations", "animation", |ctx| {
println!("Animation: {}", ctx.value);
Ok(())
});
config.parse(r#"
animations {
animation = windows, 1, 4, default
animation = fade, 1, 3, quick
}
"#)?;
// Handler calls are namespaced by category
let anims = config.get_handler_calls("animations:animation").unwrap();
assert_eq!(anims.len(), 2);
use hyprlang::{Config, SpecialCategoryDescriptor};
let mut config = Config::new();
// Register a special category
config.register_special_category(
SpecialCategoryDescriptor::keyed("device", "name")
);
config.parse(r#"
device[mouse] {
sensitivity = 0.5
accel_profile = flat
}
device[keyboard] {
repeat_rate = 50
repeat_delay = 300
}
"#)?;
// Access keyed category instances
let mouse = config.get_special_category("device", "mouse")?;
println!("Mouse sensitivity: {:?}", mouse.get("sensitivity"));
The new windowrule v3 and layerrule v2 syntax uses special category blocks:
use hyprlang::Hyprland;
let mut hypr = Hyprland::new();
hypr.parse(r#"
# New v3 syntax - windowrule as special category
windowrule[float-terminals] {
# Match properties
match:class = ^(kitty|alacritty)$
match:floating = false
# Effect properties
float = true
size = 800 600
center = true
opacity = 0.95
rounding = 10
border_color = rgba(33ccffee)
}
# Layerrule v2 syntax
layerrule[blur-waybar] {
match:namespace = waybar
blur = true
ignorealpha = 0.5
}
"#)?;
// Access windowrules
let names = hypr.windowrule_names(); // vec!["float-terminals"]
let rule = hypr.get_windowrule("float-terminals")?;
// Get properties with type safety
let class_match = rule.get_string("match:class")?; // "^(kitty|alacritty)$"
let is_float = rule.get_int("float")?; // 1 (true)
let opacity = rule.get_float("opacity")?; // 0.95
let color = rule.get_color("border_color")?; // Color { r: 51, g: 204, b: 255, a: 238 }
// Old v2 handler syntax still works for backward compatibility
hypr.parse(r#"
windowrulev2 = float, class:^(kitty)$
"#)?;
let v2_rules = hypr.all_windowrulesv2();
Windowrule v3 - Match Properties (19):
match:class, match:title, match:initial_class, match:initial_titlematch:floating, match:xwayland, match:fullscreen, match:pinnedmatch:focus, match:group, match:modal, match:tagmatch:fullscreenstate_internal, match:fullscreenstate_clientmatch:on_workspace, match:content, match:xdg_tagmatch:namespace, match:exec_tokenWindowrule v3 - Effect Properties (60+ with aliases):
float, tile, fullscreen, maximize, move, size, center, pseudo, monitor, workspace, pin, group, etc.rounding, opacity, border_color, border_size, max_size, min_size, animation, no_blur, no_shadow, xray, etc.Layerrule v2 - Match Properties (6):
match:namespace, match:address, match:class, match:title, match:monitor, match:layerLayerrule v2 - Effect Properties (14, updated in 0.53.0):
blur, blur_popups (new), ignorealpha/ignore_alpha, ignorezero, animation, noanim/no_anim, xraydim_around (new), order (new), above_lock (new), no_screen_share/noscreenshare (new)
#### Migration from Deprecated v1/v2 Methods
The old handler-based methods (`all_windowrules()`, `all_windowrulesv2()`, `all_layerrules()`) are deprecated in hyprlang-rs 0.4.0. Here's how to migrate:
**Old approach (deprecated):**
```rust
// Returns raw handler strings like "float, class:^(kitty)$"
let rules = hypr.all_windowrulesv2();
for rule_str in rules {
// Manual parsing required
println!("{}", rule_str);
}
New approach (v3 special categories):
// Iterate all windowrules with structured access
for name in hypr.windowrule_names() {
let rule = hypr.get_windowrule(&name)?;
// Type-safe property access
if let Ok(class) = rule.get_string("match:class") {
println!("Rule '{}' matches class: {}", name, class);
}
if let Ok(is_float) = rule.get_int("float") {
println!(" float = {}", is_float == 1);
}
}
// Same for layerrules
for name in hypr.layerrule_names() {
let rule = hypr.get_layerrule(&name)?;
if let Ok(ns) = rule.get_string("match:namespace") {
println!("Layerrule '{}' matches: {}", name, ns);
}
}
The new v3 syntax provides:
# .colors.conf
$borderSize = 3
use hyprlang::{Config, ConfigOptions};
use std::path::PathBuf;
let mut options = ConfigOptions::default();
options.base_dir = Some(PathBuf::from("/path/to/config"));
let mut config = Config::with_options(options);
// This will include another config file
config.parse(r#"
source = ./colors.conf
general {
border_size = $borderSize
}
"#)?;
Enable the mutation feature to modify configurations and save them:
use hyprlang::{Config, ConfigValue};
let mut config = Config::new();
config.parse(r#"
$GAPS = 10
border_size = 3
opacity = 0.9
"#)?;
// ===== Mutate Values =====
config.set_int("border_size", 5);
config.set_float("opacity", 1.0);
config.set("new_key", ConfigValue::String("value".to_string()));
// Remove values
let old = config.remove("opacity")?;
// ===== Mutate Variables =====
// Method 1: Direct mutation
config.set_variable("GAPS".to_string(), "15".to_string());
// Method 2: Mutable reference
if let Some(mut gaps) = config.get_variable_mut("GAPS") {
gaps.set("20")?; // Now GAPS = 20
}
// ===== Mutate Handlers =====
config.register_handler_fn("bind", |_| Ok(()));
config.add_handler_call("bind", "SUPER, Q, exec, terminal".to_string())?;
config.remove_handler_call("bind", 0)?; // Remove first bind
// ===== Serialize & Save =====
let output = config.serialize(); // Get string representation
config.save_as("config_modified.conf")?; // Save to file
// Verify round-trip
let mut config2 = Config::new();
config2.parse_file(Path::new("config_modified.conf"))?;
assert_eq!(config2.get_int("border_size")?, 5);
Run the comprehensive example:
cargo run --example mutation_example --features mutation
When your configuration uses source directives to include other files, the mutation feature automatically tracks which values came from which file and saves changes only to the modified files:
use hyprlang::Config;
use std::path::Path;
// Create config files
// main.conf:
// source = ./vars.conf
// source = ./appearance.conf
// border_size = 2
//
// vars.conf:
// $GAPS = 10
//
// appearance.conf:
// decoration {
// rounding = 5
// }
let mut config = Config::new();
config.parse_file(Path::new("main.conf"))?;
// ===== Check which file defines a key =====
let source = config.get_key_source_file("decoration:rounding");
println!("rounding is defined in: {:?}", source);
// ===== Mutate a value from appearance.conf =====
config.set_int("decoration:rounding", 15);
// ===== Check which files were modified =====
let modified = config.get_modified_files();
println!("Modified files: {:?}", modified); // Only appearance.conf
// ===== Save only the modified files =====
let saved = config.save_all()?;
println!("Saved files: {:?}", saved); // Only appearance.conf was written
// ===== The master config preserves source directives =====
// main.conf still contains:
// source = ./vars.conf
// source = ./appearance.conf
// border_size = 2
//
// appearance.conf now contains:
// decoration {
// rounding = 15 # <-- Updated!
// }
//
// vars.conf remains unchanged
// ===== List all source files =====
let all_files = config.get_source_files();
println!("All source files: {:?}", all_files);
// ===== Serialize a specific file =====
let appearance_content = config.serialize_file(Path::new("./appearance.conf"))?;
println!("appearance.conf:\n{}", appearance_content);
Key features:
use hyprlang::Config;
use std::path::Path;
let mut config = Config::new();
config.parse_file(Path::new("config.conf"))?;
// Access all keys
for key in config.keys() {
println!("Key: {}", key);
}
When the hyprland feature is enabled, you can use the high-level Hyprland struct:
use hyprlang::Hyprland;
use std::path::Path;
// Create a new Hyprland config (automatically registers all handlers)
let mut hypr = Hyprland::new();
// Parse your Hyprland config
hypr.parse_file(Path::new("~/.config/hypr/hyprland.conf"))?;
// Access config with typed methods
let border_size = hypr.general_border_size()?;
let gaps_in = hypr.general_gaps_in()?;
let active_border = hypr.general_active_border_color()?;
// Get all binds as an array
let binds = hypr.all_binds();
for bind in binds {
println!("Bind: {}", bind);
}
// Get all animations
let animations = hypr.all_animations();
println!("Found {} animations", animations.len());
// Get all window rules
let rules = hypr.all_windowrules();
for rule in rules {
println!("Rule: {}", rule);
}
// Access variables
let terminal = hypr.get_variable("terminal");
The Hyprland struct provides convenient typed access to:
use hyprlang::{Config, ConfigOptions};
use std::path::PathBuf;
let mut options = ConfigOptions::default();
// Collect all errors instead of stopping at the first one
options.throw_all_errors = false;
// Allow parsing after initial parse
options.allow_dynamic_parsing = true;
// Base directory for resolving source directives
options.base_dir = Some(PathBuf::from("/path/to/config"));
let config = Config::with_options(options);
Config - Main configuration managerConfigValue - Enum representing all value types
Int(i64) - Integer valueFloat(f64) - Float valueString(String) - String valueVec2(Vec2) - 2D coordinateColor(Color) - RGBA colorCustom { type_name, value } - Custom value typeColor - RGBA color (r, g, b, a)Vec2 - 2D coordinate (x, y)// Parsing
config.parse(content: &str) -> Result<()>
config.parse_file(path: &Path) -> Result<()>
// Getting values
config.get(key: &str) -> Result<&ConfigValue>
config.get_int(key: &str) -> Result<i64>
config.get_float(key: &str) -> Result<f64>
config.get_string(key: &str) -> Result<&str>
config.get_vec2(key: &str) -> Result<Vec2>
config.get_color(key: &str) -> Result<Color>
// Setting values
config.set(key: impl Into<String>, value: ConfigValue)
config.set_variable(name: String, value: String)
// Mutation (requires `mutation` feature)
config.set_int(key, value: i64)
config.set_float(key, value: f64)
config.set_string(key, value: impl Into<String>)
config.remove(key: &str) -> Result<ConfigValue>
config.get_variable_mut(name: &str) -> Option<MutableVariable>
config.remove_variable(name: &str) -> Option<String>
config.add_handler_call(handler, value: String) -> Result<()>
config.remove_handler_call(handler: &str, index: usize) -> Result<String>
config.get_special_category_mut(category, key) -> Result<MutableCategoryInstance>
// Serialization (requires `mutation` feature)
config.serialize() -> String
config.save() -> Result<()>
config.save_as(path: impl AsRef<Path>) -> Result<()>
// Multi-file mutation (requires `mutation` feature)
config.save_all() -> Result<Vec<PathBuf>>
config.serialize_file(path: &Path) -> Result<String>
config.get_key_source_file(key: &str) -> Option<&Path>
config.get_source_files() -> Vec<&Path>
config.get_modified_files() -> Vec<&Path>
// Querying
config.keys() -> Vec<&str>
config.variables() -> &HashMap<String, String>
config.has(key: &str) -> bool
// Handlers
config.register_handler_fn(keyword, handler_fn)
config.register_category_handler_fn(category, keyword, handler_fn)
config.get_handler_calls(handler: &str) -> Option<&Vec<String>>
config.all_handler_calls() -> &HashMap<String, Vec<String>>
// Special categories
config.register_special_category(descriptor)
config.get_special_category(category: &str, key: &str) -> Result<HashMap<String, &ConfigValue>>
The repository includes several examples demonstrating different features:
examples/pretty_print.rsA comprehensive example showing a complete configuration with all features:
cargo run --example pretty_print
examples/parse_hyprland.rsParse and pretty-print a real Hyprland configuration file:
cargo run --example parse_hyprland
This example demonstrates:
examples/hyprland_api.rsDemonstrate the high-level Hyprland API (requires hyprland feature):
cargo run --example hyprland_api --features hyprland
This example demonstrates:
Hyprland struct for typed config accessexamples/mutation_example.rsComprehensive example demonstrating mutation and serialization (requires mutation feature):
cargo run --example mutation_example --features mutation
This example demonstrates:
Run the full test suite:
cargo test
# Run with all features enabled
cargo test --all-features
The project includes 177 tests with 100% pass rate:
All tests from the original Hyprlang C++ implementation have been ported and pass successfully, plus additional tests for new features like expression escaping, negated conditionals, windowrule v3 syntax, and comprehensive edge case coverage.
The parser is implemented using pest with a PEG grammar. The grammar file is located at src/hyprlang.pest.
Key syntax features:
# for single-line, ## for documentation$VAR = value, $env:PATH (environment variables){{expr}} with arithmetic operators (+, -, *, /)\{{}} or {\{}} for literal bracescategory { ... } (nested supported)category[key] { ... } (keyed, static, anonymous)key = valuekeyword = value (with optional flags: keyword[flag])source = path# hyprlang if VAR, # hyprlang if !VAR, # hyprlang endif# hyprlang noerror true/falseThis project is a reimplementation of Hyprlang in Rust, based on the original C++ implementation by the Hyprland team.
Contributions are welcome! Please ensure all tests pass before submitting a PR:
cargo test
cargo clippy
cargo fmt --check