From b08f2101deb7fe62eb3a8a329f7052b74f41ed2d Mon Sep 17 00:00:00 2001 From: "@permadeath.com" Date: Sat, 22 Aug 2026 15:27:47 -0400 Subject: [PATCH] refactor(unit-search)!: a ranked query says what it is sorted by `hardest_between` read like a filter on how hard a design is. It is `sort: {"damage_at": [3, 9]}` now - a sort with a named key, so the ones coming after it have somewhere to live and a query can only be ranked by one thing at a time. Change-Id: I66b46c65c86221ca5762532cf0d6184308df97df --- crates/helm-query/src/lib.rs | 28 ++++++++++++++++------ crates/helm-wasm/filters.json | 44 +++++++++++++++++++++-------------- 2 files changed, 47 insertions(+), 25 deletions(-) diff --git a/crates/helm-query/src/lib.rs b/crates/helm-query/src/lib.rs index 0239318..05f3336 100644 --- a/crates/helm-query/src/lib.rs +++ b/crates/helm-query/src/lib.rs @@ -149,14 +149,28 @@ pub struct Filter { /// Equipment classes the unit must carry none of. pub lacks_class: Option>, - /// Rank what was selected by what it does between two hexes, hardest - /// first. `[3, 9]` is "what hits hardest at brawling range". + /// What to rank the selection by. Unranked, it comes back in library + /// order. + pub sort: Option, +} + +/// What a selection is ranked by. +/// +/// Its own type rather than a field per sort, because a query ranks by one +/// thing: two sort fields set at once would need a rule about which wins, and +/// there is no good answer to that question. +#[derive(Debug, Clone, PartialEq, Eq, Deserialize)] +#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))] +#[serde(rename_all = "snake_case", deny_unknown_fields)] +pub enum Sort { + /// Hardest first, by what each design does across a band of hexes: + /// `{"damage_at": [3, 9]}` is "what hits hardest at brawling range". /// /// A band rather than a hex, because nobody wants to know what a design /// does at exactly six. A sort rather than a filter, because the useful /// question is which of these is hardest here, not which of them clear /// some bar - and the bar would be different for every band. - pub hardest_between: Option>, + DamageAt(Vec), } /// What a filter named that the library does not have. @@ -382,7 +396,7 @@ impl Filter { if some(&self.has_quirks) { needs.push(Chunk::Quirks); } - if self.hardest_between.is_some() { + if matches!(self.sort, Some(Sort::DamageAt(_))) { needs.push(Chunk::Curve); } if some(&self.tech_base) @@ -534,11 +548,11 @@ impl Filter { /// /// `None` when it ranks by nothing, which is most filters. pub fn band(&self) -> Result, Invalid> { - let Some(band) = &self.hardest_between else { + let Some(Sort::DamageAt(band)) = &self.sort else { return Ok(None); }; let bad = |got: String| Invalid::Unknown { - field: "hardest_between", + field: "damage_at", got, valid: vec![format!("two hexes from 1 to {}", helm_core::REACH)], }; @@ -680,7 +694,7 @@ mod tests { year_max: Some(3025), bv_max: Some(2000), firepower_min: Some(10), - hardest_between: Some(vec![3, 9]), + sort: Some(Sort::DamageAt(vec![3, 9])), ..Default::default() }; let mut asked: Vec<&str> = everything.needs().iter().map(|c| c.name()).collect(); diff --git a/crates/helm-wasm/filters.json b/crates/helm-wasm/filters.json index 9657cc8..fb7f9e9 100644 --- a/crates/helm-wasm/filters.json +++ b/crates/helm-wasm/filters.json @@ -123,24 +123,30 @@ "bv_min": 99999 }, { - "hardest_between": [ - 1, - 3 - ], - "tons_max": 55 + "tons_max": 55, + "sort": { + "damage_at": [ + 1, + 3 + ] + } }, { - "hardest_between": [ - 3, - 9 - ], - "bv_max": 1600 + "bv_max": 1600, + "sort": { + "damage_at": [ + 3, + 9 + ] + } }, { - "hardest_between": [ - 15, - 21 - ] + "sort": { + "damage_at": [ + 15, + 21 + ] + } }, { "weapon": [ @@ -163,9 +169,11 @@ "equipment": [ "ecm" ], - "hardest_between": [ - 3, - 9 - ] + "sort": { + "damage_at": [ + 3, + 9 + ] + } } ] -- 2.54.0