config: Extract misc

This commit is contained in:
Ivan Molodetskikh
2025-08-27 11:01:59 +03:00
parent c637d4ce99
commit 178469301b
2 changed files with 171 additions and 166 deletions

View File

@@ -6,7 +6,6 @@ use std::fs::{self, File};
use std::io::Write;
use std::path::{Path, PathBuf};
use knuffel::errors::DecodeError;
use miette::{Context, IntoDiagnostic};
pub mod animations;
@@ -17,6 +16,7 @@ pub mod gestures;
pub mod input;
pub mod layer_rule;
pub mod layout;
pub mod misc;
pub mod output;
pub mod utils;
pub mod window_rule;
@@ -31,6 +31,7 @@ pub use crate::gestures::Gestures;
pub use crate::input::{Input, ModKey, ScrollMethod, TrackLayout, WarpMouseToFocusMode, Xkb};
pub use crate::layer_rule::LayerRule;
pub use crate::layout::*;
pub use crate::misc::*;
pub use crate::output::{Output, OutputName, Outputs, Position, Vrr};
pub use crate::utils::FloatOrInt;
pub use crate::window_rule::{FloatingPosition, RelativeTo, WindowRule};
@@ -89,120 +90,6 @@ pub struct Config {
pub workspaces: Vec<Workspace>,
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct SpawnAtStartup {
#[knuffel(arguments)]
pub command: Vec<String>,
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct SpawnShAtStartup {
#[knuffel(argument)]
pub command: String,
}
#[derive(knuffel::Decode, Debug, PartialEq)]
pub struct Cursor {
#[knuffel(child, unwrap(argument), default = String::from("default"))]
pub xcursor_theme: String,
#[knuffel(child, unwrap(argument), default = 24)]
pub xcursor_size: u8,
#[knuffel(child)]
pub hide_when_typing: bool,
#[knuffel(child, unwrap(argument))]
pub hide_after_inactive_ms: Option<u32>,
}
impl Default for Cursor {
fn default() -> Self {
Self {
xcursor_theme: String::from("default"),
xcursor_size: 24,
hide_when_typing: false,
hide_after_inactive_ms: None,
}
}
}
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct HotkeyOverlay {
#[knuffel(child)]
pub skip_at_startup: bool,
#[knuffel(child)]
pub hide_not_bound: bool,
}
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct ConfigNotification {
#[knuffel(child)]
pub disable_failed: bool,
}
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Clipboard {
#[knuffel(child)]
pub disable_primary: bool,
}
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct Overview {
#[knuffel(child, unwrap(argument), default = Self::default().zoom)]
pub zoom: FloatOrInt<0, 1>,
#[knuffel(child, default = Self::default().backdrop_color)]
pub backdrop_color: Color,
#[knuffel(child, default)]
pub workspace_shadow: appearance::WorkspaceShadow,
}
impl Default for Overview {
fn default() -> Self {
Self {
zoom: FloatOrInt(0.5),
backdrop_color: appearance::DEFAULT_BACKDROP_COLOR,
workspace_shadow: appearance::WorkspaceShadow::default(),
}
}
}
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq, Eq)]
pub struct Environment(#[knuffel(children)] pub Vec<EnvironmentVariable>);
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct EnvironmentVariable {
#[knuffel(node_name)]
pub name: String,
#[knuffel(argument)]
pub value: Option<String>,
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct XwaylandSatellite {
#[knuffel(child)]
pub off: bool,
#[knuffel(child, unwrap(argument), default = Self::default().path)]
pub path: String,
}
impl Default for XwaylandSatellite {
fn default() -> Self {
Self {
off: false,
path: String::from("xwayland-satellite"),
}
}
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct Workspace {
#[knuffel(argument)]
pub name: WorkspaceName,
#[knuffel(child, unwrap(argument))]
pub open_on_output: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorkspaceName(pub String);
#[derive(Debug, Clone)]
pub enum ConfigPath {
/// Explicitly set config path.
@@ -345,57 +232,6 @@ impl Default for Config {
}
}
impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {
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<WorkspaceName, DecodeError<S>> {
#[derive(Debug)]
struct WorkspaceNameSet(Vec<String>);
match &**val {
knuffel::ast::Literal::String(ref s) => {
let mut name_set: Vec<String> = match ctx.get::<WorkspaceNameSet>() {
Some(h) => h.0.clone(),
None => Vec::new(),
};
if name_set.iter().any(|name| name.eq_ignore_ascii_case(s)) {
ctx.emit_error(DecodeError::unexpected(
val,
"named workspace",
format!("duplicate named workspace: {s}"),
));
return Ok(Self(String::new()));
}
name_set.push(s.to_string());
ctx.set(WorkspaceNameSet(name_set));
Ok(Self(s.clone().into()))
}
_ => {
ctx.emit_error(DecodeError::unsupported(
val,
"workspace names must be strings",
));
Ok(Self(String::new()))
}
}
}
}
#[cfg(test)]
mod tests {
use insta::assert_debug_snapshot;

169
niri-config/src/misc.rs Normal file
View File

@@ -0,0 +1,169 @@
use knuffel::errors::DecodeError;
use crate::appearance::{Color, WorkspaceShadow, DEFAULT_BACKDROP_COLOR};
use crate::FloatOrInt;
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct SpawnAtStartup {
#[knuffel(arguments)]
pub command: Vec<String>,
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct SpawnShAtStartup {
#[knuffel(argument)]
pub command: String,
}
#[derive(knuffel::Decode, Debug, PartialEq)]
pub struct Cursor {
#[knuffel(child, unwrap(argument), default = String::from("default"))]
pub xcursor_theme: String,
#[knuffel(child, unwrap(argument), default = 24)]
pub xcursor_size: u8,
#[knuffel(child)]
pub hide_when_typing: bool,
#[knuffel(child, unwrap(argument))]
pub hide_after_inactive_ms: Option<u32>,
}
impl Default for Cursor {
fn default() -> Self {
Self {
xcursor_theme: String::from("default"),
xcursor_size: 24,
hide_when_typing: false,
hide_after_inactive_ms: None,
}
}
}
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct HotkeyOverlay {
#[knuffel(child)]
pub skip_at_startup: bool,
#[knuffel(child)]
pub hide_not_bound: bool,
}
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct ConfigNotification {
#[knuffel(child)]
pub disable_failed: bool,
}
#[derive(knuffel::Decode, Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct Clipboard {
#[knuffel(child)]
pub disable_primary: bool,
}
#[derive(knuffel::Decode, Debug, Clone, Copy, PartialEq)]
pub struct Overview {
#[knuffel(child, unwrap(argument), default = Self::default().zoom)]
pub zoom: FloatOrInt<0, 1>,
#[knuffel(child, default = Self::default().backdrop_color)]
pub backdrop_color: Color,
#[knuffel(child, default)]
pub workspace_shadow: WorkspaceShadow,
}
impl Default for Overview {
fn default() -> Self {
Self {
zoom: FloatOrInt(0.5),
backdrop_color: DEFAULT_BACKDROP_COLOR,
workspace_shadow: WorkspaceShadow::default(),
}
}
}
#[derive(knuffel::Decode, Debug, Default, Clone, PartialEq, Eq)]
pub struct Environment(#[knuffel(children)] pub Vec<EnvironmentVariable>);
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct EnvironmentVariable {
#[knuffel(node_name)]
pub name: String,
#[knuffel(argument)]
pub value: Option<String>,
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct XwaylandSatellite {
#[knuffel(child)]
pub off: bool,
#[knuffel(child, unwrap(argument), default = Self::default().path)]
pub path: String,
}
impl Default for XwaylandSatellite {
fn default() -> Self {
Self {
off: false,
path: String::from("xwayland-satellite"),
}
}
}
#[derive(knuffel::Decode, Debug, Clone, PartialEq, Eq)]
pub struct Workspace {
#[knuffel(argument)]
pub name: WorkspaceName,
#[knuffel(child, unwrap(argument))]
pub open_on_output: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct WorkspaceName(pub String);
impl<S: knuffel::traits::ErrorSpan> knuffel::DecodeScalar<S> for WorkspaceName {
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<WorkspaceName, DecodeError<S>> {
#[derive(Debug)]
struct WorkspaceNameSet(Vec<String>);
match &**val {
knuffel::ast::Literal::String(ref s) => {
let mut name_set: Vec<String> = match ctx.get::<WorkspaceNameSet>() {
Some(h) => h.0.clone(),
None => Vec::new(),
};
if name_set.iter().any(|name| name.eq_ignore_ascii_case(s)) {
ctx.emit_error(DecodeError::unexpected(
val,
"named workspace",
format!("duplicate named workspace: {s}"),
));
return Ok(Self(String::new()));
}
name_set.push(s.to_string());
ctx.set(WorkspaceNameSet(name_set));
Ok(Self(s.clone().into()))
}
_ => {
ctx.emit_error(DecodeError::unsupported(
val,
"workspace names must be strings",
));
Ok(Self(String::new()))
}
}
}
}