From 0f695b55a95f2ce6afa96172fbc10345c8baf28e Mon Sep 17 00:00:00 2001 From: obrusvit Date: Sun, 25 May 2025 19:18:29 +0200 Subject: [PATCH] refactor(core): fn plural_form will never panic - also minor adjustment where the function is used in Eckahrt --- core/embed/rust/src/strutil.rs | 30 +++++++++++-------- .../firmware/value_input_screen.rs | 8 ++--- 2 files changed, 21 insertions(+), 17 deletions(-) diff --git a/core/embed/rust/src/strutil.rs b/core/embed/rust/src/strutil.rs index d4854836ff..0ca2e1a265 100644 --- a/core/embed/rust/src/strutil.rs +++ b/core/embed/rust/src/strutil.rs @@ -82,20 +82,22 @@ pub fn format_pairing_code(code: u32, width: usize) -> ShortString { /// * `count` - The numeric count to select the correct plural form. /// /// # Returns -/// A `ShortString` containing the correct plural form. -/// -/// # Panics -/// Panics if: -/// - The `template` has fewer than two forms. -/// - Conversion to `ShortString` fails (via `unwrap!`). +/// A `ShortString` containing the correct plural form, or an empty +/// `ShortString` if the template is malformed or conversion fails. pub fn plural_form(template: &str, count: u32) -> ShortString { + // If no separator found, return the whole template (fallback behavior) + if !template.contains('|') { + return ShortString::try_from(template).unwrap_or_default(); + } + // Split the template by '|' into components let mut parts = template.split('|'); - // First form (singular), must exist - let first = unwrap!(parts.next().ok_or(())); - // Second form (plural or few), must exist - let second = unwrap!(parts.next().ok_or(())); + // Get the required first two parts + let (first, second) = match (parts.next(), parts.next()) { + (Some(first), Some(second)) => (first, second), + _ => return ShortString::default(), + }; // Third form (many), optional let third = parts.next(); @@ -121,8 +123,8 @@ pub fn plural_form(template: &str, count: u32) -> ShortString { } }; - // Convert the selected form into ShortString, panicking if it fails - unwrap!(ShortString::try_from(selected)) + // Convert safely, returning empty string on failure + ShortString::try_from(selected).unwrap_or_default() } #[derive(Copy, Clone)] @@ -334,8 +336,12 @@ mod tests { #[test] fn test_plural_form() { use super::plural_form; + // malformed templete, returns empty string + assert_eq!(plural_form("day", 1).as_str(), "day"); + // simple singular/plural assert_eq!(plural_form("day|days", 1).as_str(), "day"); assert_eq!(plural_form("day|days", 3).as_str(), "days"); + // Czech-style singular/few/many assert_eq!(plural_form("den|dny|dní", 1).as_str(), "den"); assert_eq!(plural_form("den|dny|dní", 3).as_str(), "dny"); assert_eq!(plural_form("den|dny|dní", 5).as_str(), "dní"); diff --git a/core/embed/rust/src/ui/layout_eckhart/firmware/value_input_screen.rs b/core/embed/rust/src/ui/layout_eckhart/firmware/value_input_screen.rs index efc552aed9..1c433e91b2 100644 --- a/core/embed/rust/src/ui/layout_eckhart/firmware/value_input_screen.rs +++ b/core/embed/rust/src/ui/layout_eckhart/firmware/value_input_screen.rs @@ -479,6 +479,7 @@ impl ValueInput for DurationInput { (self.duration.to_days(), TR::plurals__lock_after_x_days), (self.duration.to_hours(), TR::plurals__lock_after_x_hours), (self.duration.to_mins(), TR::plurals__lock_after_x_minutes), + (self.duration.to_secs(), TR::plurals__lock_after_x_seconds), ]; for &(count, tr) in &units { @@ -489,11 +490,8 @@ impl ValueInput for DurationInput { } } - // Fallback to seconds if all are 0 - let count = self.duration.to_secs(); - let plural = TString::from_translation(TR::plurals__lock_after_x_seconds) - .map(|template| plural_form(template, count)); - (count, Some(plural)) + // This should never be reached unless duration is exactly 0 + (0, None) } fn increment(&mut self) {