From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) by lore.proxmox.com (Postfix) with ESMTPS id 4350C1FF183 for ; Wed, 13 Aug 2025 13:02:24 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 4B90B3168C; Wed, 13 Aug 2025 13:04:02 +0200 (CEST) From: Maximiliano Sandoval To: pbs-devel@lists.proxmox.com Date: Wed, 13 Aug 2025 13:03:26 +0200 Message-ID: <20250813110328.330900-2-m.sandoval@proxmox.com> X-Mailer: git-send-email 2.47.2 In-Reply-To: <20250813110328.330900-1-m.sandoval@proxmox.com> References: <20250813110328.330900-1-m.sandoval@proxmox.com> MIME-Version: 1.0 X-Bm-Milter-Handled: 55990f41-d878-4baa-be0a-ee34c49e34d2 X-Bm-Transport-Timestamp: 1755082976976 X-SPAM-LEVEL: Spam detection results: 0 AWL 0.094 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% DMARC_MISSING 0.1 Missing DMARC policy KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RCVD_IN_VALIDITY_CERTIFIED_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_RPBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. RCVD_IN_VALIDITY_SAFE_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to Validity was blocked. See https://knowledge.validity.com/hc/en-us/articles/20961730681243 for more information. SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record Subject: [pbs-devel] [PATCH proxmox 2/3] time: Split parse_time_spec parser into two X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: Proxmox Backup Server development discussion Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Errors-To: pbs-devel-bounces@lists.proxmox.com Sender: "pbs-devel" When using a single parser "40:00" would be parsed as "40 minutes and 00 seconds", but this should error out: ``` $ systemd-analyze calendar 40:00 Failed to parse calendar specification '40:00': Invalid argument ``` We split into two parsers one in which seconds are specified and one in which they are not and add tests for good measure. The test added in this commit would not error out before this commit and was erroneously producing "Every 40 minutes" as a result. Signed-off-by: Maximiliano Sandoval --- proxmox-time/src/calendar_event.rs | 79 +++++++++++++++++++++++++++--- 1 file changed, 72 insertions(+), 7 deletions(-) diff --git a/proxmox-time/src/calendar_event.rs b/proxmox-time/src/calendar_event.rs index 5d5bdf83..edb258ba 100644 --- a/proxmox-time/src/calendar_event.rs +++ b/proxmox-time/src/calendar_event.rs @@ -1,5 +1,6 @@ use anyhow::Error; use nom::{ + branch::alt, bytes::complete::tag, character::complete::space0, combinator::opt, @@ -397,16 +398,13 @@ fn parse_date_time_comp_list( } } -fn parse_time_spec(i: &str) -> IResult<&str, TimeSpec> { - let (i, (opt_hour, minute, opt_second)) = tuple(( - opt(terminated(parse_date_time_comp_list(0, 24), tag(":"))), +fn parse_time_spec_with_seconds(i: &str) -> IResult<&str, TimeSpec> { + let (i, (hour, minute, second)) = tuple(( + terminated(parse_date_time_comp_list(0, 24), tag(":")), parse_date_time_comp_list(0, 60), - opt(preceded(tag(":"), parse_date_time_comp_list(0, 60))), + preceded(tag(":"), parse_date_time_comp_list(0, 60)), ))(i)?; - let hour = opt_hour.unwrap_or_default(); - let second = opt_second.unwrap_or_else(|| vec![DateTimeValue::Single(0)]); - Ok(( i, TimeSpec { @@ -417,6 +415,32 @@ fn parse_time_spec(i: &str) -> IResult<&str, TimeSpec> { )) } +fn parse_time_spec_without_seconds(i: &str) -> IResult<&str, TimeSpec> { + let (i, (opt_hour, minute)) = tuple(( + opt(terminated(parse_date_time_comp_list(0, 24), tag(":"))), + parse_date_time_comp_list(0, 60), + ))(i)?; + + let hour = opt_hour.unwrap_or_default(); + let second = vec![DateTimeValue::Single(0)]; + + Ok(( + i, + TimeSpec { + hour, + minute, + second, + }, + )) +} + +fn parse_time_spec(i: &str) -> IResult<&str, TimeSpec> { + alt(( + parse_time_spec_with_seconds, + parse_time_spec_without_seconds, + ))(i) +} + fn parse_date_spec(i: &str) -> IResult<&str, DateSpec> { // TODO: implement ~ for days (man systemd.time) if let Ok((i, (year, month, day))) = tuple(( @@ -443,3 +467,44 @@ fn parse_date_spec(i: &str) -> IResult<&str, DateSpec> { Err(parse_error(i, "invalid date spec")) } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_calendar_event_from_str() { + use std::str::FromStr; + + assert!(CalendarEvent::from_str("15:30").is_ok()); + + assert!(CalendarEvent::from_str("40:00").is_err()); + } + + #[test] + fn test_parse_time_spec() { + let (input, data) = parse_time_spec("15:30").unwrap(); + assert_eq!(input, ""); + assert_eq!( + data, + TimeSpec { + hour: vec![DateTimeValue::Single(15)], + minute: vec![DateTimeValue::Single(30)], + second: vec![DateTimeValue::Single(0)] + } + ); + + // These do not strictly fail, but should fail when parsed with FromStr + // since there will be input left to consume. + let (input, data) = parse_time_spec("40:00").unwrap(); + assert_eq!(input, ":00"); + assert_eq!( + data, + TimeSpec { + hour: vec![], + minute: vec![DateTimeValue::Single(40)], + second: vec![DateTimeValue::Single(0)] + } + ); + } +} -- 2.47.2 _______________________________________________ pbs-devel mailing list pbs-devel@lists.proxmox.com https://lists.proxmox.com/cgi-bin/mailman/listinfo/pbs-devel