From 85663ef05079c760f7459b97c12a00fb0609ba33 Mon Sep 17 00:00:00 2001 From: Julian Merkle Date: Sat, 14 Mar 2026 15:26:09 +0100 Subject: [PATCH] calendar.rs: Allow RGBA colors The android app "DAVx" creates calendars with RGBA color codes. fixes #199 --- src/domain/entities/calendar.rs | 116 ++++++++++++++++++++++---------- 1 file changed, 80 insertions(+), 36 deletions(-) diff --git a/src/domain/entities/calendar.rs b/src/domain/entities/calendar.rs index f870304d..3b6f0a49 100755 --- a/src/domain/entities/calendar.rs +++ b/src/domain/entities/calendar.rs @@ -82,24 +82,8 @@ impl Calendar { )); } - // Validate color format if provided (#RRGGBB) - if let Some(ref color_str) = color { - if !color_str.starts_with('#') || color_str.len() != 7 { - return Err(DomainError::new( - ErrorKind::InvalidInput, - "Calendar", - "Color must be in #RRGGBB format", - )); - } - - // Check if remaining characters are valid hex - if color_str[1..].chars().any(|c| !c.is_ascii_hexdigit()) { - return Err(DomainError::new( - ErrorKind::InvalidInput, - "Calendar", - "Color must be in #RRGGBB format with valid hex digits", - )); - } + if let Some(color_str) = &color { + Self::validate_color(color_str)?; } let now = Utc::now(); @@ -155,6 +139,10 @@ impl Calendar { )); } + if let Some(color_str) = &color { + Self::validate_color(color_str)?; + } + Ok(Self { id, name, @@ -253,24 +241,9 @@ impl Calendar { * @return Result indicating success or containing a domain error */ pub fn update_color(&mut self, color: Option) -> Result<()> { - // Validate color format if provided (#RRGGBB) - if let Some(ref color_str) = color { - if !color_str.starts_with('#') || color_str.len() != 7 { - return Err(DomainError::new( - ErrorKind::InvalidInput, - "Calendar", - "Color must be in #RRGGBB format", - )); - } - - // Check if remaining characters are valid hex - if color_str[1..].chars().any(|c| !c.is_ascii_hexdigit()) { - return Err(DomainError::new( - ErrorKind::InvalidInput, - "Calendar", - "Color must be in #RRGGBB format with valid hex digits", - )); - } + // Validate color format if provided + if let Some(color_str) = &color { + Self::validate_color(&color_str)?; } self.color = color; @@ -278,6 +251,21 @@ impl Calendar { Ok(()) } + /// Validate a calendar color + fn validate_color(color: &str) -> Result<()> { + if !color.starts_with('#') + || !(color.len() == 7 || color.len() == 9) + || color[1..].chars().any(|c| !c.is_ascii_hexdigit()) + { + return Err(DomainError::new( + ErrorKind::InvalidInput, + "Calendar", + "Color must be in #RRGGBB or #RRGGBBAA format", + )); + } + Ok(()) + } + /** * Sets a custom property for extended CalDAV support. * @@ -321,3 +309,59 @@ impl Calendar { self.updated_at = Utc::now(); } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_init() { + let res = Calendar::new("Name".to_string(), "ID".to_string(), None, None); + assert!(res.is_ok()); + } + + #[test] + fn test_init_color_rgb() { + let res = Calendar::new( + "Name".to_string(), + "ID".to_string(), + None, + Some("#84FFa9".to_string()), + ); + assert!(res.is_ok()); + } + + /// Format as used by the android DAVx app + #[test] + fn test_init_color_rgba() { + let res = Calendar::new( + "Name".to_string(), + "ID".to_string(), + None, + Some("#abcdef51".to_string()), + ); + assert!(res.is_ok()); + } + + #[test] + fn test_init_bad_color_1() { + let res = Calendar::new( + "Name".to_string(), + "ID".to_string(), + None, + Some("foo".to_string()), + ); + assert!(res.is_err()); + } + + #[test] + fn test_init_bad_color_2() { + let res = Calendar::new( + "Name".to_string(), + "ID".to_string(), + None, + Some("#xxjjff".to_string()), + ); + assert!(res.is_err()); + } +}