mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-22 02:01:55 +07:00
Implement include optional=true (#3022)
* feat(niri): support `include optional=true "filename.kdl"` * chore: warn if optional include ENOENT * chore: validate include directive arguments and properties Add proper validation to reject: - Extra arguments beyond the path - Unknown properties (other than "optional") - Unexpected child nodes * docs: implement suggested typographical/prose changes * fixes --------- Co-authored-by: Ivan Molodetskikh <yalterz@gmail.com>
This commit is contained in:
@@ -114,6 +114,30 @@ window-rule {
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Optional includes
|
||||||
|
|
||||||
|
<sup>Since: next release</sup>
|
||||||
|
|
||||||
|
By default, including a nonexistent file will cause an error.
|
||||||
|
You can allow nonexistent includes by setting `optional=true`:
|
||||||
|
|
||||||
|
```kdl,must-fail
|
||||||
|
// Won't fail if this file doesn't exist.
|
||||||
|
include optional=true "optional-config.kdl"
|
||||||
|
|
||||||
|
// Regular include, will fail if the file doesn't exist.
|
||||||
|
include "required-config.kdl"
|
||||||
|
```
|
||||||
|
|
||||||
|
When an optional include file is missing, niri will emit a warning in the logs on every config reload.
|
||||||
|
This reminds you that the file is missing while still loading the config successfully.
|
||||||
|
|
||||||
|
The optional file is still watched for changes, so if you create it later, the config will automatically reload and apply the new settings.
|
||||||
|
|
||||||
|
Note that `optional` only affects whether a missing file causes an error.
|
||||||
|
If the file exists but contains invalid syntax or other errors, those errors will still cause a parsing failure.
|
||||||
|
|
||||||
|
|
||||||
### Merging
|
### Merging
|
||||||
|
|
||||||
Most config sections are merged between includes, meaning that you can set only a few properties, and only those properties will change.
|
Most config sections are merged between includes, meaning that you can set only a few properties, and only those properties will change.
|
||||||
|
|||||||
+55
-5
@@ -291,7 +291,51 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
"include" => {
|
"include" => {
|
||||||
let path: PathBuf = utils::parse_arg_node("include", node, ctx)?;
|
// Parse the path argument
|
||||||
|
let mut iter_args = node.arguments.iter();
|
||||||
|
let path_val = iter_args.next().ok_or_else(|| {
|
||||||
|
DecodeError::missing(
|
||||||
|
node,
|
||||||
|
"additional argument for include path is required",
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
let path: PathBuf = knuffel::traits::DecodeScalar::decode(path_val, ctx)?;
|
||||||
|
|
||||||
|
// Check for extra arguments
|
||||||
|
if let Some(val) = iter_args.next() {
|
||||||
|
ctx.emit_error(DecodeError::unexpected(
|
||||||
|
&val.literal,
|
||||||
|
"argument",
|
||||||
|
"unexpected argument",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the optional property
|
||||||
|
let mut optional = false;
|
||||||
|
for (name, val) in &node.properties {
|
||||||
|
match &***name {
|
||||||
|
"optional" => {
|
||||||
|
optional = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
||||||
|
}
|
||||||
|
name_str => {
|
||||||
|
ctx.emit_error(DecodeError::unexpected(
|
||||||
|
name,
|
||||||
|
"property",
|
||||||
|
format!("unexpected property `{}`", name_str.escape_default()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for unexpected children
|
||||||
|
for child in node.children() {
|
||||||
|
ctx.emit_error(DecodeError::unexpected(
|
||||||
|
child,
|
||||||
|
"node",
|
||||||
|
format!("unexpected node `{}`", child.node_name.escape_default()),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
let base = ctx.get::<BasePath>().unwrap();
|
let base = ctx.get::<BasePath>().unwrap();
|
||||||
let path = base.0.join(path);
|
let path = base.0.join(path);
|
||||||
|
|
||||||
@@ -369,10 +413,16 @@ where
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
ctx.emit_error(DecodeError::missing(
|
if optional && err.kind() == std::io::ErrorKind::NotFound {
|
||||||
node,
|
// Warn about missing optional includes
|
||||||
format!("failed to read included config from {path:?}: {err}"),
|
warn!("optional include not found: {path:?}");
|
||||||
));
|
} else {
|
||||||
|
// Report all other errors normally
|
||||||
|
ctx.emit_error(DecodeError::missing(
|
||||||
|
node,
|
||||||
|
format!("failed to read included config from {path:?}: {err}"),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user