Skip to content

Commit 22b8ba9

Browse files
feat: preserve multi file mutation structure (#8)
1 parent d1d8ee5 commit 22b8ba9

8 files changed

Lines changed: 866 additions & 18 deletions

File tree

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "hyprlang"
3-
version = "0.2.1"
3+
version = "0.3.0"
44
edition = "2024"
55
authors = ["Alex Spinu"]
66
description = "A scripting language interpreter and parser for Hyprlang and Hyprland configuration files."

README.md

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,9 @@ This project is not endorsed by or affiliated with the Hyprland project/HyprWM O
2929
- 💬 **Conditional Directives** - `# hyprlang if/endif/noerror` support with negation
3030
- 🎨 **Expression Escaping** - Escape expressions with `\{{}}` or `{\{}}` for literal braces
3131
- 🔄 **Mutation & Serialization** - Modify config values and save back to files (optional)
32+
- 📁 **Multi-File Mutation Tracking** - Track and save changes to the correct source file when using `source` directives
3233
- 🎯 **Windowrule v3 / Layerrule v2** - Full support for new special category syntax with 85+ registered properties
33-
-**Fully Tested** - 171 tests covering all features
34+
-**Fully Tested** - 177 tests covering all features
3435

3536
## Installation
3637

@@ -69,6 +70,8 @@ hyprlang = { version = "0.2.1", features = ["mutation"] }
6970
This feature provides:
7071
- **Mutation API** - Modify config values, variables, handlers, and special categories
7172
- **Serialization** - Save configurations back to files with clean formatting
73+
- **Multi-file tracking** - Automatically track which values came from which source file
74+
- **Smart saving** - Save changes only to the modified files when using `source` directives
7275
- **Two mutation styles** - Direct setters and mutable references
7376
- **Round-trip support** - Parse → modify → save → parse
7477

@@ -720,6 +723,74 @@ Run the comprehensive example:
720723
cargo run --example mutation_example --features mutation
721724
```
722725

726+
### Multi-File Mutation (Optional Feature)
727+
728+
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:
729+
730+
```rust
731+
use hyprlang::Config;
732+
use std::path::Path;
733+
734+
// Create config files
735+
// main.conf:
736+
// source = ./vars.conf
737+
// source = ./appearance.conf
738+
// border_size = 2
739+
//
740+
// vars.conf:
741+
// $GAPS = 10
742+
//
743+
// appearance.conf:
744+
// decoration {
745+
// rounding = 5
746+
// }
747+
748+
let mut config = Config::new();
749+
config.parse_file(Path::new("main.conf"))?;
750+
751+
// ===== Check which file defines a key =====
752+
let source = config.get_key_source_file("decoration:rounding");
753+
println!("rounding is defined in: {:?}", source);
754+
755+
// ===== Mutate a value from appearance.conf =====
756+
config.set_int("decoration:rounding", 15);
757+
758+
// ===== Check which files were modified =====
759+
let modified = config.get_modified_files();
760+
println!("Modified files: {:?}", modified); // Only appearance.conf
761+
762+
// ===== Save only the modified files =====
763+
let saved = config.save_all()?;
764+
println!("Saved files: {:?}", saved); // Only appearance.conf was written
765+
766+
// ===== The master config preserves source directives =====
767+
// main.conf still contains:
768+
// source = ./vars.conf
769+
// source = ./appearance.conf
770+
// border_size = 2
771+
//
772+
// appearance.conf now contains:
773+
// decoration {
774+
// rounding = 15 # <-- Updated!
775+
// }
776+
//
777+
// vars.conf remains unchanged
778+
779+
// ===== List all source files =====
780+
let all_files = config.get_source_files();
781+
println!("All source files: {:?}", all_files);
782+
783+
// ===== Serialize a specific file =====
784+
let appearance_content = config.serialize_file(Path::new("./appearance.conf"))?;
785+
println!("appearance.conf:\n{}", appearance_content);
786+
```
787+
788+
**Key features:**
789+
- 🎯 **Automatic tracking** - No need to manually specify which file to update
790+
- 💾 **Selective saving** - Only modified files are written to disk
791+
- 🔒 **Structure preservation** - Source directives remain intact in the master config
792+
- 🔍 **File inspection** - Query which file defines any key
793+
723794
### Parse from File
724795

725796
```rust
@@ -853,6 +924,13 @@ config.serialize() -> String
853924
config.save() -> Result<()>
854925
config.save_as(path: impl AsRef<Path>) -> Result<()>
855926

927+
// Multi-file mutation (requires `mutation` feature)
928+
config.save_all() -> Result<Vec<PathBuf>>
929+
config.serialize_file(path: &Path) -> Result<String>
930+
config.get_key_source_file(key: &str) -> Option<&Path>
931+
config.get_source_files() -> Vec<&Path>
932+
config.get_modified_files() -> Vec<&Path>
933+
856934
// Querying
857935
config.keys() -> Vec<&str>
858936
config.variables() -> &HashMap<String, String>
@@ -938,13 +1016,14 @@ cargo test
9381016
cargo test --all-features
9391017
```
9401018

941-
The project includes **171 tests** with 100% pass rate:
1019+
The project includes **177 tests** with 100% pass rate:
9421020
- 52 unit tests covering core functionality
9431021
- 11 conditional directive tests
9441022
- 11 expression escaping tests
9451023
- 15 windowrule v3 / layerrule v2 tests
9461024
- 12 Hyprland config tests
9471025
- 10 mutation & round-trip serialization tests
1026+
- 6 multi-file mutation tests
9481027
- 19 parsing edge case tests
9491028
- 41 documentation tests
9501029

0 commit comments

Comments
 (0)