From c3acb5af5a96a3e1c1a924a4286955fd46f074c9 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Fri, 2 Oct 2026 23:54:27 +0100 Subject: [PATCH] Move shell configuration out of desktop core --- Cargo.lock | 2 - compositor/src/config.rs | 4 +- .../src/desktop.rs | 0 crates/ferese-config/src/lib.rs | 2 + .../src/notifications.rs | 0 crates/ferese-core/Cargo.toml | 1 - crates/ferese-core/src/lib.rs | 2 - scripts/tests/test_component_boundaries.py | 41 +++++++++++++++++++ shell/ferese-shell/Cargo.toml | 1 - shell/ferese-shell/src/config.rs | 10 ++--- shell/ferese-shell/src/desktop_widgets.rs | 26 ++++++------ shell/ferese-shell/src/main.rs | 6 +-- shell/ferese-shell/src/note_store.rs | 2 +- shell/ferese-shell/src/notifications.rs | 12 +++--- 14 files changed, 73 insertions(+), 36 deletions(-) rename crates/{ferese-core => ferese-config}/src/desktop.rs (100%) rename crates/{ferese-core => ferese-config}/src/notifications.rs (100%) create mode 100644 scripts/tests/test_component_boundaries.py diff --git a/Cargo.lock b/Cargo.lock index f9ace09..6ebdef0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1693,7 +1693,6 @@ name = "ferese-core" version = "0.1.0" dependencies = [ "ferese-layout", - "jiff", "serde", ] @@ -1781,7 +1780,6 @@ name = "ferese-shell" version = "0.1.0" dependencies = [ "ferese-config", - "ferese-core", "ferese-ipc", "ferese-protocols", "ferese-theme", diff --git a/compositor/src/config.rs b/compositor/src/config.rs index d20e02b..3d0570b 100644 --- a/compositor/src/config.rs +++ b/compositor/src/config.rs @@ -30,11 +30,11 @@ use crate::window_rules::{WindowRule, WindowRuleConfig}; #[derive(Debug, Default, Deserialize)] pub struct Config { #[serde(default)] - notifications: ferese_core::notifications::NotificationConfig, + notifications: ferese_config::notifications::NotificationConfig, #[serde(default)] lock_screen: crate::session_lock::IdleSettings, #[serde(default)] - desktop_widgets: ferese_core::desktop::DesktopWidgets, + desktop_widgets: ferese_config::desktop::DesktopWidgets, #[serde(default)] pub(crate) autostart: Vec, #[serde(default)] diff --git a/crates/ferese-core/src/desktop.rs b/crates/ferese-config/src/desktop.rs similarity index 100% rename from crates/ferese-core/src/desktop.rs rename to crates/ferese-config/src/desktop.rs diff --git a/crates/ferese-config/src/lib.rs b/crates/ferese-config/src/lib.rs index d025917..b2af2a7 100644 --- a/crates/ferese-config/src/lib.rs +++ b/crates/ferese-config/src/lib.rs @@ -1,4 +1,6 @@ +pub mod desktop; pub mod families; +pub mod notifications; pub mod presets; pub mod theme; diff --git a/crates/ferese-core/src/notifications.rs b/crates/ferese-config/src/notifications.rs similarity index 100% rename from crates/ferese-core/src/notifications.rs rename to crates/ferese-config/src/notifications.rs diff --git a/crates/ferese-core/Cargo.toml b/crates/ferese-core/Cargo.toml index 84fcaed..fe86c89 100644 --- a/crates/ferese-core/Cargo.toml +++ b/crates/ferese-core/Cargo.toml @@ -8,4 +8,3 @@ rust-version.workspace = true [dependencies] ferese-layout = { path = "../ferese-layout" } serde = { workspace = true, features = ["derive"] } -jiff.workspace = true diff --git a/crates/ferese-core/src/lib.rs b/crates/ferese-core/src/lib.rs index f82b0f8..a13c6cc 100644 --- a/crates/ferese-core/src/lib.rs +++ b/crates/ferese-core/src/lib.rs @@ -1,6 +1,4 @@ -pub mod desktop; mod focus; -pub mod notifications; mod output; mod workspace; mod workspace_policy; diff --git a/scripts/tests/test_component_boundaries.py b/scripts/tests/test_component_boundaries.py new file mode 100644 index 0000000..9f31f00 --- /dev/null +++ b/scripts/tests/test_component_boundaries.py @@ -0,0 +1,41 @@ +"""Keep production dependencies within the boundaries in ADR 0004.""" +import pathlib +import tomllib +import unittest + +ROOT = pathlib.Path(__file__).resolve().parents[2] + + +def dependency_graph(): + workspace = tomllib.loads((ROOT / "Cargo.toml").read_text()) + graph = {} + for member in workspace["workspace"]["members"]: + manifest = tomllib.loads((ROOT / member / "Cargo.toml").read_text()) + dependencies = set(manifest.get("dependencies", {})) + for target in manifest.get("target", {}).values(): + dependencies.update(target.get("dependencies", {})) + graph[manifest["package"]["name"]] = dependencies + return graph + + +def reachable(graph, package): + seen = set() + pending = list(graph.get(package, ())) + while pending: + dependency = pending.pop() + if dependency not in seen: + seen.add(dependency) + pending.extend(graph.get(dependency, ())) + return seen + + +class ComponentBoundaries(unittest.TestCase): + def test_shell_does_not_depend_on_core(self): + self.assertNotIn("ferese-core", reachable(dependency_graph(), "ferese-shell")) + + def test_transitive_dependencies_are_checked(self): + self.assertIn("core", reachable({"shell": {"helper"}, "helper": {"core"}}, "shell")) + + +if __name__ == "__main__": + unittest.main() diff --git a/shell/ferese-shell/Cargo.toml b/shell/ferese-shell/Cargo.toml index 0be6db2..b0465dd 100644 --- a/shell/ferese-shell/Cargo.toml +++ b/shell/ferese-shell/Cargo.toml @@ -9,7 +9,6 @@ rust-version.workspace = true ferese-ipc = { path = "../../crates/ferese-ipc" } ferese-theme = { path = "../../crates/ferese-theme" } ferese-config = { path = "../../crates/ferese-config" } -ferese-core = { path = "../../crates/ferese-core" } ferese-protocols = { path = "../../crates/ferese-protocols", default-features = false, features = ["client"] } jiff.workspace = true libc.workspace = true diff --git a/shell/ferese-shell/src/config.rs b/shell/ferese-shell/src/config.rs index 995729c..de22067 100644 --- a/shell/ferese-shell/src/config.rs +++ b/shell/ferese-shell/src/config.rs @@ -7,8 +7,8 @@ const DEFAULT_BACKGROUND: [u8; 3] = [11, 15, 20]; #[derive(Clone, Debug, Default)] pub(crate) struct ShellConfig { - pub(crate) notifications: ferese_core::notifications::NotificationConfig, - pub(crate) desktop_widgets: ferese_core::desktop::DesktopWidgets, + pub(crate) notifications: ferese_config::notifications::NotificationConfig, + pub(crate) desktop_widgets: ferese_config::desktop::DesktopWidgets, pub(crate) animations: crate::motion::Settings, pub(crate) font_family: Option, pub(crate) wallpaper: WallpaperConfig, @@ -157,9 +157,9 @@ pub(crate) enum WallpaperMode { #[derive(Debug, Default, Deserialize)] struct FereseConfig { #[serde(default)] - notifications: ferese_core::notifications::NotificationConfig, + notifications: ferese_config::notifications::NotificationConfig, #[serde(default)] - desktop_widgets: ferese_core::desktop::DesktopWidgets, + desktop_widgets: ferese_config::desktop::DesktopWidgets, #[serde(default)] animations: crate::motion::Settings, #[serde(default)] @@ -595,7 +595,7 @@ mod tests { .desktop_widgets .clock; assert!(clock.enabled); - assert_eq!(clock.anchor, ferese_core::desktop::Anchor::BottomRight); + assert_eq!(clock.anchor, ferese_config::desktop::Anchor::BottomRight); assert!(parse_test_source("desktop-widgets {\n clock {\n opacity 1.1\n }\n}\n").is_err()); assert!(parse_test_source("desktop-widgets {\n clock {\n time-format \"%\"\n }\n}\n").is_err()); } diff --git a/shell/ferese-shell/src/desktop_widgets.rs b/shell/ferese-shell/src/desktop_widgets.rs index f5aa76b..a71e599 100644 --- a/shell/ferese-shell/src/desktop_widgets.rs +++ b/shell/ferese-shell/src/desktop_widgets.rs @@ -172,7 +172,7 @@ impl FereseShell { if save { if drag.id.is_none() { let clock = &mut self.config.desktop_widgets.clock; - clock.anchor = ferese_core::desktop::Anchor::TopLeft; + clock.anchor = ferese_config::desktop::Anchor::TopLeft; clock.margin_x = drag.position.x.round() as i32; clock.margin_y = drag.position.y.round() as i32; for entry in &self.outputs { @@ -192,7 +192,7 @@ impl FereseShell { .iter_mut() .find(|note| Some(¬e.id) == drag.id.as_ref()) { - note.anchor = ferese_core::desktop::Anchor::TopLeft; + note.anchor = ferese_config::desktop::Anchor::TopLeft; note.margin_x = drag.position.x.round() as i32; note.margin_y = drag.position.y.round() as i32; @@ -285,9 +285,9 @@ impl FereseShell { return text("").into(); }; let alignment = match note.alignment { - ferese_core::desktop::Alignment::Left => alignment::Horizontal::Left, - ferese_core::desktop::Alignment::Center => alignment::Horizontal::Center, - ferese_core::desktop::Alignment::Right => alignment::Horizontal::Right, + ferese_config::desktop::Alignment::Left => alignment::Horizontal::Left, + ferese_config::desktop::Alignment::Center => alignment::Horizontal::Center, + ferese_config::desktop::Alignment::Right => alignment::Horizontal::Right, }; let font = configured_font( note.font_family @@ -477,14 +477,14 @@ impl FereseShell { } pub(super) fn view_clock_content(&self) -> Element<'_, cosmic::Action> { - use ferese_core::desktop::Alignment as ClockAlignment; + use ferese_config::desktop::Alignment as ClockAlignment; let clock = &self.config.desktop_widgets.clock; let alignment = match clock.alignment { ClockAlignment::Left => alignment::Horizontal::Left, ClockAlignment::Center => alignment::Horizontal::Center, ClockAlignment::Right => alignment::Horizontal::Right, }; - let pixel = clock.style == ferese_core::desktop::ClockStyle::Pixel; + let pixel = clock.style == ferese_config::desktop::ClockStyle::Pixel; let custom_font = clock.font_family.as_deref().filter(|family| !family.trim().is_empty()); let mut font = configured_font(if pixel { custom_font.or(Some("Cantarell")) @@ -668,7 +668,7 @@ pub(super) fn clamp_note_position( ) } -pub(super) fn note_origin(note: &ferese_core::desktop::StickyNote, output: (i32, i32)) -> cosmic::iced::Point { +pub(super) fn note_origin(note: &ferese_config::desktop::StickyNote, output: (i32, i32)) -> cosmic::iced::Point { widget_origin( note.anchor, note.margin_x, @@ -679,13 +679,13 @@ pub(super) fn note_origin(note: &ferese_core::desktop::StickyNote, output: (i32, } pub(super) fn widget_origin( - anchor: ferese_core::desktop::Anchor, + anchor: ferese_config::desktop::Anchor, margin_x: i32, margin_y: i32, size: (u32, u32), output: (i32, i32), ) -> cosmic::iced::Point { - use ferese_core::desktop::Anchor as A; + use ferese_config::desktop::Anchor as A; let x = match anchor { A::TopLeft | A::CenterLeft | A::BottomLeft => margin_x as f32, A::TopRight | A::CenterRight | A::BottomRight => output.0 as f32 - size.0 as f32 - margin_x as f32, @@ -699,16 +699,16 @@ pub(super) fn widget_origin( clamp_note_position(cosmic::iced::Point::new(x, y), output, size) } -pub(super) fn clock_placement(clock: &ferese_core::desktop::Clock) -> (Anchor, IcedMargin) { +pub(super) fn clock_placement(clock: &ferese_config::desktop::Clock) -> (Anchor, IcedMargin) { widget_placement(clock.anchor, clock.margin_x, clock.margin_y) } pub(super) fn widget_placement( - position: ferese_core::desktop::Anchor, + position: ferese_config::desktop::Anchor, margin_x: i32, margin_y: i32, ) -> (Anchor, IcedMargin) { - use ferese_core::desktop::Anchor as Position; + use ferese_config::desktop::Anchor as Position; let horizontal = match position { Position::TopLeft | Position::CenterLeft | Position::BottomLeft => Anchor::LEFT, Position::TopRight | Position::CenterRight | Position::BottomRight => Anchor::RIGHT, diff --git a/shell/ferese-shell/src/main.rs b/shell/ferese-shell/src/main.rs index 2f13d98..b114bff 100644 --- a/shell/ferese-shell/src/main.rs +++ b/shell/ferese-shell/src/main.rs @@ -1047,9 +1047,9 @@ mod tests { #[test] fn note_drag_origin_and_bounds_use_logical_output_coordinates() { use cosmic::iced::Point; - let mut note = ferese_core::desktop::StickyNote::default(); + let mut note = ferese_config::desktop::StickyNote::default(); assert_eq!(super::note_origin(¬e, (1920, 1080)), Point::new(1552., 80.)); - note.anchor = ferese_core::desktop::Anchor::Center; + note.anchor = ferese_config::desktop::Anchor::Center; assert_eq!(super::note_origin(¬e, (1920, 1080)), Point::new(800., 420.)); assert_eq!( super::clamp_note_position(Point::new(-20., 1200.), (1920, 1080), (320, 240)), @@ -1063,7 +1063,7 @@ mod tests { #[test] fn desktop_clock_anchor_margins_match_only_anchored_edges() { - use ferese_core::desktop::{Anchor as Position, Clock}; + use ferese_config::desktop::{Anchor as Position, Clock}; let clock = Clock { anchor: Position::BottomRight, margin_x: 30, diff --git a/shell/ferese-shell/src/note_store.rs b/shell/ferese-shell/src/note_store.rs index 041a062..a5edb4c 100644 --- a/shell/ferese-shell/src/note_store.rs +++ b/shell/ferese-shell/src/note_store.rs @@ -133,6 +133,6 @@ desktop-widgets { let notes = crate::config::parse_source(&source).unwrap().desktop_widgets.notes; assert_eq!(notes[0].text, "one\ntwo"); assert_eq!((notes[0].margin_x, notes[0].margin_y), (50, 60)); - assert_eq!(notes[0].anchor, ferese_core::desktop::Anchor::TopLeft); + assert_eq!(notes[0].anchor, ferese_config::desktop::Anchor::TopLeft); } } diff --git a/shell/ferese-shell/src/notifications.rs b/shell/ferese-shell/src/notifications.rs index 774fc54..a88598f 100644 --- a/shell/ferese-shell/src/notifications.rs +++ b/shell/ferese-shell/src/notifications.rs @@ -192,11 +192,11 @@ pub struct Service { } impl Service { - fn start(config: &ferese_core::notifications::NotificationConfig) -> Self { + fn start(config: &ferese_config::notifications::NotificationConfig) -> Self { Self::connect(config, None) } - fn connect(config: &ferese_core::notifications::NotificationConfig, address: Option) -> Self { + fn connect(config: &ferese_config::notifications::NotificationConfig, address: Option) -> Self { let timeout = Arc::new(std::sync::atomic::AtomicU32::new(config.timeout_ms)); let worker_timeout = timeout.clone(); let (sender, events) = tokio::sync::mpsc::channel(256); @@ -269,7 +269,7 @@ struct Toast { } pub struct Center { - config: ferese_core::notifications::NotificationConfig, + config: ferese_config::notifications::NotificationConfig, service: Service, pub ready: bool, pub dnd: bool, @@ -282,7 +282,7 @@ pub struct Center { } impl Center { - pub fn new(config: ferese_core::notifications::NotificationConfig) -> Self { + pub fn new(config: ferese_config::notifications::NotificationConfig) -> Self { Self { service: Service::start(&config), ready: false, @@ -304,7 +304,7 @@ impl Center { .try_send(Command::Local(title.into(), body.into())); } - pub fn configure(&mut self, config: ferese_core::notifications::NotificationConfig) { + pub fn configure(&mut self, config: ferese_config::notifications::NotificationConfig) { if self.config.do_not_disturb != config.do_not_disturb { self.dnd = config.do_not_disturb; } @@ -617,7 +617,7 @@ impl Center { #[cfg(test)] mod tests { - use ferese_core::notifications::NotificationConfig; + use ferese_config::notifications::NotificationConfig; use super::*;