mirror of
https://github.com/LGUG2Z/komorebi.git
synced 2026-11-13 12:23:38 +01:00
fix(config): accept an integer for an initial window placement rule
PlacementTarget deserialized through String, so the integer the docs
promise ("initial_window_placement_rules": 2) made the daemon exit at
start with an untagged enum error while "2" worked. Accept integers as
well as strings.
The derived schema described an externally tagged object that neither
form matches, so describe the type by hand and regenerate both schemas.
This commit is contained in:
1 parent
837ca257d4
commit
3b424dd707
3 files changed
+124
-53
No files matched your search
@@ -541,7 +541,6 @@ pub enum WindowPlacement {
|
||||
///
|
||||
/// NOTE: Integer indices are 1-based in the config for user-friendliness.
|
||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
|
||||
pub enum PlacementTarget {
|
||||
/// A named placement strategy
|
||||
Placement(WindowPlacement),
|
||||
@@ -582,6 +581,39 @@ impl PartialOrd for PlacementTarget {
|
||||
}
|
||||
}
|
||||
|
||||
// Written by hand because the (de)serialization is: a `WindowPlacement` name or a container index,
|
||||
// as a string or as an integer, never the externally tagged object a derive would describe
|
||||
#[cfg(feature = "schemars")]
|
||||
impl schemars::JsonSchema for PlacementTarget {
|
||||
fn schema_name() -> std::borrow::Cow<'static, str> {
|
||||
"PlacementTarget".into()
|
||||
}
|
||||
|
||||
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
|
||||
schemars::json_schema!({
|
||||
"description": "A target position for window placement, used as a key in `InitialWindowPlacementRules::Rules`.
|
||||
|
||||
Can be either a `WindowPlacement` variant name (e.g. `\"Primary\"`, `\"Last\"`)
|
||||
or a 1-based container index (e.g. `\"1\"`, `\"3\"`, or the integer `3`).
|
||||
|
||||
NOTE: Integer indices are 1-based in the config for user-friendliness.",
|
||||
"anyOf": [
|
||||
generator.subschema_for::<WindowPlacement>(),
|
||||
{
|
||||
"description": "A 1-based container index as a string",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$"
|
||||
},
|
||||
{
|
||||
"description": "A 1-based container index",
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
]
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for PlacementTarget {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.to_string().cmp(&other.to_string())
|
||||
@@ -596,8 +628,33 @@ impl Serialize for PlacementTarget {
|
||||
|
||||
impl<'de> Deserialize<'de> for PlacementTarget {
|
||||
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
|
||||
let s = String::deserialize(deserializer)?;
|
||||
s.parse().map_err(serde::de::Error::custom)
|
||||
struct PlacementTargetVisitor;
|
||||
|
||||
impl serde::de::Visitor<'_> for PlacementTargetVisitor {
|
||||
type Value = PlacementTarget;
|
||||
|
||||
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.write_str("a WindowPlacement name or a non-negative container index")
|
||||
}
|
||||
|
||||
fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
|
||||
s.parse().map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_u64<E: serde::de::Error>(self, n: u64) -> Result<Self::Value, E> {
|
||||
usize::try_from(n)
|
||||
.map(PlacementTarget::Index)
|
||||
.map_err(E::custom)
|
||||
}
|
||||
|
||||
fn visit_i64<E: serde::de::Error>(self, n: i64) -> Result<Self::Value, E> {
|
||||
usize::try_from(n)
|
||||
.map(PlacementTarget::Index)
|
||||
.map_err(E::custom)
|
||||
}
|
||||
}
|
||||
|
||||
deserializer.deserialize_any(PlacementTargetVisitor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,4 +836,32 @@ mod tests {
|
||||
|
||||
assert_eq!(path, PathBuf::from("/path/VALUE/d"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn placement_target_accepts_a_number_or_a_string() {
|
||||
let parse = |json: &str| serde_json::from_str::<InitialWindowPlacementRules>(json);
|
||||
|
||||
let index = InitialWindowPlacementRules::Target(PlacementTarget::Index(2));
|
||||
assert_eq!(parse("2").unwrap(), index);
|
||||
assert_eq!(parse(r#""2""#).unwrap(), index);
|
||||
assert_eq!(
|
||||
parse(r#""Primary""#).unwrap(),
|
||||
InitialWindowPlacementRules::Target(PlacementTarget::Placement(
|
||||
WindowPlacement::Primary
|
||||
))
|
||||
);
|
||||
|
||||
assert!(parse("-1").is_err());
|
||||
assert!(parse("1.5").is_err());
|
||||
assert!(parse("true").is_err());
|
||||
|
||||
// the keys of the map form are always strings
|
||||
let Ok(InitialWindowPlacementRules::Rules(rules)) =
|
||||
parse(r#"{"2": {"kind": "Exe", "id": "a.exe"}, "Primary": []}"#)
|
||||
else {
|
||||
panic!("expected the map form");
|
||||
};
|
||||
assert!(rules.contains_key(&PlacementTarget::Index(2)));
|
||||
assert!(rules.contains_key(&PlacementTarget::Placement(WindowPlacement::Primary)));
|
||||
}
|
||||
}
|
||||
+18
-25
@@ -3173,6 +3173,14 @@
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/PlacementMatchingRules"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[0-9]+$": {
|
||||
"$ref": "#/$defs/PlacementMatchingRules"
|
||||
},
|
||||
"^\\d+$": {
|
||||
"$ref": "#/$defs/PlacementMatchingRules"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -4974,35 +4982,20 @@
|
||||
]
|
||||
},
|
||||
"PlacementTarget": {
|
||||
"description": "A target position for window placement, used as a key in `InitialWindowPlacementRules::Rules`.\n\nCan be either a `WindowPlacement` variant name (e.g. `\"Primary\"`, `\"Last\"`)\nor a 1-based container index (e.g. `\"1\"`, `\"3\"`).\n\nNOTE: Integer indices are 1-based in the config for user-friendliness.",
|
||||
"oneOf": [
|
||||
"description": "A target position for window placement, used as a key in `InitialWindowPlacementRules::Rules`.\n\nCan be either a `WindowPlacement` variant name (e.g. `\"Primary\"`, `\"Last\"`)\nor a 1-based container index (e.g. `\"1\"`, `\"3\"`, or the integer `3`).\n\nNOTE: Integer indices are 1-based in the config for user-friendliness.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "A named placement strategy",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Placement": {
|
||||
"$ref": "#/$defs/WindowPlacement"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"Placement"
|
||||
]
|
||||
"$ref": "#/$defs/WindowPlacement"
|
||||
},
|
||||
{
|
||||
"description": "A 1-based container index as a string",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$"
|
||||
},
|
||||
{
|
||||
"description": "A 1-based container index",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Index": {
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"Index"
|
||||
]
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
+18
-25
@@ -2883,6 +2883,14 @@
|
||||
"type": "object",
|
||||
"additionalProperties": {
|
||||
"$ref": "#/$defs/PlacementMatchingRules"
|
||||
},
|
||||
"patternProperties": {
|
||||
"^[0-9]+$": {
|
||||
"$ref": "#/$defs/PlacementMatchingRules"
|
||||
},
|
||||
"^\\d+$": {
|
||||
"$ref": "#/$defs/PlacementMatchingRules"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
@@ -3748,35 +3756,20 @@
|
||||
]
|
||||
},
|
||||
"PlacementTarget": {
|
||||
"description": "A target position for window placement, used as a key in `InitialWindowPlacementRules::Rules`.\n\nCan be either a `WindowPlacement` variant name (e.g. `\"Primary\"`, `\"Last\"`)\nor a 1-based container index (e.g. `\"1\"`, `\"3\"`).\n\nNOTE: Integer indices are 1-based in the config for user-friendliness.",
|
||||
"oneOf": [
|
||||
"description": "A target position for window placement, used as a key in `InitialWindowPlacementRules::Rules`.\n\nCan be either a `WindowPlacement` variant name (e.g. `\"Primary\"`, `\"Last\"`)\nor a 1-based container index (e.g. `\"1\"`, `\"3\"`, or the integer `3`).\n\nNOTE: Integer indices are 1-based in the config for user-friendliness.",
|
||||
"anyOf": [
|
||||
{
|
||||
"description": "A named placement strategy",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Placement": {
|
||||
"$ref": "#/$defs/WindowPlacement"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"Placement"
|
||||
]
|
||||
"$ref": "#/$defs/WindowPlacement"
|
||||
},
|
||||
{
|
||||
"description": "A 1-based container index as a string",
|
||||
"type": "string",
|
||||
"pattern": "^[0-9]+$"
|
||||
},
|
||||
{
|
||||
"description": "A 1-based container index",
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"Index": {
|
||||
"type": "integer",
|
||||
"format": "uint",
|
||||
"minimum": 0
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"Index"
|
||||
]
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user