diff --git a/crates/komorebi-config/src/lib.rs b/crates/komorebi-config/src/lib.rs index ba126d86..a40e456b 100644 --- a/crates/komorebi-config/src/lib.rs +++ b/crates/komorebi-config/src/lib.rs @@ -19,4 +19,6 @@ pub mod changes; pub mod defaults; pub mod matching; pub mod placement; +#[cfg(feature = "schemars")] +pub mod platforms; pub mod static_config; diff --git a/crates/komorebi-config/src/platforms.rs b/crates/komorebi-config/src/platforms.rs new file mode 100644 index 00000000..f56e0acb --- /dev/null +++ b/crates/komorebi-config/src/platforms.rs @@ -0,0 +1,48 @@ +//! Which platform reads which key of `komorebi.json`. +//! +//! The table is not written a second time: it is read from the `x-komorebi-platforms` markers +//! of the one `StaticConfig`, so a key cannot be added without saying who reads it. + +use komorebi_protocol::platforms::Platform; +use serde_json::Value; + +use crate::schema::PLATFORMS; +use crate::schema::platform_name; +use crate::schema::union; +use crate::static_config::StaticConfig; + +/// Whether `platform` reads the top-level `key` of a configuration. A key the type does not +/// know is read by nobody (it is ignored everywhere). +#[must_use] +pub fn reads(platform: Platform, key: &str) -> bool { + let schema = union::(); + match schema["properties"].get(key) { + None => false, + Some(property) => match property.get(PLATFORMS) { + Some(Value::Array(platforms)) => platforms + .iter() + .any(|p| p.as_str() == Some(platform_name(platform))), + _ => true, + }, + } +} + +/// The top-level keys of the configuration text `raw` that are keys of `komorebi.json` but that +/// `platform` does not read, in file order: what a daemon says at load time instead of silently +/// dropping them. Keys that are not known at all are not reported. +/// +/// # Errors +/// +/// The text is not JSON. +pub fn unsupported_keys(raw: &str, platform: Platform) -> color_eyre::Result> { + let value: Value = serde_json::from_str(raw)?; + let schema = union::(); + let known = schema["properties"].as_object(); + Ok(value + .as_object() + .into_iter() + .flat_map(|object| object.keys()) + .filter(|key| known.is_some_and(|k| k.contains_key(*key)) && !reads(platform, key)) + .cloned() + .collect()) +} diff --git a/crates/komorebi-config/tests/schemas.rs b/crates/komorebi-config/tests/schemas.rs index 87c48471..1d4ac3a6 100644 --- a/crates/komorebi-config/tests/schemas.rs +++ b/crates/komorebi-config/tests/schemas.rs @@ -251,3 +251,55 @@ fn the_schema_of_each_platform_is_a_view_of_the_one_static_config() { ); } } + +#[test] +fn the_platform_table_comes_from_the_markers() { + use komorebi_config::platforms::reads; + use komorebi_config::platforms::unsupported_keys; + use komorebi_protocol::platforms::Platform; + + let windows_only = ["stackbar", "border_style", "slow_application_identifiers"]; + let kde_macos_only = [ + "border_radius", + "titleless_applications", + "tabbed_applications", + ]; + let windows_kde = ["transparency", "focus_follows_mouse"]; + + for key in windows_only { + assert!(reads(Platform::Windows, key)); + assert!( + !reads(Platform::Kde, key) && !reads(Platform::MacOs, key), + "{key}" + ); + } + for key in kde_macos_only { + assert!(!reads(Platform::Windows, key), "{key}"); + assert!(reads(Platform::Kde, key) && reads(Platform::MacOs, key)); + } + for key in windows_kde { + assert!(reads(Platform::Windows, key) && reads(Platform::Kde, key)); + assert!(!reads(Platform::MacOs, key), "{key}"); + } + assert!(reads(Platform::MacOs, "monitors") && !reads(Platform::MacOs, "no_such_key")); + + // the keys each tree's schema.json holds are the keys that platform reads + for (tree, platform) in [ + (&TREES[0], Platform::Windows), + (&TREES[1], Platform::Kde), + (&TREES[2], Platform::MacOs), + ] { + let schema: Value = serde_json::from_str(&checked_in(tree, "schema.json")).unwrap(); + for key in schema["properties"].as_object().unwrap().keys() { + assert!(reads(platform, key), "{} reads {key}", tree.name); + } + } + + let raw = r#"{ "stackbar": {}, "border": true, "a_key_nobody_knows": 1, "border_radius": 5 }"#; + assert_eq!(unsupported_keys(raw, Platform::Kde).unwrap(), ["stackbar"]); + assert_eq!( + unsupported_keys(raw, Platform::Windows).unwrap(), + ["border_radius"] + ); + assert!(unsupported_keys("{ ", Platform::Kde).is_err()); +}