hyprlang-rs
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.
Features
- 🎯 Complete Hyprlang Implementation - Full compatibility with the original C++ version
- 🚀 Fast PEG Parser - Built with pest for efficient parsing
- 🔧 Type-Safe API - Strongly-typed configuration values (Int, Float, String, Vec2, Color)
- 📦 Variable System - Support for user-defined and environment variables with cycle detection
- 🧮 Expression Evaluation - Arithmetic expressions with
{{expr}}syntax - 🎨 Color Support - Multiple color formats:
rgba(),rgb(), and hex colors - 📐 Vec2 Coordinates - Built-in support for 2D coordinate pairs
- 🔌 Handler System - Extensible keyword handlers for custom syntax
- 🏷️ Special Categories - Keyed, static, and anonymous category types
- 📄 Source Directives - Include external configuration files
- 💬 Conditional Directives -
# hyprlang if/endif/noerrorsupport with negation - 🎨 Expression Escaping - Escape expressions with
\{{}}or{\{}}for literal braces - 🔄 Mutation & Serialization - Modify config values and save back to files (optional)
- 🎯 Windowrule v3 / Layerrule v2 - Full support for new special category syntax with 85+ registered properties
- ✅ Fully Tested - 171 tests covering all features
Installation
Add this to your Cargo.toml:
[]
= "0.2.1"
Optional Features
hyprland Feature
Enable the hyprland feature to get a high-level Hyprland struct with pre-configured handlers and typed access to Hyprland configuration options:
[]
= { = "0.2.1", = ["hyprland"] }
This feature provides:
- Automatic registration of all Hyprland handlers (bind, monitor, env, etc.)
- Typed accessor methods for common Hyprland config values
- Convenient methods to access all binds, windowrules, animations, etc.
mutation Feature
Enable the mutation feature to modify configuration values and serialize configs back to files:
[]
= { = "0.2.1", = ["mutation"] }
This feature provides:
- Mutation API - Modify config values, variables, handlers, and special categories
- Serialization - Save configurations back to files with clean formatting
- Two mutation styles - Direct setters and mutable references
- Round-trip support - Parse → modify → save → parse
Quick Start
use Config;
Hyprland API (Optional Feature)
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.
Why Use the Hyprland Feature?
Without the Hyprland feature (using low-level Config API):
use Config;
let mut config = new;
// Manually register all handlers
config.register_handler_fn;
config.register_handler_fn;
config.register_handler_fn;
// ... register 20+ more handlers
config.register_category_handler_fn;
config.register_category_handler_fn;
// Access values with string keys and manual type conversion
let border_size = config.get_int?;
let gaps_in = config.get_string?; // Could be int or string
let binds = config.get_handler_calls.unwrap_or;
With the Hyprland feature (using high-level Hyprland API):
use Hyprland;
let mut hypr = 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>
Complete Example
use Hyprland;
use Path;
Available Methods
General Settings
hypr.general_border_size .general_gaps_in // CSS-style: "5" or "5 10 15 20"
hypr.general_gaps_out // CSS-style: "20" or "5 10 15 20"
hypr.general_layout // "dwindle" or "master"
hypr.general_allow_tearing .general_active_border_color .general_inactive_border_color
Decoration Settings
hypr.decoration_rounding .decoration_active_opacity .decoration_inactive_opacity .decoration_blur_enabled .decoration_blur_size .decoration_blur_passes
Animation Settings
hypr.animations_enabled .all_animations // All animation definitions
hypr.all_beziers // All bezier curve definitions
Input Settings
hypr.input_kb_layout .input_follow_mouse .input_sensitivity .input_touchpad_natural_scroll
Layout Settings
hypr.dwindle_pseudotile .dwindle_preserve_split .master_new_status
Misc Settings
hypr.misc_disable_hyprland_logo .misc_force_default_wallpaper
Handler Calls (Arrays)
hypr.all_binds // All bind definitions
hypr.all_bindm // All mouse bindings
hypr.all_bindel // All bindel definitions
hypr.all_bindl // All bindl definitions
hypr.all_windowrules // All windowrule v1 definitions (deprecated)
hypr.all_windowrulesv2 // All windowrule v2 definitions (deprecated)
hypr.all_layerrules // All layerrule v1 definitions (deprecated)
hypr.all_workspaces // All workspace definitions
hypr.all_monitors // All monitor definitions
hypr.all_env // All env definitions
hypr.all_exec // All exec definitions
hypr.all_exec_once // All exec-once definitions
Windowrule v3 & Layerrule v2 (Special Categories)
// New v3 syntax for windowrules
hypr.windowrule_names // All windowrule names
hypr.get_windowrule // Get specific rule
// New v2 syntax for layerrules
hypr.layerrule_names // All layerrule names
hypr.get_layerrule // Get specific rule
// RuleInstance helper provides typed access to properties:
rule.get // Get any property
rule.get_string // Get as string
rule.get_int // Get as integer
rule.get_float // Get as float
rule.get_color // Get as color
Variables
hypr.variables // All variables
hypr.get_variable // Get specific variable
Direct Config Access
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?;
config.register_handler_fn;
What Handlers Are Pre-Registered?
The Hyprland struct automatically registers these handlers:
Root-level handlers:
monitor- Monitor configurationenv- Environment variablesbind,bindm,bindel,bindl,bindr,binde,bindn- Keybindingswindowrule,windowrulev2- Window ruleslayerrule- Layer rulesworkspace- Workspace configurationexec,exec-once- Commandssource- File inclusionblurls- Blur layer surfaceplugin- Plugin loading
Category-specific handlers:
animations:animation- Animation definitionsanimations:bezier- Bezier curve definitions
Special 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)
When to Use Each API
Use the Hyprland API when:
- ✅ You're working specifically with Hyprland configurations
- ✅ You want typed, convenient access to common config values
- ✅ You want all Hyprland handlers pre-registered automatically
- ✅ You're building tools for Hyprland users (config editors, validators, etc.)
Use the low-level Config API when:
- ✅ You're implementing a different config language
- ✅ You need full control over handler registration
- ✅ You're working with a custom config format
- ✅ You want minimal dependencies (no Hyprland-specific code)
Usage Examples
Basic Values
use Config;
let mut config = new;
config.parse?;
assert_eq!;
assert_eq!;
assert_eq!;
Variables
use Config;
let mut config = new;
config.parse?;
// Access variables directly
let vars = config.variables;
assert_eq!;
// Or access expanded values
assert_eq!;
Colors
use Config;
let mut config = new;
config.parse?;
let color = config.get_color?;
println!;
Vec2 (2D Coordinates)
use Config;
let mut config = new;
config.parse?;
let pos = config.get_vec2?;
assert_eq!;
assert_eq!;
Expressions
use Config;
let mut config = new;
config.parse?;
assert_eq!;
assert_eq!;
assert_eq!;
Nested Categories
use Config;
let mut config = new;
config.parse?;
// Access with colon-separated paths
assert_eq!;
assert_eq!;
assert_eq!;
Custom Handlers
use Config;
let mut config = new;
// Register a handler for custom keywords
config.register_handler_fn;
config.parse?;
// Access handler calls as arrays
let binds = config.get_handler_calls.unwrap;
assert_eq!;
Category-Specific Handlers
use Config;
let mut config = new;
// Register handlers that only work in specific categories
config.register_category_handler_fn;
config.parse?;
// Handler calls are namespaced by category
let anims = config.get_handler_calls.unwrap;
assert_eq!;
Special Categories
use ;
let mut config = new;
// Register a special category
config.register_special_category;
config.parse?;
// Access keyed category instances
let mouse = config.get_special_category?;
println!;
Windowrule v3 / Layerrule v2 (Hyprland Feature)
The new windowrule v3 and layerrule v2 syntax uses special category blocks:
use Hyprland;
let mut hypr = new;
hypr.parse?;
// Access windowrules
let names = hypr.windowrule_names; // vec!["float-terminals"]
let rule = hypr.get_windowrule?;
// Get properties with type safety
let class_match = rule.get_string?; // "^(kitty|alacritty)$"
let is_float = rule.get_int?; // 1 (true)
let opacity = rule.get_float?; // 0.95
let color = rule.get_color?; // Color { r: 51, g: 204, b: 255, a: 238 }
// Old v2 handler syntax still works for backward compatibility
hypr.parse?;
let v2_rules = hypr.all_windowrulesv2;
Supported Properties
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_token
Windowrule v3 - Effect Properties (60+ with aliases):
- Static:
float,tile,fullscreen,maximize,move,size,center,pseudo,monitor,workspace,pin,group, etc. - Dynamic:
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:layer
Layerrule v2 - Effect Properties (6):
blur,ignorealpha,ignorezero,animation,noanim,xray
### Source Directive
```hyprlang
# .colors.conf
$borderSize = 3
use ;
use PathBuf;
let mut options = default;
options.base_dir = Some;
let mut config = with_options;
// This will include another config file
config.parse?;
Mutation & Serialization (Optional Feature)
Enable the mutation feature to modify configurations and save them:
use ;
let mut config = new;
config.parse?;
// ===== Mutate Values =====
config.set_int;
config.set_float;
config.set;
// Remove values
let old = config.remove?;
// ===== Mutate Variables =====
// Method 1: Direct mutation
config.set_variable;
// Method 2: Mutable reference
if let Some = config.get_variable_mut
// ===== Mutate Handlers =====
config.register_handler_fn;
config.add_handler_call?;
config.remove_handler_call?; // Remove first bind
// ===== Serialize & Save =====
let output = config.serialize; // Get string representation
config.save_as?; // Save to file
// Verify round-trip
let mut config2 = new;
config2.parse_file?;
assert_eq!;
Run the comprehensive example:
Parse from File
use Config;
use Path;
let mut config = new;
config.parse_file?;
// Access all keys
for key in config.keys
Hyprland Feature (Optional)
When the hyprland feature is enabled, you can use the high-level Hyprland struct:
use Hyprland;
use Path;
// Create a new Hyprland config (automatically registers all handlers)
let mut hypr = new;
// Parse your Hyprland config
hypr.parse_file?;
// 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
// Get all animations
let animations = hypr.all_animations;
println!;
// Get all window rules
let rules = hypr.all_windowrules;
for rule in rules
// Access variables
let terminal = hypr.get_variable;
The Hyprland struct provides convenient typed access to:
- General settings: border_size, gaps, colors, layout, etc.
- Decoration: rounding, opacity, blur settings
- Animations: enabled status, all animations, all beziers
- Input: keyboard layout, mouse settings, touchpad
- Layout: dwindle and master layout settings
- Handlers: all binds, windowrules, monitors, env vars, exec-once, etc.
- Variables: all user-defined variables
Configuration Options
use ;
use PathBuf;
let mut options = 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;
let config = with_options;
API Overview
Main Types
Config- Main configuration managerConfigValue- Enum representing all value typesInt(i64)- Integer valueFloat(f64)- Float valueString(String)- String valueVec2(Vec2)- 2D coordinateColor(Color)- RGBA colorCustom { type_name, value }- Custom value type
Color- RGBA color (r, g, b, a)Vec2- 2D coordinate (x, y)
Key Methods
// Parsing
config.parse .parse_file // Getting values
config.get .get_int .get_float .get_string .get_vec2 .get_color // Setting values
config.set
Examples
The repository includes several examples demonstrating different features:
examples/pretty_print.rs
A comprehensive example showing a complete configuration with all features:
examples/parse_hyprland.rs
Parse and pretty-print a real Hyprland configuration file:
This example demonstrates:
- Parsing complex real-world configs
- Variable handling
- Nested categories
- Handler calls (binds, windowrules, etc.)
- Beautiful formatted output
examples/hyprland_api.rs
Demonstrate the high-level Hyprland API (requires hyprland feature):
This example demonstrates:
- Using the
Hyprlandstruct for typed config access - Accessing general, decoration, animation, and input settings
- Getting all binds, windowrules, and other handler calls
- Working with variables
- Comprehensive display of all Hyprland configuration options
examples/mutation_example.rs
Comprehensive example demonstrating mutation and serialization (requires mutation feature):
This example demonstrates:
- Mutating configuration values (both direct setters and mutable references)
- Mutating variables using both API styles
- Adding and removing handler calls
- Serializing configurations to strings
- Saving configurations to files
- Round-trip verification (parse → mutate → save → parse)
Testing
Run the full test suite:
# Run with all features enabled
The project includes 171 tests with 100% pass rate:
- 52 unit tests covering core functionality
- 11 conditional directive tests
- 11 expression escaping tests
- 15 windowrule v3 / layerrule v2 tests
- 12 Hyprland config tests
- 10 mutation & round-trip serialization tests
- 19 parsing edge case tests
- 41 documentation tests
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.
Grammar
The parser is implemented using pest with a PEG grammar. The grammar file is located at src/hyprlang.pest.
Key syntax features:
- Comments:
#for single-line,##for documentation - Variables:
$VAR = value,$env:PATH(environment variables) - Expressions:
{{expr}}with arithmetic operators (+, -, *, /) - Expression escaping:
\{{}}or{\{}}for literal braces - Categories:
category { ... }(nested supported) - Special categories:
category[key] { ... }(keyed, static, anonymous) - Assignments:
key = value - Handlers:
keyword = value(with optional flags:keyword[flag]) - Source directive:
source = path - Conditional directives:
# hyprlang if VAR,# hyprlang if !VAR,# hyprlang endif - Error suppression:
# hyprlang noerror true/false
License
This project is a reimplementation of Hyprlang in Rust, based on the original C++ implementation by the Hyprland team.
Contributing
Contributions are welcome! Please ensure all tests pass before submitting a PR: