mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-21 02:01:55 +07:00
config: Move some stuff to utils
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
use knuffel::errors::DecodeError;
|
||||
use knuffel::Decode as _;
|
||||
|
||||
use crate::{expect_only_children, parse_arg_node, FloatOrInt};
|
||||
use crate::utils::{expect_only_children, parse_arg_node};
|
||||
use crate::FloatOrInt;
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
||||
pub struct Animations {
|
||||
|
||||
@@ -12,7 +12,7 @@ use smithay::input::keyboard::keysyms::KEY_NoSymbol;
|
||||
use smithay::input::keyboard::xkb::{keysym_from_name, KEYSYM_CASE_INSENSITIVE};
|
||||
use smithay::input::keyboard::Keysym;
|
||||
|
||||
use crate::expect_only_children;
|
||||
use crate::utils::expect_only_children;
|
||||
|
||||
#[derive(Debug, Default, PartialEq)]
|
||||
pub struct Binds(pub Vec<Bind>);
|
||||
|
||||
@@ -5,7 +5,8 @@ use smithay::input::keyboard::XkbConfig;
|
||||
use smithay::reexports::input;
|
||||
|
||||
use crate::binds::Modifiers;
|
||||
use crate::{FloatOrInt, Percent};
|
||||
use crate::utils::Percent;
|
||||
use crate::FloatOrInt;
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Default, PartialEq)]
|
||||
pub struct Input {
|
||||
|
||||
@@ -4,7 +4,8 @@ use niri_ipc::{ColumnDisplay, SizeChange};
|
||||
use crate::appearance::{
|
||||
Border, FocusRing, InsertHint, Shadow, TabIndicator, DEFAULT_BACKGROUND_COLOR,
|
||||
};
|
||||
use crate::{expect_only_children, Color, FloatOrInt};
|
||||
use crate::utils::expect_only_children;
|
||||
use crate::{Color, FloatOrInt};
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq)]
|
||||
pub struct Layout {
|
||||
|
||||
+2
-160
@@ -5,10 +5,9 @@ use std::ffi::OsStr;
|
||||
use std::fs::{self, File};
|
||||
use std::io::Write;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use knuffel::errors::DecodeError;
|
||||
use miette::{miette, Context, IntoDiagnostic};
|
||||
use miette::{Context, IntoDiagnostic};
|
||||
|
||||
pub mod animations;
|
||||
pub mod appearance;
|
||||
@@ -33,6 +32,7 @@ pub use crate::input::{Input, ModKey, ScrollMethod, TrackLayout, WarpMouseToFocu
|
||||
pub use crate::layer_rule::LayerRule;
|
||||
pub use crate::layout::*;
|
||||
pub use crate::output::{Output, OutputName, Outputs, Position, Vrr};
|
||||
pub use crate::utils::FloatOrInt;
|
||||
pub use crate::window_rule::{FloatingPosition, RelativeTo, WindowRule};
|
||||
|
||||
#[derive(knuffel::Decode, Debug, PartialEq)]
|
||||
@@ -89,13 +89,6 @@ pub struct Config {
|
||||
pub workspaces: Vec<Workspace>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Percent(pub f64);
|
||||
|
||||
// MIN and MAX generics are only used during parsing to check the value.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct FloatOrInt<const MIN: i32, const MAX: i32>(pub f64);
|
||||
|
||||
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
|
||||
pub struct SpawnAtStartup {
|
||||
#[knuffel(arguments)]
|
||||
@@ -210,72 +203,6 @@ pub struct Workspace {
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct WorkspaceName(pub String);
|
||||
|
||||
impl<S: knuffel::traits::ErrorSpan, const MIN: i32, const MAX: i32> knuffel::DecodeScalar<S>
|
||||
for FloatOrInt<MIN, MAX>
|
||||
{
|
||||
fn type_check(
|
||||
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) {
|
||||
if let Some(type_name) = &type_name {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
type_name,
|
||||
"type name",
|
||||
"no type name expected for this node",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_decode(
|
||||
val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
match &**val {
|
||||
knuffel::ast::Literal::Int(ref value) => match value.try_into() {
|
||||
Ok(v) => {
|
||||
if (MIN..=MAX).contains(&v) {
|
||||
Ok(FloatOrInt(f64::from(v)))
|
||||
} else {
|
||||
ctx.emit_error(DecodeError::conversion(
|
||||
val,
|
||||
format!("value must be between {MIN} and {MAX}"),
|
||||
));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.emit_error(DecodeError::conversion(val, e));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
},
|
||||
knuffel::ast::Literal::Decimal(ref value) => match value.try_into() {
|
||||
Ok(v) => {
|
||||
if (f64::from(MIN)..=f64::from(MAX)).contains(&v) {
|
||||
Ok(FloatOrInt(v))
|
||||
} else {
|
||||
ctx.emit_error(DecodeError::conversion(
|
||||
val,
|
||||
format!("value must be between {MIN} and {MAX}"),
|
||||
));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.emit_error(DecodeError::conversion(val, e));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
ctx.emit_error(DecodeError::unsupported(
|
||||
val,
|
||||
"Unsupported value, only numbers are recognized",
|
||||
));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum ConfigPath {
|
||||
/// Explicitly set config path.
|
||||
@@ -418,74 +345,6 @@ impl Default for Config {
|
||||
}
|
||||
}
|
||||
|
||||
fn expect_only_children<S>(
|
||||
node: &knuffel::ast::SpannedNode<S>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) where
|
||||
S: knuffel::traits::ErrorSpan,
|
||||
{
|
||||
if let Some(type_name) = &node.type_name {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
type_name,
|
||||
"type name",
|
||||
"no type name expected for this node",
|
||||
));
|
||||
}
|
||||
|
||||
for val in node.arguments.iter() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
&val.literal,
|
||||
"argument",
|
||||
"no arguments expected for this node",
|
||||
))
|
||||
}
|
||||
|
||||
for name in node.properties.keys() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
name,
|
||||
"property",
|
||||
"no properties expected for this node",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_arg_node<S: knuffel::traits::ErrorSpan, T: knuffel::traits::DecodeScalar<S>>(
|
||||
name: &str,
|
||||
node: &knuffel::ast::SpannedNode<S>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) -> Result<T, DecodeError<S>> {
|
||||
let mut iter_args = node.arguments.iter();
|
||||
let val = iter_args.next().ok_or_else(|| {
|
||||
DecodeError::missing(node, format!("additional argument `{name}` is required"))
|
||||
})?;
|
||||
|
||||
let value = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
||||
|
||||
if let Some(val) = iter_args.next() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
&val.literal,
|
||||
"argument",
|
||||
"unexpected argument",
|
||||
));
|
||||
}
|
||||
for name in node.properties.keys() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
name,
|
||||
"property",
|
||||
format!("unexpected property `{}`", name.escape_default()),
|
||||
));
|
||||
}
|
||||
for child in node.children() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
child,
|
||||
"node",
|
||||
format!("unexpected node `{}`", child.node_name.escape_default()),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {
|
||||
fn type_check(
|
||||
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
|
||||
@@ -537,23 +396,6 @@ impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Percent {
|
||||
type Err = miette::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let Some((value, empty)) = s.split_once('%') else {
|
||||
return Err(miette!("value must end with '%'"));
|
||||
};
|
||||
|
||||
if !empty.is_empty() {
|
||||
return Err(miette!("trailing characters after '%' are not allowed"));
|
||||
}
|
||||
|
||||
let value: f64 = value.parse().map_err(|_| miette!("error parsing value"))?;
|
||||
Ok(Percent(value / 100.))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use insta::assert_debug_snapshot;
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
use knuffel::errors::DecodeError;
|
||||
use miette::miette;
|
||||
use regex::Regex;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
pub struct Percent(pub f64);
|
||||
|
||||
// MIN and MAX generics are only used during parsing to check the value.
|
||||
#[derive(Debug, Default, Clone, Copy, PartialEq)]
|
||||
pub struct FloatOrInt<const MIN: i32, const MAX: i32>(pub f64);
|
||||
|
||||
/// `Regex` that implements `PartialEq` by its string form.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct RegexEq(pub Regex);
|
||||
@@ -21,3 +30,154 @@ impl FromStr for RegexEq {
|
||||
Regex::from_str(s).map(Self)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Percent {
|
||||
type Err = miette::Error;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let Some((value, empty)) = s.split_once('%') else {
|
||||
return Err(miette!("value must end with '%'"));
|
||||
};
|
||||
|
||||
if !empty.is_empty() {
|
||||
return Err(miette!("trailing characters after '%' are not allowed"));
|
||||
}
|
||||
|
||||
let value: f64 = value.parse().map_err(|_| miette!("error parsing value"))?;
|
||||
Ok(Percent(value / 100.))
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: knuffel::traits::ErrorSpan, const MIN: i32, const MAX: i32> knuffel::DecodeScalar<S>
|
||||
for FloatOrInt<MIN, MAX>
|
||||
{
|
||||
fn type_check(
|
||||
type_name: &Option<knuffel::span::Spanned<knuffel::ast::TypeName, S>>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) {
|
||||
if let Some(type_name) = &type_name {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
type_name,
|
||||
"type name",
|
||||
"no type name expected for this node",
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
fn raw_decode(
|
||||
val: &knuffel::span::Spanned<knuffel::ast::Literal, S>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) -> Result<Self, DecodeError<S>> {
|
||||
match &**val {
|
||||
knuffel::ast::Literal::Int(ref value) => match value.try_into() {
|
||||
Ok(v) => {
|
||||
if (MIN..=MAX).contains(&v) {
|
||||
Ok(FloatOrInt(f64::from(v)))
|
||||
} else {
|
||||
ctx.emit_error(DecodeError::conversion(
|
||||
val,
|
||||
format!("value must be between {MIN} and {MAX}"),
|
||||
));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.emit_error(DecodeError::conversion(val, e));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
},
|
||||
knuffel::ast::Literal::Decimal(ref value) => match value.try_into() {
|
||||
Ok(v) => {
|
||||
if (f64::from(MIN)..=f64::from(MAX)).contains(&v) {
|
||||
Ok(FloatOrInt(v))
|
||||
} else {
|
||||
ctx.emit_error(DecodeError::conversion(
|
||||
val,
|
||||
format!("value must be between {MIN} and {MAX}"),
|
||||
));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
}
|
||||
Err(e) => {
|
||||
ctx.emit_error(DecodeError::conversion(val, e));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
},
|
||||
_ => {
|
||||
ctx.emit_error(DecodeError::unsupported(
|
||||
val,
|
||||
"Unsupported value, only numbers are recognized",
|
||||
));
|
||||
Ok(FloatOrInt::default())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expect_only_children<S>(
|
||||
node: &knuffel::ast::SpannedNode<S>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) where
|
||||
S: knuffel::traits::ErrorSpan,
|
||||
{
|
||||
if let Some(type_name) = &node.type_name {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
type_name,
|
||||
"type name",
|
||||
"no type name expected for this node",
|
||||
));
|
||||
}
|
||||
|
||||
for val in node.arguments.iter() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
&val.literal,
|
||||
"argument",
|
||||
"no arguments expected for this node",
|
||||
))
|
||||
}
|
||||
|
||||
for name in node.properties.keys() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
name,
|
||||
"property",
|
||||
"no properties expected for this node",
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn parse_arg_node<S: knuffel::traits::ErrorSpan, T: knuffel::traits::DecodeScalar<S>>(
|
||||
name: &str,
|
||||
node: &knuffel::ast::SpannedNode<S>,
|
||||
ctx: &mut knuffel::decode::Context<S>,
|
||||
) -> Result<T, DecodeError<S>> {
|
||||
let mut iter_args = node.arguments.iter();
|
||||
let val = iter_args.next().ok_or_else(|| {
|
||||
DecodeError::missing(node, format!("additional argument `{name}` is required"))
|
||||
})?;
|
||||
|
||||
let value = knuffel::traits::DecodeScalar::decode(val, ctx)?;
|
||||
|
||||
if let Some(val) = iter_args.next() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
&val.literal,
|
||||
"argument",
|
||||
"unexpected argument",
|
||||
));
|
||||
}
|
||||
for name in node.properties.keys() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
name,
|
||||
"property",
|
||||
format!("unexpected property `{}`", name.escape_default()),
|
||||
));
|
||||
}
|
||||
for child in node.children() {
|
||||
ctx.emit_error(DecodeError::unexpected(
|
||||
child,
|
||||
"node",
|
||||
format!("unexpected node `{}`", child.node_name.escape_default()),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(value)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user