From 4c0cb3bc69b89cda984a2c10c1332a311359886f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Imobach=20Gonz=C3=A1lez=20Sosa?= Date: Tue, 5 Mar 2024 12:05:44 +0000 Subject: [PATCH] rust: handle invalid pattern selection reasons --- rust/agama-lib/src/software.rs | 2 +- rust/agama-lib/src/software/client.rs | 36 ++++++++++++++++++++------- rust/agama-server/src/software/web.rs | 28 +++++++++++++++++---- 3 files changed, 51 insertions(+), 15 deletions(-) diff --git a/rust/agama-lib/src/software.rs b/rust/agama-lib/src/software.rs index 43e2c1fd6..9231b0e5b 100644 --- a/rust/agama-lib/src/software.rs +++ b/rust/agama-lib/src/software.rs @@ -5,6 +5,6 @@ pub mod proxies; mod settings; mod store; -pub use client::{Pattern, SelectedBy, SoftwareClient}; +pub use client::{Pattern, SelectedBy, SoftwareClient, UnknownSelectedBy}; pub use settings::SoftwareSettings; pub use store::SoftwareStore; diff --git a/rust/agama-lib/src/software/client.rs b/rust/agama-lib/src/software/client.rs index 239c1a16a..476997684 100644 --- a/rust/agama-lib/src/software/client.rs +++ b/rust/agama-lib/src/software/client.rs @@ -22,7 +22,7 @@ pub struct Pattern { } /// Represents the reason why a pattern is selected. -#[derive(Clone, Copy, Debug, Serialize)] +#[derive(Clone, Copy, Debug, PartialEq, Serialize)] pub enum SelectedBy { /// The pattern was selected by the user. User = 0, @@ -32,12 +32,18 @@ pub enum SelectedBy { None = 2, } -impl From for SelectedBy { - fn from(value: u8) -> Self { +#[derive(Debug, thiserror::Error)] +#[error("Unknown selected by value: '{0}'")] +pub struct UnknownSelectedBy(u8); + +impl TryFrom for SelectedBy { + type Error = UnknownSelectedBy; + + fn try_from(value: u8) -> Result { match value { - 0 => Self::User, - 1 => Self::Auto, - _ => Self::None, + 0 => Ok(Self::User), + 1 => Ok(Self::Auto), + _ => Err(UnknownSelectedBy(value)), } } } @@ -83,8 +89,14 @@ impl<'a> SoftwareClient<'a> { .selected_patterns() .await? .into_iter() - .filter(|(_id, reason)| *reason == SelectedBy::User as u8) - .map(|(id, _reason)| id) + .filter_map(|(id, reason)| match SelectedBy::try_from(reason) { + Ok(reason) if reason == SelectedBy::User => Some(id), + Ok(_reason) => None, + Err(e) => { + log::warn!("Ignoring pattern {}. Error: {}", &id, e); + None + } + }) .collect(); Ok(patterns) } @@ -94,7 +106,13 @@ impl<'a> SoftwareClient<'a> { let patterns = self.software_proxy.selected_patterns().await?; let patterns = patterns .into_iter() - .map(|(id, reason)| (id, reason.into())) + .filter_map(|(id, reason)| match SelectedBy::try_from(reason) { + Ok(reason) => Some((id, reason)), + Err(e) => { + log::warn!("Ignoring pattern {}. Error: {}", &id, e); + None + } + }) .collect(); Ok(patterns) } diff --git a/rust/agama-server/src/software/web.rs b/rust/agama-server/src/software/web.rs index 8a9f1f309..d8575a333 100644 --- a/rust/agama-server/src/software/web.rs +++ b/rust/agama-server/src/software/web.rs @@ -11,7 +11,7 @@ use agama_lib::{ product::{Product, ProductClient}, software::{ proxies::{Software1Proxy, SoftwareProductProxy}, - Pattern, SelectedBy, SoftwareClient, + Pattern, SelectedBy, SoftwareClient, UnknownSelectedBy, }, }; use axum::{ @@ -90,16 +90,34 @@ async fn patterns_changed_stream( .await .then(|change| async move { if let Ok(patterns) = change.get().await { - let patterns: HashMap = - patterns.into_iter().map(|(k, v)| (k, v.into())).collect(); - return Some(Event::PatternsChanged(patterns)); + return match reason_to_selected_by(patterns) { + Ok(patterns) => Some(patterns), + Err(error) => { + log::warn!("Ignoring the list of changed patterns. Error: {}", error); + None + } + }; } None }) - .filter_map(|e| e); + .filter_map(|e| e.map(Event::PatternsChanged)); Ok(stream) } +// Returns a hash replacing the selection "reason" from D-Bus with a SelectedBy variant. +fn reason_to_selected_by( + patterns: HashMap, +) -> Result, UnknownSelectedBy> { + let mut selected: HashMap = HashMap::new(); + for (id, reason) in patterns { + match SelectedBy::try_from(reason) { + Ok(selected_by) => selected.insert(id, selected_by), + Err(e) => return Err(e), + }; + } + Ok(selected) +} + /// Sets up and returns the axum service for the software module. pub async fn software_service(dbus: zbus::Connection) -> Result { let product = ProductClient::new(dbus.clone()).await?;