From 0a928348b9fe2aef10ab8ca52a25a48bd8aad667 Mon Sep 17 00:00:00 2001 From: Vasilito Date: Thu, 7 May 2026 21:20:15 +0100 Subject: [PATCH] fix: handle nullable Cub AUR fields Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../system/cub/source/cub-lib/src/aur.rs | 102 ++++++++++++++++-- 1 file changed, 94 insertions(+), 8 deletions(-) diff --git a/local/recipes/system/cub/source/cub-lib/src/aur.rs b/local/recipes/system/cub/source/cub-lib/src/aur.rs index e61057164..6e032e873 100644 --- a/local/recipes/system/cub/source/cub-lib/src/aur.rs +++ b/local/recipes/system/cub/source/cub-lib/src/aur.rs @@ -11,28 +11,28 @@ pub struct AurPackage { #[serde(rename = "Version")] pub version: String, #[serde(rename = "Description")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_nullable_string")] pub description: String, #[serde(rename = "URL")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_nullable_string")] pub url: String, #[serde(rename = "License")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_aur_list")] pub license: Vec, #[serde(rename = "Depends")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_aur_list")] pub depends: Vec, #[serde(rename = "MakeDepends")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_aur_list")] pub makedepends: Vec, #[serde(rename = "OptDepends")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_aur_list")] pub optdepends: Vec, #[serde(rename = "Provides")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_aur_list")] pub provides: Vec, #[serde(rename = "Conflicts")] - #[serde(default)] + #[serde(default, deserialize_with = "deserialize_aur_list")] pub conflicts: Vec, #[serde(rename = "NumVotes")] pub num_votes: u64, @@ -45,6 +45,92 @@ pub struct AurPackage { pub out_of_date: Option, } +fn deserialize_nullable_string<'de, D>(deserializer: D) -> Result +where + D: serde::Deserializer<'de>, +{ + use serde::de::{self, Visitor}; + + struct NullableStringVisitor; + + impl<'de> Visitor<'de> for NullableStringVisitor { + type Value = String; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("null or string") + } + + fn visit_str(self, v: &str) -> Result { + Ok(v.to_string()) + } + + fn visit_string(self, v: String) -> Result { + Ok(v) + } + + fn visit_none(self) -> Result { + Ok(String::new()) + } + + fn visit_unit(self) -> Result { + Ok(String::new()) + } + } + + deserializer.deserialize_any(NullableStringVisitor) +} + +fn deserialize_aur_list<'de, D>(deserializer: D) -> Result, D::Error> +where + D: serde::Deserializer<'de>, +{ + use serde::de::{self, SeqAccess, Visitor}; + + struct AurListVisitor; + + impl<'de> Visitor<'de> for AurListVisitor { + type Value = Vec; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("null, string, or array of strings") + } + + fn visit_str(self, v: &str) -> Result { + if v.is_empty() { + Ok(Vec::new()) + } else { + Ok(vec![v.to_string()]) + } + } + + fn visit_string(self, v: String) -> Result { + if v.is_empty() { + Ok(Vec::new()) + } else { + Ok(vec![v]) + } + } + + fn visit_none(self) -> Result { + Ok(Vec::new()) + } + + fn visit_unit(self) -> Result { + Ok(Vec::new()) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let mut items = Vec::new(); + while let Some(item) = seq.next_element::()? { + items.push(item); + } + Ok(items) + } + } + + deserializer.deserialize_any(AurListVisitor) +} + fn deserialize_out_of_date<'de, D>(deserializer: D) -> Result, D::Error> where D: serde::Deserializer<'de>,