public inbox for pbs-devel@lists.proxmox.com
 help / color / mirror / Atom feed
* [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time
@ 2021-11-30 12:11 Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 01/14] proxmox-time: calendar-events: implement repeated ranges Dominik Csapak
                   ` (15 more replies)
  0 siblings, 16 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:11 UTC (permalink / raw)
  To: pbs-devel

adds two new features to calendar events (1/14 and 2/14):
* repeated range syntax: '7..17/2' means '7,9,11,13,15,17' (much shorter)
* make hours optional (all hours are selected then), necessary for pve
  compatibility

patches 3-14/14 are cleanup, code moves and rustfmt commits, so that
the code is better organized (and more idiomatic rust, e.g.
impl FromStr instead of 'parse_*')

these should be backwards compatible, so no major version bump necessary

proxmox-backup patch is to not use the deprecated functions anymore

proxmox:

Dominik Csapak (14):
  proxmox-time: calendar-events: implement repeated ranges
  proxmox-time: calendar-events: make hour optional
  proxmox-time: move common parse functions to parse_helpers
  proxmox-time: move WeekDays into own file
  proxmox-time: split DateTimeValue into own file
  proxmox-time: move parse_daily_duration to daily_duration.rs
  proxmox-time: daily_duration.rs: rustfmt
  proxmox-time: move CalendarEvent into calendar_events.rs
  proxmox-time: move TimeSpan into time_span.rs
  proxmox-time: move tests from time.rs to test.rs
  proxmox-time: lib.rs: rustfmt
  proxmox-time: calendar-events: make compute_next_event a method
  proxmox-time: calendar_events: implement FromStr
  proxmox-time: time-span: implement FromStr

 proxmox-time/src/calendar_event.rs  | 447 +++++++++++++++++++++
 proxmox-time/src/daily_duration.rs  | 130 ++++--
 proxmox-time/src/date_time_value.rs |  82 ++++
 proxmox-time/src/lib.rs             |  38 +-
 proxmox-time/src/parse_helpers.rs   |  75 ++++
 proxmox-time/src/parse_time.rs      | 513 ------------------------
 proxmox-time/src/test.rs            | 263 +++++++++++++
 proxmox-time/src/time.rs            | 591 ----------------------------
 proxmox-time/src/time_span.rs       | 274 +++++++++++++
 proxmox-time/src/week_days.rs       |  69 ++++
 10 files changed, 1332 insertions(+), 1150 deletions(-)
 create mode 100644 proxmox-time/src/calendar_event.rs
 create mode 100644 proxmox-time/src/date_time_value.rs
 create mode 100644 proxmox-time/src/parse_helpers.rs
 delete mode 100644 proxmox-time/src/parse_time.rs
 create mode 100644 proxmox-time/src/test.rs
 delete mode 100644 proxmox-time/src/time.rs
 create mode 100644 proxmox-time/src/time_span.rs
 create mode 100644 proxmox-time/src/week_days.rs

proxmox-backup:

Dominik Csapak (1):
  remove use of deprecated functions from proxmox-time

 pbs-api-types/src/tape/media_pool.rs |  6 +++---
 src/bin/proxmox-backup-proxy.rs      | 10 +++++-----
 src/server/jobstate.rs               |  6 +++---
 src/tape/inventory.rs                |  3 +--
 src/tape/media_pool.rs               |  3 +--
 src/tape/test/compute_media_state.rs |  4 ++--
 6 files changed, 15 insertions(+), 17 deletions(-)

-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 01/14] proxmox-time: calendar-events: implement repeated ranges
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
@ 2021-11-30 12:11 ` Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 02/14] proxmox-time: calendar-events: make hour optional Dominik Csapak
                   ` (14 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:11 UTC (permalink / raw)
  To: pbs-devel

so that we can have e.g. '7..17/2:00' as timespec

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/parse_time.rs | 10 +++++++---
 proxmox-time/src/time.rs       | 27 ++++++++++++++++++++++-----
 2 files changed, 29 insertions(+), 8 deletions(-)

diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
index 89d147e..8159a1a 100644
--- a/proxmox-time/src/parse_time.rs
+++ b/proxmox-time/src/parse_time.rs
@@ -191,12 +191,16 @@ fn parse_date_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, DateTimeVa
             if value > end {
                 return Err(parse_error(i, "range start is bigger than end"));
             }
-            return Ok((i, DateTimeValue::Range(value, end)))
+            if let Some(time) = i.strip_prefix('/') {
+                let (time, repeat) = parse_time_comp(max)(time)?;
+                return Ok((time, DateTimeValue::Repeated(value, repeat, Some(end))));
+            }
+            return Ok((i, DateTimeValue::Range(value, end)));
         }
 
         if let Some(time) = i.strip_prefix('/') {
             let (time, repeat) = parse_time_comp(max)(time)?;
-            Ok((time, DateTimeValue::Repeated(value, repeat)))
+            Ok((time, DateTimeValue::Repeated(value, repeat, None)))
         } else {
             Ok((i, DateTimeValue::Single(value)))
         }
@@ -209,7 +213,7 @@ fn parse_date_time_comp_list(start: u32, max: usize) -> impl Fn(&str) -> IResult
             if let Some(time) = rest.strip_prefix('/') {
                 let (n, repeat) = parse_time_comp(max)(time)?;
                 if repeat > 0 {
-                    return Ok((n, vec![DateTimeValue::Repeated(start, repeat)]));
+                    return Ok((n, vec![DateTimeValue::Repeated(start, repeat, None)]));
                 }
             }
             return Ok((rest, Vec::new()));
diff --git a/proxmox-time/src/time.rs b/proxmox-time/src/time.rs
index a220f2c..2dfe425 100644
--- a/proxmox-time/src/time.rs
+++ b/proxmox-time/src/time.rs
@@ -25,7 +25,7 @@ bitflags!{
 pub(crate) enum DateTimeValue {
     Single(u32),
     Range(u32, u32),
-    Repeated(u32, u32),
+    Repeated(u32, u32, Option<u32>),
 }
 
 impl DateTimeValue {
@@ -34,11 +34,16 @@ impl DateTimeValue {
         match self {
             DateTimeValue::Single(v) => *v == value,
             DateTimeValue::Range(start, end) => value >= *start && value <= *end,
-            DateTimeValue::Repeated(start, repetition) => {
+            DateTimeValue::Repeated(start, repetition, opt_end) => {
                 if value >= *start {
                     if *repetition > 0 {
                         let offset = value - start;
-                        offset % repetition == 0
+                        let res = offset % repetition == 0;
+                        if let Some(end) = opt_end {
+                            res && value <= *end
+                        } else {
+                            res
+                        }
                     } else {
                         *start == value
                     }
@@ -78,11 +83,18 @@ impl DateTimeValue {
                         }
                     }
                 }
-                DateTimeValue::Repeated(start, repetition) => {
+                DateTimeValue::Repeated(start, repetition, opt_end) => {
                     if value < *start {
                         set_next(*start);
                     } else if *repetition > 0 {
-                        set_next(start + ((value - start + repetition) / repetition) * repetition);
+                        let n = start + ((value - start + repetition) / repetition) * repetition;
+                        if let Some(end) = opt_end {
+                            if n <= *end {
+                                set_next(n);
+                            }
+                        } else {
+                            set_next(n);
+                        }
                     }
                 }
             }
@@ -455,6 +467,11 @@ mod test {
         test_value("sat", THURSDAY_00_00, THURSDAY_00_00 + 2*DAY)?;
         test_value("sun", THURSDAY_00_00, THURSDAY_00_00 + 3*DAY)?;
 
+        // test repeated ranges
+        test_value("4..10/2:0", THURSDAY_00_00, THURSDAY_00_00 + 4*HOUR)?;
+        test_value("4..10/2:0", THURSDAY_00_00 + 5*HOUR, THURSDAY_00_00 + 6*HOUR)?;
+        test_value("4..10/2:0", THURSDAY_00_00 + 11*HOUR, THURSDAY_00_00 + 1*DAY + 4*HOUR)?;
+
         // test multiple values for a single field
         // and test that the order does not matter
         test_value("5,10:4,8", THURSDAY_00_00, THURSDAY_00_00 + 5*HOUR + 4*MIN)?;
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 02/14] proxmox-time: calendar-events: make hour optional
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 01/14] proxmox-time: calendar-events: implement repeated ranges Dominik Csapak
@ 2021-11-30 12:11 ` Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 03/14] proxmox-time: move common parse functions to parse_helpers Dominik Csapak
                   ` (13 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:11 UTC (permalink / raw)
  To: pbs-devel

to be compatible with our perl calendar events, we have to make hour optional
in that case we select every hour, so 'X' is the same as writing '*:X'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/parse_time.rs | 17 ++++++++---------
 proxmox-time/src/time.rs       |  4 ++++
 2 files changed, 12 insertions(+), 9 deletions(-)

diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
index 8159a1a..aa1c58e 100644
--- a/proxmox-time/src/parse_time.rs
+++ b/proxmox-time/src/parse_time.rs
@@ -10,7 +10,7 @@ use nom::{
     error::{context, ParseError, VerboseError},
     bytes::complete::{tag, take_while1},
     combinator::{map_res, all_consuming, opt, recognize},
-    sequence::{pair, preceded, tuple},
+    sequence::{pair, preceded, terminated, tuple},
     character::complete::{alpha1, space0, digit1},
     multi::separated_nonempty_list,
 };
@@ -225,17 +225,16 @@ fn parse_date_time_comp_list(start: u32, max: usize) -> impl Fn(&str) -> IResult
 
 fn parse_time_spec(i: &str) -> IResult<&str, TimeSpec> {
 
-    let (i, (hour, minute, opt_second)) = tuple((
-        parse_date_time_comp_list(0, 24),
-        preceded(tag(":"), parse_date_time_comp_list(0, 60)),
+    let (i, (opt_hour, minute, opt_second)) = tuple((
+        opt(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))),
     ))(i)?;
 
-    if let Some(second) = opt_second {
-        Ok((i, TimeSpec { hour, minute, second }))
-    } else {
-        Ok((i, TimeSpec { hour, minute, second: vec![DateTimeValue::Single(0)] }))
-    }
+    let hour = opt_hour.unwrap_or_else(Vec::new);
+    let second = opt_second.unwrap_or_else(|| vec![DateTimeValue::Single(0)]);
+
+    Ok((i, TimeSpec { hour, minute, second }))
 }
 
 fn parse_date_spec(i: &str) -> IResult<&str, DateSpec> {
diff --git a/proxmox-time/src/time.rs b/proxmox-time/src/time.rs
index 2dfe425..fb18949 100644
--- a/proxmox-time/src/time.rs
+++ b/proxmox-time/src/time.rs
@@ -451,6 +451,10 @@ mod test {
         const JUL_31_2020: i64 = 1596153600; // Friday, 2020-07-31 00:00:00
         const DEC_31_2020: i64 = 1609372800; // Thursday, 2020-12-31 00:00:00
 
+        // minute only syntax
+        test_value("0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
+        test_value("*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
+
         test_value("*:0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
         test_value("*:*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
         test_value("*:*:*", THURSDAY_00_00, THURSDAY_00_00 + 1)?;
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 03/14] proxmox-time: move common parse functions to parse_helpers
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 01/14] proxmox-time: calendar-events: implement repeated ranges Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 02/14] proxmox-time: calendar-events: make hour optional Dominik Csapak
@ 2021-11-30 12:11 ` Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 04/14] proxmox-time: move WeekDays into own file Dominik Csapak
                   ` (12 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:11 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/lib.rs           |  2 +
 proxmox-time/src/parse_helpers.rs | 75 +++++++++++++++++++++++++++++++
 proxmox-time/src/parse_time.rs    | 56 +----------------------
 3 files changed, 78 insertions(+), 55 deletions(-)
 create mode 100644 proxmox-time/src/parse_helpers.rs

diff --git a/proxmox-time/src/lib.rs b/proxmox-time/src/lib.rs
index d72509f..d2a4cb7 100644
--- a/proxmox-time/src/lib.rs
+++ b/proxmox-time/src/lib.rs
@@ -13,6 +13,8 @@ pub use parse_time::*;
 mod time;
 pub use time::*;
 
+pub(crate) mod parse_helpers;
+
 mod daily_duration;
 pub use daily_duration::*;
 
diff --git a/proxmox-time/src/parse_helpers.rs b/proxmox-time/src/parse_helpers.rs
new file mode 100644
index 0000000..4890b2f
--- /dev/null
+++ b/proxmox-time/src/parse_helpers.rs
@@ -0,0 +1,75 @@
+use anyhow::{bail, Error};
+
+use super::daily_duration::*;
+
+use nom::{
+    bytes::complete::tag,
+    character::complete::digit1,
+    combinator::{all_consuming, map_res, opt, recognize},
+    error::{ParseError, VerboseError},
+    sequence::{preceded, tuple},
+};
+
+pub(crate) type IResult<I, O, E = VerboseError<I>> = Result<(I, O), nom::Err<E>>;
+
+pub(crate) fn parse_error<'a>(
+    i: &'a str,
+    context: &'static str,
+) -> nom::Err<VerboseError<&'a str>> {
+    let err = VerboseError { errors: Vec::new() };
+    let err = VerboseError::add_context(i, context, err);
+    nom::Err::Error(err)
+}
+
+// Parse a 64 bit unsigned integer
+pub(crate) fn parse_u64(i: &str) -> IResult<&str, u64> {
+    map_res(recognize(digit1), str::parse)(i)
+}
+
+// Parse complete input, generate simple error message (use this for sinple line input).
+pub(crate) fn parse_complete_line<'a, F, O>(what: &str, i: &'a str, parser: F) -> Result<O, Error>
+where
+    F: Fn(&'a str) -> IResult<&'a str, O>,
+{
+    match all_consuming(parser)(i) {
+        Err(nom::Err::Error(VerboseError { errors }))
+        | Err(nom::Err::Failure(VerboseError { errors })) => {
+            if errors.is_empty() {
+                bail!("unable to parse {}", what);
+            } else {
+                bail!(
+                    "unable to parse {} at '{}' - {:?}",
+                    what,
+                    errors[0].0,
+                    errors[0].1
+                );
+            }
+        }
+        Err(err) => {
+            bail!("unable to parse {} - {}", what, err);
+        }
+        Ok((_, data)) => Ok(data),
+    }
+}
+
+pub(crate) fn parse_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, u32> {
+    move |i: &str| {
+        let (i, v) = map_res(recognize(digit1), str::parse)(i)?;
+        if (v as usize) >= max {
+            return Err(parse_error(i, "time value too large"));
+        }
+        Ok((i, v))
+    }
+}
+
+pub(crate) fn parse_hm_time(i: &str) -> IResult<&str, HmTime> {
+    let (i, (hour, opt_minute)) = tuple((
+        parse_time_comp(24),
+        opt(preceded(tag(":"), parse_time_comp(60))),
+    ))(i)?;
+
+    match opt_minute {
+        Some(minute) => Ok((i, HmTime { hour, minute })),
+        None => Ok((i, HmTime { hour, minute: 0 })),
+    }
+}
diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
index aa1c58e..7b0e168 100644
--- a/proxmox-time/src/parse_time.rs
+++ b/proxmox-time/src/parse_time.rs
@@ -15,38 +15,7 @@ use nom::{
     multi::separated_nonempty_list,
 };
 
-type IResult<I, O, E = VerboseError<I>> = Result<(I, O), nom::Err<E>>;
-
-fn parse_error<'a>(i: &'a str, context: &'static str) -> nom::Err<VerboseError<&'a str>> {
-    let err = VerboseError { errors: Vec::new() };
-    let err = VerboseError::add_context(i, context, err);
-    nom::Err::Error(err)
-}
-
-// Parse a 64 bit unsigned integer
-fn parse_u64(i: &str) -> IResult<&str, u64> {
-    map_res(recognize(digit1), str::parse)(i)
-}
-
-// Parse complete input, generate simple error message (use this for sinple line input).
-fn parse_complete_line<'a, F, O>(what: &str, i: &'a str, parser: F) -> Result<O, Error>
-    where F: Fn(&'a str) -> IResult<&'a str, O>,
-{
-    match all_consuming(parser)(i) {
-        Err(nom::Err::Error(VerboseError { errors })) |
-        Err(nom::Err::Failure(VerboseError { errors })) => {
-            if errors.is_empty() {
-                bail!("unable to parse {}", what);
-            } else {
-                bail!("unable to parse {} at '{}' - {:?}", what, errors[0].0, errors[0].1);
-            }
-        }
-        Err(err) => {
-            bail!("unable to parse {} - {}", what, err);
-        }
-        Ok((_, data)) => Ok(data),
-    }
-}
+use crate::parse_helpers::{parse_complete_line, parse_error, parse_hm_time, parse_time_comp, parse_u64, IResult};
 
 lazy_static! {
     static ref TIME_SPAN_UNITS: HashMap<&'static str, f64> = {
@@ -129,16 +98,6 @@ struct DateSpec {
     day: Vec<DateTimeValue>,
 }
 
-fn parse_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, u32> {
-    move |i: &str| {
-        let (i, v) = map_res(recognize(digit1), str::parse)(i)?;
-        if (v as usize) >= max {
-            return Err(parse_error(i, "time value too large"));
-        }
-        Ok((i, v))
-    }
-}
-
 fn parse_weekday(i: &str) -> IResult<&str, WeekDays> {
     let (i, text) = alpha1(i)?;
 
@@ -501,16 +460,3 @@ fn parse_daily_duration_incomplete(mut i: &str) -> IResult<&str, DailyDuration>
 
     Ok((i, duration))
 }
-
-fn parse_hm_time(i: &str) -> IResult<&str, HmTime> {
-
-    let (i, (hour, opt_minute)) = tuple((
-        parse_time_comp(24),
-        opt(preceded(tag(":"), parse_time_comp(60))),
-    ))(i)?;
-
-    match opt_minute {
-        Some(minute) => Ok((i, HmTime { hour, minute })),
-        None => Ok((i, HmTime { hour, minute: 0})),
-    }
-}
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 04/14] proxmox-time: move WeekDays into own file
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (2 preceding siblings ...)
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 03/14] proxmox-time: move common parse functions to parse_helpers Dominik Csapak
@ 2021-11-30 12:11 ` Dominik Csapak
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 05/14] proxmox-time: split DateTimeValue " Dominik Csapak
                   ` (11 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:11 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/lib.rs        |  3 ++
 proxmox-time/src/parse_time.rs | 45 +---------------------
 proxmox-time/src/time.rs       | 16 +-------
 proxmox-time/src/week_days.rs  | 69 ++++++++++++++++++++++++++++++++++
 4 files changed, 74 insertions(+), 59 deletions(-)
 create mode 100644 proxmox-time/src/week_days.rs

diff --git a/proxmox-time/src/lib.rs b/proxmox-time/src/lib.rs
index d2a4cb7..bce4b56 100644
--- a/proxmox-time/src/lib.rs
+++ b/proxmox-time/src/lib.rs
@@ -15,6 +15,9 @@ pub use time::*;
 
 pub(crate) mod parse_helpers;
 
+mod week_days;
+pub use week_days::*;
+
 mod daily_duration;
 pub use daily_duration::*;
 
diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
index 7b0e168..6dde4ae 100644
--- a/proxmox-time/src/parse_time.rs
+++ b/proxmox-time/src/parse_time.rs
@@ -16,6 +16,7 @@ use nom::{
 };
 
 use crate::parse_helpers::{parse_complete_line, parse_error, parse_hm_time, parse_time_comp, parse_u64, IResult};
+use crate::{parse_weekdays_range, WeekDays};
 
 lazy_static! {
     static ref TIME_SPAN_UNITS: HashMap<&'static str, f64> = {
@@ -98,50 +99,6 @@ struct DateSpec {
     day: Vec<DateTimeValue>,
 }
 
-fn parse_weekday(i: &str) -> IResult<&str, WeekDays> {
-    let (i, text) = alpha1(i)?;
-
-    match text.to_ascii_lowercase().as_str() {
-        "monday" | "mon" => Ok((i, WeekDays::MONDAY)),
-        "tuesday" | "tue" => Ok((i, WeekDays::TUESDAY)),
-        "wednesday" | "wed" => Ok((i, WeekDays::WEDNESDAY)),
-        "thursday" | "thu" => Ok((i, WeekDays::THURSDAY)),
-        "friday" | "fri" => Ok((i, WeekDays::FRIDAY)),
-        "saturday" | "sat" => Ok((i, WeekDays::SATURDAY)),
-        "sunday" | "sun" => Ok((i, WeekDays::SUNDAY)),
-        _ => return Err(parse_error(text, "weekday")),
-    }
-}
-
-fn parse_weekdays_range(i: &str) -> IResult<&str, WeekDays> {
-    let (i, startday) = parse_weekday(i)?;
-
-    let generate_range = |start, end| {
-        let mut res = 0;
-        let mut pos = start;
-        loop {
-            res |= pos;
-            if pos >= end { break; }
-            pos <<= 1;
-        }
-        WeekDays::from_bits(res).unwrap()
-    };
-
-    if let (i, Some((_, endday))) = opt(pair(tag(".."),parse_weekday))(i)? {
-        let start = startday.bits();
-        let end = endday.bits();
-        if start > end {
-            let set1 = generate_range(start, WeekDays::SUNDAY.bits());
-            let set2 = generate_range(WeekDays::MONDAY.bits(), end);
-            Ok((i, set1 | set2))
-        } else {
-            Ok((i, generate_range(start, end)))
-        }
-    } else {
-        Ok((i, startday))
-    }
-}
-
 fn parse_date_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, DateTimeValue> {
     move |i: &str| {
         let (i, value) = parse_time_comp(max)(i)?;
diff --git a/proxmox-time/src/time.rs b/proxmox-time/src/time.rs
index fb18949..2b2a936 100644
--- a/proxmox-time/src/time.rs
+++ b/proxmox-time/src/time.rs
@@ -1,26 +1,12 @@
 use std::convert::TryInto;
 
 use anyhow::Error;
-use bitflags::bitflags;
 
 use crate::TmEditor;
+use crate::WeekDays;
 
 use crate::{parse_calendar_event, parse_time_span};
 
-bitflags!{
-    /// Defines one or more days of a week.
-    #[derive(Default)]
-    pub struct WeekDays: u8 {
-        const MONDAY = 1;
-        const TUESDAY = 2;
-        const WEDNESDAY = 4;
-        const THURSDAY = 8;
-        const FRIDAY = 16;
-        const SATURDAY = 32;
-        const SUNDAY = 64;
-    }
-}
-
 #[derive(Debug, Clone)]
 pub(crate) enum DateTimeValue {
     Single(u32),
diff --git a/proxmox-time/src/week_days.rs b/proxmox-time/src/week_days.rs
new file mode 100644
index 0000000..64d4137
--- /dev/null
+++ b/proxmox-time/src/week_days.rs
@@ -0,0 +1,69 @@
+use bitflags::bitflags;
+use nom::{
+    bytes::complete::tag,
+    character::complete::alpha1,
+    combinator::opt,
+    sequence::pair,
+};
+
+use crate::parse_helpers::{parse_error, IResult};
+
+bitflags! {
+    /// Defines one or more days of a week.
+    #[derive(Default)]
+    pub struct WeekDays: u8 {
+        const MONDAY = 1;
+        const TUESDAY = 2;
+        const WEDNESDAY = 4;
+        const THURSDAY = 8;
+        const FRIDAY = 16;
+        const SATURDAY = 32;
+        const SUNDAY = 64;
+    }
+}
+
+fn parse_weekday(i: &str) -> IResult<&str, WeekDays> {
+    let (i, text) = alpha1(i)?;
+
+    match text.to_ascii_lowercase().as_str() {
+        "monday" | "mon" => Ok((i, WeekDays::MONDAY)),
+        "tuesday" | "tue" => Ok((i, WeekDays::TUESDAY)),
+        "wednesday" | "wed" => Ok((i, WeekDays::WEDNESDAY)),
+        "thursday" | "thu" => Ok((i, WeekDays::THURSDAY)),
+        "friday" | "fri" => Ok((i, WeekDays::FRIDAY)),
+        "saturday" | "sat" => Ok((i, WeekDays::SATURDAY)),
+        "sunday" | "sun" => Ok((i, WeekDays::SUNDAY)),
+        _ => return Err(parse_error(text, "weekday")),
+    }
+}
+
+pub(crate) fn parse_weekdays_range(i: &str) -> IResult<&str, WeekDays> {
+    let (i, startday) = parse_weekday(i)?;
+
+    let generate_range = |start, end| {
+        let mut res = 0;
+        let mut pos = start;
+        loop {
+            res |= pos;
+            if pos >= end {
+                break;
+            }
+            pos <<= 1;
+        }
+        WeekDays::from_bits(res).unwrap()
+    };
+
+    if let (i, Some((_, endday))) = opt(pair(tag(".."), parse_weekday))(i)? {
+        let start = startday.bits();
+        let end = endday.bits();
+        if start > end {
+            let set1 = generate_range(start, WeekDays::SUNDAY.bits());
+            let set2 = generate_range(WeekDays::MONDAY.bits(), end);
+            Ok((i, set1 | set2))
+        } else {
+            Ok((i, generate_range(start, end)))
+        }
+    } else {
+        Ok((i, startday))
+    }
+}
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 05/14] proxmox-time: split DateTimeValue into own file
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (3 preceding siblings ...)
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 04/14] proxmox-time: move WeekDays into own file Dominik Csapak
@ 2021-11-30 12:11 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 06/14] proxmox-time: move parse_daily_duration to daily_duration.rs Dominik Csapak
                   ` (10 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:11 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/date_time_value.rs | 82 ++++++++++++++++++++++++++++
 proxmox-time/src/lib.rs             |  2 +
 proxmox-time/src/parse_time.rs      |  1 +
 proxmox-time/src/time.rs            | 84 +----------------------------
 4 files changed, 86 insertions(+), 83 deletions(-)
 create mode 100644 proxmox-time/src/date_time_value.rs

diff --git a/proxmox-time/src/date_time_value.rs b/proxmox-time/src/date_time_value.rs
new file mode 100644
index 0000000..c3c2035
--- /dev/null
+++ b/proxmox-time/src/date_time_value.rs
@@ -0,0 +1,82 @@
+#[derive(Debug, Clone)]
+pub(crate) enum DateTimeValue {
+    Single(u32),
+    Range(u32, u32),
+    Repeated(u32, u32, Option<u32>),
+}
+
+impl DateTimeValue {
+    // Test if the entry contains the value
+    pub fn contains(&self, value: u32) -> bool {
+        match self {
+            DateTimeValue::Single(v) => *v == value,
+            DateTimeValue::Range(start, end) => value >= *start && value <= *end,
+            DateTimeValue::Repeated(start, repetition, opt_end) => {
+                if value >= *start {
+                    if *repetition > 0 {
+                        let offset = value - start;
+                        let res = offset % repetition == 0;
+                        if let Some(end) = opt_end {
+                            res && value <= *end
+                        } else {
+                            res
+                        }
+                    } else {
+                        *start == value
+                    }
+                } else {
+                    false
+                }
+            }
+        }
+    }
+
+    pub fn list_contains(list: &[DateTimeValue], value: u32) -> bool {
+        list.iter().any(|spec| spec.contains(value))
+    }
+
+    // Find an return an entry greater than value
+    pub fn find_next(list: &[DateTimeValue], value: u32) -> Option<u32> {
+        let mut next: Option<u32> = None;
+        let mut set_next = |v: u32| {
+            if let Some(n) = next {
+                if v < n { next = Some(v); }
+            } else {
+                next = Some(v);
+            }
+        };
+        for spec in list {
+            match spec {
+                DateTimeValue::Single(v) => {
+                    if *v > value { set_next(*v); }
+                }
+                DateTimeValue::Range(start, end) => {
+                    if value < *start {
+                        set_next(*start);
+                    } else {
+                        let n = value + 1;
+                        if n >= *start && n <= *end {
+                            set_next(n);
+                        }
+                    }
+                }
+                DateTimeValue::Repeated(start, repetition, opt_end) => {
+                    if value < *start {
+                        set_next(*start);
+                    } else if *repetition > 0 {
+                        let n = start + ((value - start + repetition) / repetition) * repetition;
+                        if let Some(end) = opt_end {
+                            if n <= *end {
+                                set_next(n);
+                            }
+                        } else {
+                            set_next(n);
+                        }
+                    }
+                }
+            }
+        }
+
+        next
+    }
+}
diff --git a/proxmox-time/src/lib.rs b/proxmox-time/src/lib.rs
index bce4b56..3d888a2 100644
--- a/proxmox-time/src/lib.rs
+++ b/proxmox-time/src/lib.rs
@@ -15,6 +15,8 @@ pub use time::*;
 
 pub(crate) mod parse_helpers;
 
+pub(crate) mod date_time_value;
+
 mod week_days;
 pub use week_days::*;
 
diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
index 6dde4ae..00a9241 100644
--- a/proxmox-time/src/parse_time.rs
+++ b/proxmox-time/src/parse_time.rs
@@ -17,6 +17,7 @@ use nom::{
 
 use crate::parse_helpers::{parse_complete_line, parse_error, parse_hm_time, parse_time_comp, parse_u64, IResult};
 use crate::{parse_weekdays_range, WeekDays};
+use crate::date_time_value::DateTimeValue;
 
 lazy_static! {
     static ref TIME_SPAN_UNITS: HashMap<&'static str, f64> = {
diff --git a/proxmox-time/src/time.rs b/proxmox-time/src/time.rs
index 2b2a936..7b9625f 100644
--- a/proxmox-time/src/time.rs
+++ b/proxmox-time/src/time.rs
@@ -2,94 +2,12 @@ use std::convert::TryInto;
 
 use anyhow::Error;
 
+use crate::date_time_value::DateTimeValue;
 use crate::TmEditor;
 use crate::WeekDays;
 
 use crate::{parse_calendar_event, parse_time_span};
 
-#[derive(Debug, Clone)]
-pub(crate) enum DateTimeValue {
-    Single(u32),
-    Range(u32, u32),
-    Repeated(u32, u32, Option<u32>),
-}
-
-impl DateTimeValue {
-    // Test if the entry contains the value
-    pub fn contains(&self, value: u32) -> bool {
-        match self {
-            DateTimeValue::Single(v) => *v == value,
-            DateTimeValue::Range(start, end) => value >= *start && value <= *end,
-            DateTimeValue::Repeated(start, repetition, opt_end) => {
-                if value >= *start {
-                    if *repetition > 0 {
-                        let offset = value - start;
-                        let res = offset % repetition == 0;
-                        if let Some(end) = opt_end {
-                            res && value <= *end
-                        } else {
-                            res
-                        }
-                    } else {
-                        *start == value
-                    }
-                } else {
-                    false
-                }
-            }
-        }
-    }
-
-    pub fn list_contains(list: &[DateTimeValue], value: u32) -> bool {
-        list.iter().any(|spec| spec.contains(value))
-    }
-
-    // Find an return an entry greater than value
-    pub fn find_next(list: &[DateTimeValue], value: u32) -> Option<u32> {
-        let mut next: Option<u32> = None;
-        let mut set_next = |v: u32| {
-            if let Some(n) = next {
-                if v < n { next = Some(v); }
-            } else {
-                next = Some(v);
-            }
-        };
-        for spec in list {
-            match spec {
-                DateTimeValue::Single(v) => {
-                    if *v > value { set_next(*v); }
-                }
-                DateTimeValue::Range(start, end) => {
-                    if value < *start {
-                        set_next(*start);
-                    } else {
-                        let n = value + 1;
-                        if n >= *start && n <= *end {
-                            set_next(n);
-                        }
-                    }
-                }
-                DateTimeValue::Repeated(start, repetition, opt_end) => {
-                    if value < *start {
-                        set_next(*start);
-                    } else if *repetition > 0 {
-                        let n = start + ((value - start + repetition) / repetition) * repetition;
-                        if let Some(end) = opt_end {
-                            if n <= *end {
-                                set_next(n);
-                            }
-                        } else {
-                            set_next(n);
-                        }
-                    }
-                }
-            }
-        }
-
-        next
-    }
-}
-
 /// Calendar events may be used to refer to one or more points in time in a
 /// single expression. They are designed after the systemd.time Calendar Events
 /// specification, but are not guaranteed to be 100% compatible.
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 06/14] proxmox-time: move parse_daily_duration to daily_duration.rs
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (4 preceding siblings ...)
  2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 05/14] proxmox-time: split DateTimeValue " Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 07/14] proxmox-time: daily_duration.rs: rustfmt Dominik Csapak
                   ` (9 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/daily_duration.rs | 54 ++++++++++++++++++++++++++++--
 proxmox-time/src/parse_time.rs     | 43 ------------------------
 2 files changed, 51 insertions(+), 46 deletions(-)

diff --git a/proxmox-time/src/daily_duration.rs b/proxmox-time/src/daily_duration.rs
index 2f4b03d..d5e0d90 100644
--- a/proxmox-time/src/daily_duration.rs
+++ b/proxmox-time/src/daily_duration.rs
@@ -2,10 +2,15 @@ use std::cmp::{Ordering, PartialOrd};
 use std::convert::{TryFrom, TryInto};
 
 use anyhow::Error;
+use nom::{
+    bytes::complete::tag,
+    character::complete::space0,
+    error::context,
+    multi::separated_nonempty_list,
+};
 
-use crate::{TmEditor, WeekDays};
-
-pub use super::parse_time::parse_daily_duration;
+use crate::parse_helpers::{parse_complete_line, parse_error, parse_hm_time, IResult};
+use crate::{parse_weekdays_range, TmEditor, WeekDays};
 
 /// Time of Day (hour with minute)
 #[derive(Default, PartialEq, Clone, Debug)]
@@ -79,6 +84,49 @@ impl DailyDuration {
     }
 }
 
+/// Parse a [DailyDuration]
+pub fn parse_daily_duration(i: &str) -> Result<DailyDuration, Error> {
+    parse_complete_line("daily duration", i, parse_daily_duration_incomplete)
+}
+
+fn parse_daily_duration_incomplete(mut i: &str) -> IResult<&str, DailyDuration> {
+    let mut duration = DailyDuration::default();
+
+    if i.starts_with(|c: char| char::is_ascii_alphabetic(&c)) {
+        let (n, range_list) = context(
+            "weekday range list",
+            separated_nonempty_list(tag(","), parse_weekdays_range),
+        )(i)?;
+
+        i = space0(n)?.0;
+
+        for range in range_list {
+            duration.days.insert(range);
+        }
+    }
+
+    let (i, start) = parse_hm_time(i)?;
+
+    let i = space0(i)?.0;
+
+    let (i, _) = tag("-")(i)?;
+
+    let i = space0(i)?.0;
+
+    let end_time_start = i;
+
+    let (i, end) = parse_hm_time(i)?;
+
+    if start > end {
+        return Err(parse_error(end_time_start, "end time before start time"));
+    }
+
+    duration.start = start;
+    duration.end = end;
+
+    Ok((i, duration))
+}
+
 #[cfg(test)]
 mod test {
 
diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
index 00a9241..adaf7ac 100644
--- a/proxmox-time/src/parse_time.rs
+++ b/proxmox-time/src/parse_time.rs
@@ -375,46 +375,3 @@ fn parse_time_span_incomplete(mut i: &str) -> IResult<&str, TimeSpan> {
 
     Ok((i, ts))
 }
-
-/// Parse a [DailyDuration]
-pub fn parse_daily_duration(i: &str) -> Result<DailyDuration, Error> {
-    parse_complete_line("daily duration", i, parse_daily_duration_incomplete)
-}
-
-fn parse_daily_duration_incomplete(mut i: &str) -> IResult<&str, DailyDuration> {
-
-    let mut duration = DailyDuration::default();
-
-    if i.starts_with(|c: char| char::is_ascii_alphabetic(&c)) {
-
-        let (n, range_list) =  context(
-            "weekday range list",
-            separated_nonempty_list(tag(","), parse_weekdays_range)
-        )(i)?;
-
-        i = space0(n)?.0;
-
-        for range in range_list  { duration.days.insert(range); }
-    }
-
-    let (i, start) = parse_hm_time(i)?;
-
-    let i = space0(i)?.0;
-
-    let (i, _) = tag("-")(i)?;
-
-    let i = space0(i)?.0;
-
-    let end_time_start = i;
-
-    let (i, end) = parse_hm_time(i)?;
-
-    if start > end {
-        return Err(parse_error(end_time_start, "end time before start time"));
-    }
-
-    duration.start = start;
-    duration.end = end;
-
-    Ok((i, duration))
-}
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 07/14] proxmox-time: daily_duration.rs: rustfmt
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (5 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 06/14] proxmox-time: move parse_daily_duration to daily_duration.rs Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 08/14] proxmox-time: move CalendarEvent into calendar_events.rs Dominik Csapak
                   ` (8 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/daily_duration.rs | 76 ++++++++++++++++++------------
 1 file changed, 46 insertions(+), 30 deletions(-)

diff --git a/proxmox-time/src/daily_duration.rs b/proxmox-time/src/daily_duration.rs
index d5e0d90..54037ba 100644
--- a/proxmox-time/src/daily_duration.rs
+++ b/proxmox-time/src/daily_duration.rs
@@ -23,7 +23,7 @@ impl PartialOrd for HmTime {
     fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
         let mut order = self.hour.cmp(&other.hour);
         if order == Ordering::Equal {
-            order =  self.minute.cmp(&other.minute);
+            order = self.minute.cmp(&other.minute);
         }
         Some(order)
     }
@@ -39,10 +39,8 @@ pub struct DailyDuration {
 }
 
 impl DailyDuration {
-
     /// Test it time is within this frame
     pub fn time_match(&self, epoch: i64, utc: bool) -> Result<bool, Error> {
-
         let t = TmEditor::with_epoch(epoch, utc)?;
 
         Ok(self.time_match_with_tm_editor(&t))
@@ -55,18 +53,17 @@ impl DailyDuration {
     pub fn time_match_with_tm_editor(&self, t: &TmEditor) -> bool {
         let all_days = self.days.is_empty() || self.days.is_all();
 
-        if !all_days { // match day first
+        if !all_days {
+            // match day first
             match u32::try_from(t.day_num()) {
-                Ok(day_num) => {
-                    match WeekDays::from_bits(1<<day_num) {
-                        Some(day) => {
-                            if !self.days.contains(day) {
-                                return false;
-                            }
+                Ok(day_num) => match WeekDays::from_bits(1 << day_num) {
+                    Some(day) => {
+                        if !self.days.contains(day) {
+                            return false;
                         }
-                        None => return false,
                     }
-                }
+                    None => return false,
+                },
                 Err(_) => return false,
             }
         }
@@ -136,43 +133,62 @@ mod test {
 
     fn test_parse(
         duration_str: &str,
-        start_h: u32, start_m: u32,
-        end_h: u32, end_m: u32,
+        start_h: u32,
+        start_m: u32,
+        end_h: u32,
+        end_m: u32,
         days: &[usize],
     ) -> Result<(), Error> {
         let mut day_bits = 0;
-        for day in days { day_bits |= 1<<day; }
+        for day in days {
+            day_bits |= 1 << day;
+        }
         let expected_days = WeekDays::from_bits(day_bits).unwrap();
 
         let duration = parse_daily_duration(duration_str)?;
 
         if duration.start.hour != start_h {
-            bail!("start hour missmatch, extected {}, got {:?}", start_h, duration);
+            bail!(
+                "start hour missmatch, extected {}, got {:?}",
+                start_h,
+                duration
+            );
         }
         if duration.start.minute != start_m {
-            bail!("start minute missmatch, extected {}, got {:?}", start_m, duration);
+            bail!(
+                "start minute missmatch, extected {}, got {:?}",
+                start_m,
+                duration
+            );
         }
         if duration.end.hour != end_h {
             bail!("end hour missmatch, extected {}, got {:?}", end_h, duration);
         }
         if duration.end.minute != end_m {
-            bail!("end minute missmatch, extected {}, got {:?}", end_m, duration);
+            bail!(
+                "end minute missmatch, extected {}, got {:?}",
+                end_m,
+                duration
+            );
         }
 
         if duration.days != expected_days {
-            bail!("weekday missmatch, extected {:?}, got {:?}", expected_days, duration);
+            bail!(
+                "weekday missmatch, extected {:?}, got {:?}",
+                expected_days,
+                duration
+            );
         }
 
         Ok(())
     }
 
     const fn make_test_time(mday: i32, hour: i32, min: i32) -> i64 {
-        (mday*3600*24 + hour*3600 + min*60) as i64
+        (mday * 3600 * 24 + hour * 3600 + min * 60) as i64
     }
 
     #[test]
     fn test_daily_duration_parser() -> Result<(), Error> {
-
         assert!(parse_daily_duration("").is_err());
         assert!(parse_daily_duration(" 8-12").is_err());
         assert!(parse_daily_duration("8:60-12").is_err());
@@ -186,8 +202,8 @@ mod test {
         test_parse("8:05 - 12:20", 8, 5, 12, 20, &[])?;
 
         test_parse("mon 8-12", 8, 0, 12, 0, &[0])?;
-        test_parse("tue..fri 8-12", 8, 0, 12, 0, &[1,2,3,4])?;
-        test_parse("sat,tue..thu,fri 8-12", 8, 0, 12, 0, &[1,2,3,4,5])?;
+        test_parse("tue..fri 8-12", 8, 0, 12, 0, &[1, 2, 3, 4])?;
+        test_parse("sat,tue..thu,fri 8-12", 8, 0, 12, 0, &[1, 2, 3, 4, 5])?;
 
         Ok(())
     }
@@ -196,25 +212,25 @@ mod test {
     fn test_time_match() -> Result<(), Error> {
         const THURSDAY_80_00: i64 = make_test_time(0, 8, 0);
         const THURSDAY_12_00: i64 = make_test_time(0, 12, 0);
-        const DAY: i64 = 3600*24;
+        const DAY: i64 = 3600 * 24;
 
         let duration = parse_daily_duration("thu..fri 8:05-12")?;
 
         assert!(!duration.time_match(THURSDAY_80_00, true)?);
         assert!(!duration.time_match(THURSDAY_80_00 + DAY, true)?);
-        assert!(!duration.time_match(THURSDAY_80_00 + 2*DAY, true)?);
+        assert!(!duration.time_match(THURSDAY_80_00 + 2 * DAY, true)?);
 
-        assert!(duration.time_match(THURSDAY_80_00 + 5*60, true)?);
-        assert!(duration.time_match(THURSDAY_80_00 + 5*60 + DAY, true)?);
-        assert!(!duration.time_match(THURSDAY_80_00 + 5*60 + 2*DAY, true)?);
+        assert!(duration.time_match(THURSDAY_80_00 + 5 * 60, true)?);
+        assert!(duration.time_match(THURSDAY_80_00 + 5 * 60 + DAY, true)?);
+        assert!(!duration.time_match(THURSDAY_80_00 + 5 * 60 + 2 * DAY, true)?);
 
         assert!(duration.time_match(THURSDAY_12_00 - 1, true)?);
         assert!(duration.time_match(THURSDAY_12_00 - 1 + DAY, true)?);
-        assert!(!duration.time_match(THURSDAY_12_00 - 1 + 2*DAY, true)?);
+        assert!(!duration.time_match(THURSDAY_12_00 - 1 + 2 * DAY, true)?);
 
         assert!(!duration.time_match(THURSDAY_12_00, true)?);
         assert!(!duration.time_match(THURSDAY_12_00 + DAY, true)?);
-        assert!(!duration.time_match(THURSDAY_12_00 + 2*DAY, true)?);
+        assert!(!duration.time_match(THURSDAY_12_00 + 2 * DAY, true)?);
 
         Ok(())
     }
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 08/14] proxmox-time: move CalendarEvent into calendar_events.rs
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (6 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 07/14] proxmox-time: daily_duration.rs: rustfmt Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 09/14] proxmox-time: move TimeSpan into time_span.rs Dominik Csapak
                   ` (7 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

and all relevant parsing functions as well

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/calendar_event.rs | 429 +++++++++++++++++++++++++++++
 proxmox-time/src/lib.rs            |   3 +
 proxmox-time/src/parse_time.rs     | 220 ---------------
 proxmox-time/src/time.rs           | 160 +----------
 4 files changed, 433 insertions(+), 379 deletions(-)
 create mode 100644 proxmox-time/src/calendar_event.rs

diff --git a/proxmox-time/src/calendar_event.rs b/proxmox-time/src/calendar_event.rs
new file mode 100644
index 0000000..1c21a84
--- /dev/null
+++ b/proxmox-time/src/calendar_event.rs
@@ -0,0 +1,429 @@
+use std::convert::TryInto;
+
+use anyhow::Error;
+use nom::{
+    bytes::complete::tag,
+    character::complete::space0,
+    combinator::opt,
+    error::context,
+    multi::separated_nonempty_list,
+    sequence::{preceded, terminated, tuple},
+};
+
+use crate::date_time_value::DateTimeValue;
+use crate::parse_helpers::{parse_complete_line, parse_error, parse_time_comp, IResult};
+use crate::{parse_weekdays_range, TmEditor, WeekDays};
+
+/// Calendar events may be used to refer to one or more points in time in a
+/// single expression. They are designed after the systemd.time Calendar Events
+/// specification, but are not guaranteed to be 100% compatible.
+#[derive(Default, Clone, Debug)]
+pub struct CalendarEvent {
+    /// the days in a week this event should trigger
+    pub(crate) days: WeekDays,
+    /// the second(s) this event should trigger
+    pub(crate) second: Vec<DateTimeValue>, // todo: support float values
+    /// the minute(s) this event should trigger
+    pub(crate) minute: Vec<DateTimeValue>,
+    /// the hour(s) this event should trigger
+    pub(crate) hour: Vec<DateTimeValue>,
+    /// the day(s) in a month this event should trigger
+    pub(crate) day: Vec<DateTimeValue>,
+    /// the month(s) in a year this event should trigger
+    pub(crate) month: Vec<DateTimeValue>,
+    /// the years(s) this event should trigger
+    pub(crate) year: Vec<DateTimeValue>,
+}
+
+/// Verify the format of the [CalendarEvent]
+pub fn verify_calendar_event(i: &str) -> Result<(), Error> {
+    parse_calendar_event(i)?;
+    Ok(())
+}
+
+/// Compute the next event
+pub fn compute_next_event(
+    event: &CalendarEvent,
+    last: i64,
+    utc: bool,
+) -> Result<Option<i64>, Error> {
+
+    let last = last + 1; // at least one second later
+
+    let all_days = event.days.is_empty() || event.days.is_all();
+
+    let mut t = TmEditor::with_epoch(last, utc)?;
+
+    let mut count = 0;
+
+    loop {
+        // cancel after 1000 loops
+        if count > 1000 {
+            return Ok(None);
+        } else {
+            count += 1;
+        }
+
+        if !event.year.is_empty() {
+            let year: u32 = t.year().try_into()?;
+            if !DateTimeValue::list_contains(&event.year, year) {
+                if let Some(n) = DateTimeValue::find_next(&event.year, year) {
+                    t.add_years((n - year).try_into()?)?;
+                    continue;
+                } else {
+                    // if we have no valid year, we cannot find a correct timestamp
+                    return Ok(None);
+                }
+            }
+        }
+
+        if !event.month.is_empty() {
+            let month: u32 = t.month().try_into()?;
+            if !DateTimeValue::list_contains(&event.month, month) {
+                if let Some(n) = DateTimeValue::find_next(&event.month, month) {
+                    t.add_months((n - month).try_into()?)?;
+                } else {
+                    // if we could not find valid month, retry next year
+                    t.add_years(1)?;
+                }
+                continue;
+            }
+        }
+
+        if !event.day.is_empty() {
+            let day: u32 = t.day().try_into()?;
+            if !DateTimeValue::list_contains(&event.day, day) {
+                if let Some(n) = DateTimeValue::find_next(&event.day, day) {
+                    t.add_days((n - day).try_into()?)?;
+                } else {
+                    // if we could not find valid mday, retry next month
+                    t.add_months(1)?;
+                }
+                continue;
+            }
+        }
+
+        if !all_days { // match day first
+            let day_num: u32 = t.day_num().try_into()?;
+            let day = WeekDays::from_bits(1<<day_num).unwrap();
+            if !event.days.contains(day) {
+                if let Some(n) = ((day_num+1)..7)
+                    .find(|d| event.days.contains(WeekDays::from_bits(1<<d).unwrap()))
+                {
+                    // try next day
+                    t.add_days((n - day_num).try_into()?)?;
+                } else {
+                    // try next week
+                    t.add_days((7 - day_num).try_into()?)?;
+                }
+                continue;
+            }
+        }
+
+        // this day
+        if !event.hour.is_empty() {
+            let hour = t.hour().try_into()?;
+            if !DateTimeValue::list_contains(&event.hour, hour) {
+                if let Some(n) = DateTimeValue::find_next(&event.hour, hour) {
+                    // test next hour
+                    t.set_time(n.try_into()?, 0, 0)?;
+                } else {
+                    // test next day
+                    t.add_days(1)?;
+                }
+                continue;
+            }
+        }
+
+        // this hour
+        if !event.minute.is_empty() {
+            let minute = t.min().try_into()?;
+            if !DateTimeValue::list_contains(&event.minute, minute) {
+                if let Some(n) = DateTimeValue::find_next(&event.minute, minute) {
+                    // test next minute
+                    t.set_min_sec(n.try_into()?, 0)?;
+                } else {
+                    // test next hour
+                    t.set_time(t.hour() + 1, 0, 0)?;
+                }
+                continue;
+            }
+        }
+
+        // this minute
+        if !event.second.is_empty() {
+            let second = t.sec().try_into()?;
+            if !DateTimeValue::list_contains(&event.second, second) {
+                if let Some(n) = DateTimeValue::find_next(&event.second, second) {
+                    // test next second
+                    t.set_sec(n.try_into()?)?;
+                } else {
+                    // test next min
+                    t.set_min_sec(t.min() + 1, 0)?;
+                }
+                continue;
+            }
+        }
+
+        let next = t.into_epoch()?;
+        return Ok(Some(next))
+    }
+}
+
+/// Parse a [CalendarEvent]
+pub fn parse_calendar_event(i: &str) -> Result<CalendarEvent, Error> {
+    parse_complete_line("calendar event", i, parse_calendar_event_incomplete)
+}
+
+fn parse_calendar_event_incomplete(mut i: &str) -> IResult<&str, CalendarEvent> {
+    let mut has_dayspec = false;
+    let mut has_timespec = false;
+    let mut has_datespec = false;
+
+    let mut event = CalendarEvent::default();
+
+    if i.starts_with(|c: char| char::is_ascii_alphabetic(&c)) {
+        match i {
+            "minutely" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        second: vec![DateTimeValue::Single(0)],
+                        ..Default::default()
+                    },
+                ));
+            }
+            "hourly" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        minute: vec![DateTimeValue::Single(0)],
+                        second: vec![DateTimeValue::Single(0)],
+                        ..Default::default()
+                    },
+                ));
+            }
+            "daily" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        hour: vec![DateTimeValue::Single(0)],
+                        minute: vec![DateTimeValue::Single(0)],
+                        second: vec![DateTimeValue::Single(0)],
+                        ..Default::default()
+                    },
+                ));
+            }
+            "weekly" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        hour: vec![DateTimeValue::Single(0)],
+                        minute: vec![DateTimeValue::Single(0)],
+                        second: vec![DateTimeValue::Single(0)],
+                        days: WeekDays::MONDAY,
+                        ..Default::default()
+                    },
+                ));
+            }
+            "monthly" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        hour: vec![DateTimeValue::Single(0)],
+                        minute: vec![DateTimeValue::Single(0)],
+                        second: vec![DateTimeValue::Single(0)],
+                        day: vec![DateTimeValue::Single(1)],
+                        ..Default::default()
+                    },
+                ));
+            }
+            "yearly" | "annually" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        hour: vec![DateTimeValue::Single(0)],
+                        minute: vec![DateTimeValue::Single(0)],
+                        second: vec![DateTimeValue::Single(0)],
+                        day: vec![DateTimeValue::Single(1)],
+                        month: vec![DateTimeValue::Single(1)],
+                        ..Default::default()
+                    },
+                ));
+            }
+            "quarterly" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        hour: vec![DateTimeValue::Single(0)],
+                        minute: vec![DateTimeValue::Single(0)],
+                        second: vec![DateTimeValue::Single(0)],
+                        day: vec![DateTimeValue::Single(1)],
+                        month: vec![
+                            DateTimeValue::Single(1),
+                            DateTimeValue::Single(4),
+                            DateTimeValue::Single(7),
+                            DateTimeValue::Single(10),
+                        ],
+                        ..Default::default()
+                    },
+                ));
+            }
+            "semiannually" | "semi-annually" => {
+                return Ok((
+                    "",
+                    CalendarEvent {
+                        hour: vec![DateTimeValue::Single(0)],
+                        minute: vec![DateTimeValue::Single(0)],
+                        second: vec![DateTimeValue::Single(0)],
+                        day: vec![DateTimeValue::Single(1)],
+                        month: vec![DateTimeValue::Single(1), DateTimeValue::Single(7)],
+                        ..Default::default()
+                    },
+                ));
+            }
+            _ => { /* continue */ }
+        }
+
+        let (n, range_list) = context(
+            "weekday range list",
+            separated_nonempty_list(tag(","), parse_weekdays_range),
+        )(i)?;
+
+        has_dayspec = true;
+
+        i = space0(n)?.0;
+
+        for range in range_list {
+            event.days.insert(range);
+        }
+    }
+
+    if let (n, Some(date)) = opt(parse_date_spec)(i)? {
+        event.year = date.year;
+        event.month = date.month;
+        event.day = date.day;
+        has_datespec = true;
+        i = space0(n)?.0;
+    }
+
+    if let (n, Some(time)) = opt(parse_time_spec)(i)? {
+        event.hour = time.hour;
+        event.minute = time.minute;
+        event.second = time.second;
+        has_timespec = true;
+        i = n;
+    } else {
+        event.hour = vec![DateTimeValue::Single(0)];
+        event.minute = vec![DateTimeValue::Single(0)];
+        event.second = vec![DateTimeValue::Single(0)];
+    }
+
+    if !(has_dayspec || has_timespec || has_datespec) {
+        return Err(parse_error(i, "date or time specification"));
+    }
+
+    Ok((i, event))
+}
+
+struct TimeSpec {
+    hour: Vec<DateTimeValue>,
+    minute: Vec<DateTimeValue>,
+    second: Vec<DateTimeValue>,
+}
+
+struct DateSpec {
+    year: Vec<DateTimeValue>,
+    month: Vec<DateTimeValue>,
+    day: Vec<DateTimeValue>,
+}
+
+fn parse_date_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, DateTimeValue> {
+    move |i: &str| {
+        let (i, value) = parse_time_comp(max)(i)?;
+
+        if let (i, Some(end)) = opt(preceded(tag(".."), parse_time_comp(max)))(i)? {
+            if value > end {
+                return Err(parse_error(i, "range start is bigger than end"));
+            }
+            if let Some(time) = i.strip_prefix('/') {
+                let (time, repeat) = parse_time_comp(max)(time)?;
+                return Ok((time, DateTimeValue::Repeated(value, repeat, Some(end))));
+            }
+            return Ok((i, DateTimeValue::Range(value, end)));
+        }
+
+        if let Some(time) = i.strip_prefix('/') {
+            let (time, repeat) = parse_time_comp(max)(time)?;
+            Ok((time, DateTimeValue::Repeated(value, repeat, None)))
+        } else {
+            Ok((i, DateTimeValue::Single(value)))
+        }
+    }
+}
+
+fn parse_date_time_comp_list(
+    start: u32,
+    max: usize,
+) -> impl Fn(&str) -> IResult<&str, Vec<DateTimeValue>> {
+    move |i: &str| {
+        if let Some(rest) = i.strip_prefix('*') {
+            if let Some(time) = rest.strip_prefix('/') {
+                let (n, repeat) = parse_time_comp(max)(time)?;
+                if repeat > 0 {
+                    return Ok((n, vec![DateTimeValue::Repeated(start, repeat, None)]));
+                }
+            }
+            return Ok((rest, Vec::new()));
+        }
+
+        separated_nonempty_list(tag(","), parse_date_time_comp(max))(i)
+    }
+}
+
+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(":"))),
+        parse_date_time_comp_list(0, 60),
+        opt(preceded(tag(":"), parse_date_time_comp_list(0, 60))),
+    ))(i)?;
+
+    let hour = opt_hour.unwrap_or_else(Vec::new);
+    let second = opt_second.unwrap_or_else(|| vec![DateTimeValue::Single(0)]);
+
+    Ok((
+        i,
+        TimeSpec {
+            hour,
+            minute,
+            second,
+        },
+    ))
+}
+
+fn parse_date_spec(i: &str) -> IResult<&str, DateSpec> {
+    // TODO: implement ~ for days (man systemd.time)
+    if let Ok((i, (year, month, day))) = tuple((
+        parse_date_time_comp_list(0, 2200), // the upper limit for systemd, stay compatible
+        preceded(tag("-"), parse_date_time_comp_list(1, 13)),
+        preceded(tag("-"), parse_date_time_comp_list(1, 32)),
+    ))(i)
+    {
+        Ok((i, DateSpec { year, month, day }))
+    } else if let Ok((i, (month, day))) = tuple((
+        parse_date_time_comp_list(1, 13),
+        preceded(tag("-"), parse_date_time_comp_list(1, 32)),
+    ))(i)
+    {
+        Ok((
+            i,
+            DateSpec {
+                year: Vec::new(),
+                month,
+                day,
+            },
+        ))
+    } else {
+        Err(parse_error(i, "invalid date spec"))
+    }
+}
diff --git a/proxmox-time/src/lib.rs b/proxmox-time/src/lib.rs
index 3d888a2..0436628 100644
--- a/proxmox-time/src/lib.rs
+++ b/proxmox-time/src/lib.rs
@@ -17,6 +17,9 @@ pub(crate) mod parse_helpers;
 
 pub(crate) mod date_time_value;
 
+mod calendar_event;
+pub use calendar_event::*;
+
 mod week_days;
 pub use week_days::*;
 
diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
index adaf7ac..51c0486 100644
--- a/proxmox-time/src/parse_time.rs
+++ b/proxmox-time/src/parse_time.rs
@@ -88,226 +88,6 @@ lazy_static! {
     };
 }
 
-struct TimeSpec {
-    hour: Vec<DateTimeValue>,
-    minute: Vec<DateTimeValue>,
-    second: Vec<DateTimeValue>,
-}
-
-struct DateSpec {
-    year: Vec<DateTimeValue>,
-    month: Vec<DateTimeValue>,
-    day: Vec<DateTimeValue>,
-}
-
-fn parse_date_time_comp(max: usize) -> impl Fn(&str) -> IResult<&str, DateTimeValue> {
-    move |i: &str| {
-        let (i, value) = parse_time_comp(max)(i)?;
-
-        if let (i, Some(end)) = opt(preceded(tag(".."), parse_time_comp(max)))(i)? {
-            if value > end {
-                return Err(parse_error(i, "range start is bigger than end"));
-            }
-            if let Some(time) = i.strip_prefix('/') {
-                let (time, repeat) = parse_time_comp(max)(time)?;
-                return Ok((time, DateTimeValue::Repeated(value, repeat, Some(end))));
-            }
-            return Ok((i, DateTimeValue::Range(value, end)));
-        }
-
-        if let Some(time) = i.strip_prefix('/') {
-            let (time, repeat) = parse_time_comp(max)(time)?;
-            Ok((time, DateTimeValue::Repeated(value, repeat, None)))
-        } else {
-            Ok((i, DateTimeValue::Single(value)))
-        }
-    }
-}
-
-fn parse_date_time_comp_list(start: u32, max: usize) -> impl Fn(&str) -> IResult<&str, Vec<DateTimeValue>> {
-    move |i: &str| {
-        if let Some(rest) = i.strip_prefix('*') {
-            if let Some(time) = rest.strip_prefix('/') {
-                let (n, repeat) = parse_time_comp(max)(time)?;
-                if repeat > 0 {
-                    return Ok((n, vec![DateTimeValue::Repeated(start, repeat, None)]));
-                }
-            }
-            return Ok((rest, Vec::new()));
-        }
-
-        separated_nonempty_list(tag(","), parse_date_time_comp(max))(i)
-    }
-}
-
-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(":"))),
-        parse_date_time_comp_list(0, 60),
-        opt(preceded(tag(":"), parse_date_time_comp_list(0, 60))),
-    ))(i)?;
-
-    let hour = opt_hour.unwrap_or_else(Vec::new);
-    let second = opt_second.unwrap_or_else(|| vec![DateTimeValue::Single(0)]);
-
-    Ok((i, TimeSpec { hour, minute, second }))
-}
-
-fn parse_date_spec(i: &str) -> IResult<&str, DateSpec> {
-
-    // TODO: implement ~ for days (man systemd.time)
-    if let Ok((i, (year, month, day))) = tuple((
-        parse_date_time_comp_list(0, 2200), // the upper limit for systemd, stay compatible
-        preceded(tag("-"), parse_date_time_comp_list(1, 13)),
-        preceded(tag("-"), parse_date_time_comp_list(1, 32)),
-    ))(i) {
-        Ok((i, DateSpec { year, month, day }))
-    } else if let Ok((i, (month, day))) = tuple((
-        parse_date_time_comp_list(1, 13),
-        preceded(tag("-"), parse_date_time_comp_list(1, 32)),
-    ))(i) {
-        Ok((i, DateSpec { year: Vec::new(), month, day }))
-    } else {
-        Err(parse_error(i, "invalid date spec"))
-    }
-}
-
-/// Parse a [CalendarEvent]
-pub fn parse_calendar_event(i: &str) -> Result<CalendarEvent, Error> {
-    parse_complete_line("calendar event", i, parse_calendar_event_incomplete)
-}
-
-fn parse_calendar_event_incomplete(mut i: &str) -> IResult<&str, CalendarEvent> {
-
-    let mut has_dayspec = false;
-    let mut has_timespec = false;
-    let mut has_datespec = false;
-
-    let mut event = CalendarEvent::default();
-
-    if i.starts_with(|c: char| char::is_ascii_alphabetic(&c)) {
-
-        match i {
-            "minutely" => {
-                return Ok(("", CalendarEvent {
-                    second: vec![DateTimeValue::Single(0)],
-                    ..Default::default()
-                }));
-            }
-            "hourly" => {
-                return Ok(("", CalendarEvent {
-                    minute: vec![DateTimeValue::Single(0)],
-                    second: vec![DateTimeValue::Single(0)],
-                    ..Default::default()
-                }));
-            }
-            "daily" => {
-                return Ok(("", CalendarEvent {
-                    hour: vec![DateTimeValue::Single(0)],
-                    minute: vec![DateTimeValue::Single(0)],
-                    second: vec![DateTimeValue::Single(0)],
-                    ..Default::default()
-                }));
-            }
-            "weekly" => {
-                return Ok(("", CalendarEvent {
-                    hour: vec![DateTimeValue::Single(0)],
-                    minute: vec![DateTimeValue::Single(0)],
-                    second: vec![DateTimeValue::Single(0)],
-                    days: WeekDays::MONDAY,
-                    ..Default::default()
-                }));
-            }
-            "monthly" => {
-                return Ok(("", CalendarEvent {
-                    hour: vec![DateTimeValue::Single(0)],
-                    minute: vec![DateTimeValue::Single(0)],
-                    second: vec![DateTimeValue::Single(0)],
-                    day: vec![DateTimeValue::Single(1)],
-                    ..Default::default()
-                }));
-            }
-            "yearly" | "annually" => {
-                return Ok(("", CalendarEvent {
-                    hour: vec![DateTimeValue::Single(0)],
-                    minute: vec![DateTimeValue::Single(0)],
-                    second: vec![DateTimeValue::Single(0)],
-                    day: vec![DateTimeValue::Single(1)],
-                    month: vec![DateTimeValue::Single(1)],
-                    ..Default::default()
-                }));
-            }
-            "quarterly" => {
-                return Ok(("", CalendarEvent {
-                    hour: vec![DateTimeValue::Single(0)],
-                    minute: vec![DateTimeValue::Single(0)],
-                    second: vec![DateTimeValue::Single(0)],
-                    day: vec![DateTimeValue::Single(1)],
-                    month: vec![
-                        DateTimeValue::Single(1),
-                        DateTimeValue::Single(4),
-                        DateTimeValue::Single(7),
-                        DateTimeValue::Single(10),
-                    ],
-                    ..Default::default()
-                }));
-            }
-            "semiannually" | "semi-annually" => {
-                return Ok(("", CalendarEvent {
-                    hour: vec![DateTimeValue::Single(0)],
-                    minute: vec![DateTimeValue::Single(0)],
-                    second: vec![DateTimeValue::Single(0)],
-                    day: vec![DateTimeValue::Single(1)],
-                    month: vec![
-                        DateTimeValue::Single(1),
-                        DateTimeValue::Single(7),
-                    ],
-                    ..Default::default()
-                }));
-            }
-            _ => { /* continue */ }
-        }
-
-        let (n, range_list) =  context(
-            "weekday range list",
-            separated_nonempty_list(tag(","), parse_weekdays_range)
-        )(i)?;
-
-        has_dayspec = true;
-
-        i = space0(n)?.0;
-
-        for range in range_list  { event.days.insert(range); }
-    }
-
-    if let (n, Some(date)) = opt(parse_date_spec)(i)? {
-        event.year = date.year;
-        event.month = date.month;
-        event.day = date.day;
-        has_datespec = true;
-        i = space0(n)?.0;
-    }
-
-    if let (n, Some(time)) = opt(parse_time_spec)(i)? {
-        event.hour = time.hour;
-        event.minute = time.minute;
-        event.second = time.second;
-        has_timespec = true;
-        i = n;
-    } else {
-        event.hour = vec![DateTimeValue::Single(0)];
-        event.minute = vec![DateTimeValue::Single(0)];
-        event.second = vec![DateTimeValue::Single(0)];
-    }
-
-    if !(has_dayspec || has_timespec || has_datespec) {
-        return Err(parse_error(i, "date or time specification"));
-    }
-
-    Ok((i, event))
-}
-
 fn parse_time_unit(i: &str) ->  IResult<&str, &str> {
     let (n, text) = take_while1(|c: char| char::is_ascii_alphabetic(&c) || c == 'µ')(i)?;
     if TIME_SPAN_UNITS.contains_key(&text) {
diff --git a/proxmox-time/src/time.rs b/proxmox-time/src/time.rs
index 7b9625f..1c45a09 100644
--- a/proxmox-time/src/time.rs
+++ b/proxmox-time/src/time.rs
@@ -1,33 +1,10 @@
-use std::convert::TryInto;
-
 use anyhow::Error;
 
 use crate::date_time_value::DateTimeValue;
 use crate::TmEditor;
 use crate::WeekDays;
 
-use crate::{parse_calendar_event, parse_time_span};
-
-/// Calendar events may be used to refer to one or more points in time in a
-/// single expression. They are designed after the systemd.time Calendar Events
-/// specification, but are not guaranteed to be 100% compatible.
-#[derive(Default, Clone, Debug)]
-pub struct CalendarEvent {
-    /// the days in a week this event should trigger
-    pub(crate) days: WeekDays,
-    /// the second(s) this event should trigger
-    pub(crate) second: Vec<DateTimeValue>, // todo: support float values
-    /// the minute(s) this event should trigger
-    pub(crate) minute: Vec<DateTimeValue>,
-    /// the hour(s) this event should trigger
-    pub(crate) hour: Vec<DateTimeValue>,
-    /// the day(s) in a month this event should trigger
-    pub(crate) day: Vec<DateTimeValue>,
-    /// the month(s) in a year this event should trigger
-    pub(crate) month: Vec<DateTimeValue>,
-    /// the years(s) this event should trigger
-    pub(crate) year: Vec<DateTimeValue>,
-}
+use crate::{compute_next_event, parse_calendar_event, parse_time_span};
 
 /// A time spans defines a time duration
 #[derive(Default, Clone, Debug)]
@@ -148,141 +125,6 @@ pub fn verify_time_span(i: &str) -> Result<(), Error> {
     Ok(())
 }
 
-/// Verify the format of the [CalendarEvent]
-pub fn verify_calendar_event(i: &str) -> Result<(), Error> {
-    parse_calendar_event(i)?;
-    Ok(())
-}
-
-/// Compute the next event
-pub fn compute_next_event(
-    event: &CalendarEvent,
-    last: i64,
-    utc: bool,
-) -> Result<Option<i64>, Error> {
-
-    let last = last + 1; // at least one second later
-
-    let all_days = event.days.is_empty() || event.days.is_all();
-
-    let mut t = TmEditor::with_epoch(last, utc)?;
-
-    let mut count = 0;
-
-    loop {
-        // cancel after 1000 loops
-        if count > 1000 {
-            return Ok(None);
-        } else {
-            count += 1;
-        }
-
-        if !event.year.is_empty() {
-            let year: u32 = t.year().try_into()?;
-            if !DateTimeValue::list_contains(&event.year, year) {
-                if let Some(n) = DateTimeValue::find_next(&event.year, year) {
-                    t.add_years((n - year).try_into()?)?;
-                    continue;
-                } else {
-                    // if we have no valid year, we cannot find a correct timestamp
-                    return Ok(None);
-                }
-            }
-        }
-
-        if !event.month.is_empty() {
-            let month: u32 = t.month().try_into()?;
-            if !DateTimeValue::list_contains(&event.month, month) {
-                if let Some(n) = DateTimeValue::find_next(&event.month, month) {
-                    t.add_months((n - month).try_into()?)?;
-                } else {
-                    // if we could not find valid month, retry next year
-                    t.add_years(1)?;
-                }
-                continue;
-            }
-        }
-
-        if !event.day.is_empty() {
-            let day: u32 = t.day().try_into()?;
-            if !DateTimeValue::list_contains(&event.day, day) {
-                if let Some(n) = DateTimeValue::find_next(&event.day, day) {
-                    t.add_days((n - day).try_into()?)?;
-                } else {
-                    // if we could not find valid mday, retry next month
-                    t.add_months(1)?;
-                }
-                continue;
-            }
-        }
-
-        if !all_days { // match day first
-            let day_num: u32 = t.day_num().try_into()?;
-            let day = WeekDays::from_bits(1<<day_num).unwrap();
-            if !event.days.contains(day) {
-                if let Some(n) = ((day_num+1)..7)
-                    .find(|d| event.days.contains(WeekDays::from_bits(1<<d).unwrap()))
-                {
-                    // try next day
-                    t.add_days((n - day_num).try_into()?)?;
-                } else {
-                    // try next week
-                    t.add_days((7 - day_num).try_into()?)?;
-                }
-                continue;
-            }
-        }
-
-        // this day
-        if !event.hour.is_empty() {
-            let hour = t.hour().try_into()?;
-            if !DateTimeValue::list_contains(&event.hour, hour) {
-                if let Some(n) = DateTimeValue::find_next(&event.hour, hour) {
-                    // test next hour
-                    t.set_time(n.try_into()?, 0, 0)?;
-                } else {
-                    // test next day
-                    t.add_days(1)?;
-                }
-                continue;
-            }
-        }
-
-        // this hour
-        if !event.minute.is_empty() {
-            let minute = t.min().try_into()?;
-            if !DateTimeValue::list_contains(&event.minute, minute) {
-                if let Some(n) = DateTimeValue::find_next(&event.minute, minute) {
-                    // test next minute
-                    t.set_min_sec(n.try_into()?, 0)?;
-                } else {
-                    // test next hour
-                    t.set_time(t.hour() + 1, 0, 0)?;
-                }
-                continue;
-            }
-        }
-
-        // this minute
-        if !event.second.is_empty() {
-            let second = t.sec().try_into()?;
-            if !DateTimeValue::list_contains(&event.second, second) {
-                if let Some(n) = DateTimeValue::find_next(&event.second, second) {
-                    // test next second
-                    t.set_sec(n.try_into()?)?;
-                } else {
-                    // test next min
-                    t.set_min_sec(t.min() + 1, 0)?;
-                }
-                continue;
-            }
-        }
-
-        let next = t.into_epoch()?;
-        return Ok(Some(next))
-    }
-}
-
 #[cfg(test)]
 mod test {
 
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 09/14] proxmox-time: move TimeSpan into time_span.rs
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (7 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 08/14] proxmox-time: move CalendarEvent into calendar_events.rs Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 10/14] proxmox-time: move tests from time.rs to test.rs Dominik Csapak
                   ` (6 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

and related parsing functions

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/lib.rs        |   6 +-
 proxmox-time/src/parse_time.rs | 157 -------------------
 proxmox-time/src/time.rs       | 129 +---------------
 proxmox-time/src/time_span.rs  | 265 +++++++++++++++++++++++++++++++++
 4 files changed, 269 insertions(+), 288 deletions(-)
 delete mode 100644 proxmox-time/src/parse_time.rs
 create mode 100644 proxmox-time/src/time_span.rs

diff --git a/proxmox-time/src/lib.rs b/proxmox-time/src/lib.rs
index 0436628..cbb7008 100644
--- a/proxmox-time/src/lib.rs
+++ b/proxmox-time/src/lib.rs
@@ -7,9 +7,6 @@ use anyhow::{bail, format_err, Error};
 mod tm_editor;
 pub use tm_editor::*;
 
-mod parse_time;
-pub use parse_time::*;
-
 mod time;
 pub use time::*;
 
@@ -20,6 +17,9 @@ pub(crate) mod date_time_value;
 mod calendar_event;
 pub use calendar_event::*;
 
+mod time_span;
+pub use time_span::*;
+
 mod week_days;
 pub use week_days::*;
 
diff --git a/proxmox-time/src/parse_time.rs b/proxmox-time/src/parse_time.rs
deleted file mode 100644
index 51c0486..0000000
--- a/proxmox-time/src/parse_time.rs
+++ /dev/null
@@ -1,157 +0,0 @@
-use std::collections::HashMap;
-
-use anyhow::{bail, Error};
-use lazy_static::lazy_static;
-
-use super::time::*;
-use super::daily_duration::*;
-
-use nom::{
-    error::{context, ParseError, VerboseError},
-    bytes::complete::{tag, take_while1},
-    combinator::{map_res, all_consuming, opt, recognize},
-    sequence::{pair, preceded, terminated, tuple},
-    character::complete::{alpha1, space0, digit1},
-    multi::separated_nonempty_list,
-};
-
-use crate::parse_helpers::{parse_complete_line, parse_error, parse_hm_time, parse_time_comp, parse_u64, IResult};
-use crate::{parse_weekdays_range, WeekDays};
-use crate::date_time_value::DateTimeValue;
-
-lazy_static! {
-    static ref TIME_SPAN_UNITS: HashMap<&'static str, f64> = {
-        let mut map = HashMap::new();
-
-        let second = 1.0;
-
-        map.insert("seconds", second);
-        map.insert("second", second);
-        map.insert("sec", second);
-        map.insert("s", second);
-
-        let msec = second / 1000.0;
-
-        map.insert("msec", msec);
-        map.insert("ms", msec);
-
-        let usec = msec / 1000.0;
-
-        map.insert("usec", usec);
-        map.insert("us", usec);
-        map.insert("µs", usec);
-
-        let nsec = usec / 1000.0;
-
-        map.insert("nsec", nsec);
-        map.insert("ns", nsec);
-
-        let minute = second * 60.0;
-
-        map.insert("minutes", minute);
-        map.insert("minute", minute);
-        map.insert("min", minute);
-        map.insert("m", minute);
-
-        let hour = minute * 60.0;
-
-        map.insert("hours", hour);
-        map.insert("hour", hour);
-        map.insert("hr", hour);
-        map.insert("h", hour);
-
-        let day = hour * 24.0 ;
-
-        map.insert("days", day);
-        map.insert("day", day);
-        map.insert("d", day);
-
-        let week = day * 7.0;
-
-        map.insert("weeks", week);
-        map.insert("week", week);
-        map.insert("w", week);
-
-        let month = 30.44 * day;
-
-        map.insert("months", month);
-        map.insert("month", month);
-        map.insert("M", month);
-
-        let year = 365.25 * day;
-
-        map.insert("years", year);
-        map.insert("year", year);
-        map.insert("y", year);
-
-        map
-    };
-}
-
-fn parse_time_unit(i: &str) ->  IResult<&str, &str> {
-    let (n, text) = take_while1(|c: char| char::is_ascii_alphabetic(&c) || c == 'µ')(i)?;
-    if TIME_SPAN_UNITS.contains_key(&text) {
-        Ok((n, text))
-    } else {
-        Err(parse_error(text, "time unit"))
-    }
-}
-
-
-/// Parse a [TimeSpan]
-pub fn parse_time_span(i: &str) -> Result<TimeSpan, Error> {
-    parse_complete_line("time span", i, parse_time_span_incomplete)
-}
-
-fn parse_time_span_incomplete(mut i: &str) -> IResult<&str, TimeSpan> {
-
-    let mut ts = TimeSpan::default();
-
-    loop {
-        i = space0(i)?.0;
-        if i.is_empty() { break; }
-        let (n, num) = parse_u64(i)?;
-        i = space0(n)?.0;
-
-        if let (n, Some(unit)) = opt(parse_time_unit)(i)? {
-            i = n;
-            match unit {
-                "seconds" | "second" | "sec" | "s" => {
-                    ts.seconds += num;
-                }
-                "msec" | "ms" => {
-                    ts.msec += num;
-                }
-                "usec" | "us" | "µs" => {
-                    ts.usec += num;
-                }
-                "nsec" | "ns" => {
-                    ts.nsec += num;
-                }
-                "minutes" | "minute" | "min" | "m" => {
-                    ts.minutes += num;
-                }
-                "hours" | "hour" | "hr" | "h" => {
-                    ts.hours += num;
-                }
-                "days" | "day" | "d" => {
-                    ts.days += num;
-                }
-                "weeks" | "week" | "w" => {
-                    ts.weeks += num;
-                }
-                "months" | "month" | "M" => {
-                    ts.months += num;
-                }
-                "years" | "year" | "y" => {
-                    ts.years += num;
-                }
-                _ => return Err(parse_error(unit, "internal error")),
-            }
-        } else {
-            ts.seconds += num;
-        }
-    }
-
-    Ok((i, ts))
-}
diff --git a/proxmox-time/src/time.rs b/proxmox-time/src/time.rs
index 1c45a09..ac2f510 100644
--- a/proxmox-time/src/time.rs
+++ b/proxmox-time/src/time.rs
@@ -1,136 +1,9 @@
-use anyhow::Error;
-
-use crate::date_time_value::DateTimeValue;
-use crate::TmEditor;
-use crate::WeekDays;
-
-use crate::{compute_next_event, parse_calendar_event, parse_time_span};
-
-/// A time spans defines a time duration
-#[derive(Default, Clone, Debug)]
-pub struct TimeSpan {
-    pub nsec: u64,
-    pub usec: u64,
-    pub msec: u64,
-    pub seconds: u64,
-    pub minutes: u64,
-    pub hours: u64,
-    pub days: u64,
-    pub weeks: u64,
-    pub months: u64,
-    pub years: u64,
-}
-
-impl From<TimeSpan> for f64 {
-    fn from(ts: TimeSpan) -> Self {
-        (ts.seconds as f64) +
-            ((ts.nsec as f64) / 1_000_000_000.0)  +
-            ((ts.usec as f64) / 1_000_000.0)  +
-            ((ts.msec as f64) / 1_000.0)  +
-            ((ts.minutes as f64) * 60.0)  +
-            ((ts.hours as f64) * 3600.0)  +
-            ((ts.days as f64) * 3600.0 * 24.0)  +
-            ((ts.weeks as f64) * 3600.0 * 24.0 * 7.0)  +
-            ((ts.months as f64) * 3600.0 * 24.0 * 30.44)  +
-            ((ts.years as f64) * 3600.0 * 24.0 * 365.25)
-    }
-}
-
-impl From<std::time::Duration> for TimeSpan {
-    fn from(duration: std::time::Duration) -> Self {
-        let mut duration = duration.as_nanos();
-        let nsec = (duration % 1000) as u64;
-        duration /= 1000;
-        let usec = (duration % 1000) as u64;
-        duration /= 1000;
-        let msec = (duration % 1000) as u64;
-        duration /= 1000;
-        let seconds = (duration % 60) as u64;
-        duration /= 60;
-        let minutes = (duration % 60) as u64;
-        duration /= 60;
-        let hours = (duration % 24) as u64;
-        duration /= 24;
-        let years = (duration as f64 / 365.25) as u64;
-        let ydays = (duration as f64 % 365.25) as u64;
-        let months = (ydays as f64 / 30.44) as u64;
-        let mdays = (ydays as f64 % 30.44) as u64;
-        let weeks = mdays / 7;
-        let days = mdays % 7;
-        Self {
-            nsec,
-            usec,
-            msec,
-            seconds,
-            minutes,
-            hours,
-            days,
-            weeks,
-            months,
-            years,
-        }
-    }
-}
-
-impl std::fmt::Display for TimeSpan {
-    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
-        let mut first = true;
-        { // block scope for mutable borrows
-            let mut do_write = |v: u64, unit: &str| -> Result<(), std::fmt::Error> {
-                if !first {
-                    write!(f, " ")?;
-                }
-                first = false;
-                write!(f, "{}{}", v, unit)
-            };
-            if self.years > 0 {
-                do_write(self.years, "y")?;
-            }
-            if self.months > 0 {
-                do_write(self.months, "m")?;
-            }
-            if self.weeks > 0 {
-                do_write(self.weeks, "w")?;
-            }
-            if self.days > 0 {
-                do_write(self.days, "d")?;
-            }
-            if self.hours > 0 {
-                do_write(self.hours, "h")?;
-            }
-            if self.minutes > 0 {
-                do_write(self.minutes, "min")?;
-            }
-        }
-        if !first {
-            write!(f, " ")?;
-        }
-        let seconds = self.seconds as f64 + (self.msec as f64 / 1000.0);
-        if seconds >= 0.1 {
-            if seconds >= 1.0 || !first {
-                write!(f, "{:.0}s", seconds)?;
-            } else {
-                write!(f, "{:.1}s", seconds)?;
-            }
-        } else if first {
-            write!(f, "<0.1s")?;
-        }
-        Ok(())
-    }
-}
-
-/// Verify the format of the [TimeSpan]
-pub fn verify_time_span(i: &str) -> Result<(), Error> {
-    parse_time_span(i)?;
-    Ok(())
-}
-
 #[cfg(test)]
 mod test {
 
     use anyhow::bail;
+    use crate::*;
 
-    use super::*;
 
     fn test_event(v: &'static str) -> Result<(), Error> {
         match parse_calendar_event(v) {
diff --git a/proxmox-time/src/time_span.rs b/proxmox-time/src/time_span.rs
new file mode 100644
index 0000000..2f8cf59
--- /dev/null
+++ b/proxmox-time/src/time_span.rs
@@ -0,0 +1,265 @@
+use std::collections::HashMap;
+
+use anyhow::Error;
+use lazy_static::lazy_static;
+use nom::{bytes::complete::take_while1, character::complete::space0, combinator::opt};
+
+use crate::parse_helpers::{parse_complete_line, parse_error, parse_u64, IResult};
+
+lazy_static! {
+    static ref TIME_SPAN_UNITS: HashMap<&'static str, f64> = {
+        let mut map = HashMap::new();
+
+        let second = 1.0;
+
+        map.insert("seconds", second);
+        map.insert("second", second);
+        map.insert("sec", second);
+        map.insert("s", second);
+
+        let msec = second / 1000.0;
+
+        map.insert("msec", msec);
+        map.insert("ms", msec);
+
+        let usec = msec / 1000.0;
+
+        map.insert("usec", usec);
+        map.insert("us", usec);
+        map.insert("µs", usec);
+
+        let nsec = usec / 1000.0;
+
+        map.insert("nsec", nsec);
+        map.insert("ns", nsec);
+
+        let minute = second * 60.0;
+
+        map.insert("minutes", minute);
+        map.insert("minute", minute);
+        map.insert("min", minute);
+        map.insert("m", minute);
+
+        let hour = minute * 60.0;
+
+        map.insert("hours", hour);
+        map.insert("hour", hour);
+        map.insert("hr", hour);
+        map.insert("h", hour);
+
+        let day = hour * 24.0;
+
+        map.insert("days", day);
+        map.insert("day", day);
+        map.insert("d", day);
+
+        let week = day * 7.0;
+
+        map.insert("weeks", week);
+        map.insert("week", week);
+        map.insert("w", week);
+
+        let month = 30.44 * day;
+
+        map.insert("months", month);
+        map.insert("month", month);
+        map.insert("M", month);
+
+        let year = 365.25 * day;
+
+        map.insert("years", year);
+        map.insert("year", year);
+        map.insert("y", year);
+
+        map
+    };
+}
+
+/// A time spans defines a time duration
+#[derive(Default, Clone, Debug)]
+pub struct TimeSpan {
+    pub nsec: u64,
+    pub usec: u64,
+    pub msec: u64,
+    pub seconds: u64,
+    pub minutes: u64,
+    pub hours: u64,
+    pub days: u64,
+    pub weeks: u64,
+    pub months: u64,
+    pub years: u64,
+}
+
+impl From<TimeSpan> for f64 {
+    fn from(ts: TimeSpan) -> Self {
+        (ts.seconds as f64)
+            + ((ts.nsec as f64) / 1_000_000_000.0)
+            + ((ts.usec as f64) / 1_000_000.0)
+            + ((ts.msec as f64) / 1_000.0)
+            + ((ts.minutes as f64) * 60.0)
+            + ((ts.hours as f64) * 3600.0)
+            + ((ts.days as f64) * 3600.0 * 24.0)
+            + ((ts.weeks as f64) * 3600.0 * 24.0 * 7.0)
+            + ((ts.months as f64) * 3600.0 * 24.0 * 30.44)
+            + ((ts.years as f64) * 3600.0 * 24.0 * 365.25)
+    }
+}
+
+impl From<std::time::Duration> for TimeSpan {
+    fn from(duration: std::time::Duration) -> Self {
+        let mut duration = duration.as_nanos();
+        let nsec = (duration % 1000) as u64;
+        duration /= 1000;
+        let usec = (duration % 1000) as u64;
+        duration /= 1000;
+        let msec = (duration % 1000) as u64;
+        duration /= 1000;
+        let seconds = (duration % 60) as u64;
+        duration /= 60;
+        let minutes = (duration % 60) as u64;
+        duration /= 60;
+        let hours = (duration % 24) as u64;
+        duration /= 24;
+        let years = (duration as f64 / 365.25) as u64;
+        let ydays = (duration as f64 % 365.25) as u64;
+        let months = (ydays as f64 / 30.44) as u64;
+        let mdays = (ydays as f64 % 30.44) as u64;
+        let weeks = mdays / 7;
+        let days = mdays % 7;
+        Self {
+            nsec,
+            usec,
+            msec,
+            seconds,
+            minutes,
+            hours,
+            days,
+            weeks,
+            months,
+            years,
+        }
+    }
+}
+
+impl std::fmt::Display for TimeSpan {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
+        let mut first = true;
+        {
+            // block scope for mutable borrows
+            let mut do_write = |v: u64, unit: &str| -> Result<(), std::fmt::Error> {
+                if !first {
+                    write!(f, " ")?;
+                }
+                first = false;
+                write!(f, "{}{}", v, unit)
+            };
+            if self.years > 0 {
+                do_write(self.years, "y")?;
+            }
+            if self.months > 0 {
+                do_write(self.months, "m")?;
+            }
+            if self.weeks > 0 {
+                do_write(self.weeks, "w")?;
+            }
+            if self.days > 0 {
+                do_write(self.days, "d")?;
+            }
+            if self.hours > 0 {
+                do_write(self.hours, "h")?;
+            }
+            if self.minutes > 0 {
+                do_write(self.minutes, "min")?;
+            }
+        }
+        if !first {
+            write!(f, " ")?;
+        }
+        let seconds = self.seconds as f64 + (self.msec as f64 / 1000.0);
+        if seconds >= 0.1 {
+            if seconds >= 1.0 || !first {
+                write!(f, "{:.0}s", seconds)?;
+            } else {
+                write!(f, "{:.1}s", seconds)?;
+            }
+        } else if first {
+            write!(f, "<0.1s")?;
+        }
+        Ok(())
+    }
+}
+
+fn parse_time_unit(i: &str) -> IResult<&str, &str> {
+    let (n, text) = take_while1(|c: char| char::is_ascii_alphabetic(&c) || c == 'µ')(i)?;
+    if TIME_SPAN_UNITS.contains_key(&text) {
+        Ok((n, text))
+    } else {
+        Err(parse_error(text, "time unit"))
+    }
+}
+
+/// Parse a [TimeSpan]
+pub fn parse_time_span(i: &str) -> Result<TimeSpan, Error> {
+    parse_complete_line("time span", i, parse_time_span_incomplete)
+}
+
+fn parse_time_span_incomplete(mut i: &str) -> IResult<&str, TimeSpan> {
+    let mut ts = TimeSpan::default();
+
+    loop {
+        i = space0(i)?.0;
+        if i.is_empty() {
+            break;
+        }
+        let (n, num) = parse_u64(i)?;
+        i = space0(n)?.0;
+
+        if let (n, Some(unit)) = opt(parse_time_unit)(i)? {
+            i = n;
+            match unit {
+                "seconds" | "second" | "sec" | "s" => {
+                    ts.seconds += num;
+                }
+                "msec" | "ms" => {
+                    ts.msec += num;
+                }
+                "usec" | "us" | "µs" => {
+                    ts.usec += num;
+                }
+                "nsec" | "ns" => {
+                    ts.nsec += num;
+                }
+                "minutes" | "minute" | "min" | "m" => {
+                    ts.minutes += num;
+                }
+                "hours" | "hour" | "hr" | "h" => {
+                    ts.hours += num;
+                }
+                "days" | "day" | "d" => {
+                    ts.days += num;
+                }
+                "weeks" | "week" | "w" => {
+                    ts.weeks += num;
+                }
+                "months" | "month" | "M" => {
+                    ts.months += num;
+                }
+                "years" | "year" | "y" => {
+                    ts.years += num;
+                }
+                _ => return Err(parse_error(unit, "internal error")),
+            }
+        } else {
+            ts.seconds += num;
+        }
+    }
+
+    Ok((i, ts))
+}
+
+/// Verify the format of the [TimeSpan]
+pub fn verify_time_span(i: &str) -> Result<(), Error> {
+    parse_time_span(i)?;
+    Ok(())
+}
+
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 10/14] proxmox-time: move tests from time.rs to test.rs
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (8 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 09/14] proxmox-time: move TimeSpan into time_span.rs Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 11/14] proxmox-time: lib.rs: rustfmt Dominik Csapak
                   ` (5 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/lib.rs  |   6 +-
 proxmox-time/src/test.rs | 263 +++++++++++++++++++++++++++++++++++++++
 proxmox-time/src/time.rs | 231 ----------------------------------
 3 files changed, 266 insertions(+), 234 deletions(-)
 create mode 100644 proxmox-time/src/test.rs
 delete mode 100644 proxmox-time/src/time.rs

diff --git a/proxmox-time/src/lib.rs b/proxmox-time/src/lib.rs
index cbb7008..fa59eee 100644
--- a/proxmox-time/src/lib.rs
+++ b/proxmox-time/src/lib.rs
@@ -7,9 +7,6 @@ use anyhow::{bail, format_err, Error};
 mod tm_editor;
 pub use tm_editor::*;
 
-mod time;
-pub use time::*;
-
 pub(crate) mod parse_helpers;
 
 pub(crate) mod date_time_value;
@@ -26,6 +23,9 @@ pub use week_days::*;
 mod daily_duration;
 pub use daily_duration::*;
 
+#[cfg(test)]
+mod test;
+
 /// Safe bindings to libc timelocal
 ///
 /// We set tm_isdst to -1.
diff --git a/proxmox-time/src/test.rs b/proxmox-time/src/test.rs
new file mode 100644
index 0000000..3007156
--- /dev/null
+++ b/proxmox-time/src/test.rs
@@ -0,0 +1,263 @@
+use anyhow::bail;
+
+use super::*;
+
+fn test_event(v: &'static str) -> Result<(), Error> {
+    match parse_calendar_event(v) {
+        Ok(event) => println!("CalendarEvent '{}' => {:?}", v, event),
+        Err(err) => bail!("parsing '{}' failed - {}", v, err),
+    }
+
+    Ok(())
+}
+
+const fn make_test_time(mday: i32, hour: i32, min: i32) -> i64 {
+    (mday * 3600 * 24 + hour * 3600 + min * 60) as i64
+}
+
+#[test]
+fn test_compute_next_event() -> Result<(), Error> {
+    let test_value = |v: &'static str, last: i64, expect: i64| -> Result<i64, Error> {
+        let event = match parse_calendar_event(v) {
+            Ok(event) => event,
+            Err(err) => bail!("parsing '{}' failed - {}", v, err),
+        };
+
+        match compute_next_event(&event, last, true) {
+            Ok(Some(next)) => {
+                if next == expect {
+                    println!("next {:?} => {}", event, next);
+                } else {
+                    bail!(
+                        "next {:?} failed\nnext:  {:?}\nexpect: {:?}",
+                        event,
+                        crate::gmtime(next),
+                        crate::gmtime(expect),
+                    );
+                }
+            }
+            Ok(None) => bail!("next {:?} failed to find a timestamp", event),
+            Err(err) => bail!("compute next for '{}' failed - {}", v, err),
+        }
+
+        Ok(expect)
+    };
+
+    let test_never = |v: &'static str, last: i64| -> Result<(), Error> {
+        let event = match parse_calendar_event(v) {
+            Ok(event) => event,
+            Err(err) => bail!("parsing '{}' failed - {}", v, err),
+        };
+
+        match compute_next_event(&event, last, true)? {
+            None => Ok(()),
+            Some(next) => bail!(
+                "compute next for '{}' succeeded, but expected fail - result {}",
+                v,
+                next
+            ),
+        }
+    };
+
+    const MIN: i64 = 60;
+    const HOUR: i64 = 3600;
+    const DAY: i64 = 3600 * 24;
+
+    const THURSDAY_00_00: i64 = make_test_time(0, 0, 0);
+    const THURSDAY_15_00: i64 = make_test_time(0, 15, 0);
+
+    const JUL_31_2020: i64 = 1596153600; // Friday, 2020-07-31 00:00:00
+    const DEC_31_2020: i64 = 1609372800; // Thursday, 2020-12-31 00:00:00
+
+    // minute only syntax
+    test_value("0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
+    test_value("*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
+
+    test_value("*:0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
+    test_value("*:*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
+    test_value("*:*:*", THURSDAY_00_00, THURSDAY_00_00 + 1)?;
+    test_value("*:3:5", THURSDAY_00_00, THURSDAY_00_00 + 3 * MIN + 5)?;
+
+    test_value("mon *:*", THURSDAY_00_00, THURSDAY_00_00 + 4 * DAY)?;
+    test_value(
+        "mon 2:*",
+        THURSDAY_00_00,
+        THURSDAY_00_00 + 4 * DAY + 2 * HOUR,
+    )?;
+    test_value(
+        "mon 2:50",
+        THURSDAY_00_00,
+        THURSDAY_00_00 + 4 * DAY + 2 * HOUR + 50 * MIN,
+    )?;
+
+    test_value("tue", THURSDAY_00_00, THURSDAY_00_00 + 5 * DAY)?;
+    test_value("wed", THURSDAY_00_00, THURSDAY_00_00 + 6 * DAY)?;
+    test_value("thu", THURSDAY_00_00, THURSDAY_00_00 + 7 * DAY)?;
+    test_value("fri", THURSDAY_00_00, THURSDAY_00_00 + 1 * DAY)?;
+    test_value("sat", THURSDAY_00_00, THURSDAY_00_00 + 2 * DAY)?;
+    test_value("sun", THURSDAY_00_00, THURSDAY_00_00 + 3 * DAY)?;
+
+    // test repeated ranges
+    test_value("4..10/2:0", THURSDAY_00_00, THURSDAY_00_00 + 4 * HOUR)?;
+    test_value(
+        "4..10/2:0",
+        THURSDAY_00_00 + 5 * HOUR,
+        THURSDAY_00_00 + 6 * HOUR,
+    )?;
+    test_value(
+        "4..10/2:0",
+        THURSDAY_00_00 + 11 * HOUR,
+        THURSDAY_00_00 + 1 * DAY + 4 * HOUR,
+    )?;
+
+    // test multiple values for a single field
+    // and test that the order does not matter
+    test_value(
+        "5,10:4,8",
+        THURSDAY_00_00,
+        THURSDAY_00_00 + 5 * HOUR + 4 * MIN,
+    )?;
+    test_value(
+        "10,5:8,4",
+        THURSDAY_00_00,
+        THURSDAY_00_00 + 5 * HOUR + 4 * MIN,
+    )?;
+    test_value(
+        "6,4..10:23,5/5",
+        THURSDAY_00_00,
+        THURSDAY_00_00 + 4 * HOUR + 5 * MIN,
+    )?;
+    test_value(
+        "4..10,6:5/5,23",
+        THURSDAY_00_00,
+        THURSDAY_00_00 + 4 * HOUR + 5 * MIN,
+    )?;
+
+    // test month wrapping
+    test_value("sat", JUL_31_2020, JUL_31_2020 + 1 * DAY)?;
+    test_value("sun", JUL_31_2020, JUL_31_2020 + 2 * DAY)?;
+    test_value("mon", JUL_31_2020, JUL_31_2020 + 3 * DAY)?;
+    test_value("tue", JUL_31_2020, JUL_31_2020 + 4 * DAY)?;
+    test_value("wed", JUL_31_2020, JUL_31_2020 + 5 * DAY)?;
+    test_value("thu", JUL_31_2020, JUL_31_2020 + 6 * DAY)?;
+    test_value("fri", JUL_31_2020, JUL_31_2020 + 7 * DAY)?;
+
+    // test year wrapping
+    test_value("fri", DEC_31_2020, DEC_31_2020 + 1 * DAY)?;
+    test_value("sat", DEC_31_2020, DEC_31_2020 + 2 * DAY)?;
+    test_value("sun", DEC_31_2020, DEC_31_2020 + 3 * DAY)?;
+    test_value("mon", DEC_31_2020, DEC_31_2020 + 4 * DAY)?;
+    test_value("tue", DEC_31_2020, DEC_31_2020 + 5 * DAY)?;
+    test_value("wed", DEC_31_2020, DEC_31_2020 + 6 * DAY)?;
+    test_value("thu", DEC_31_2020, DEC_31_2020 + 7 * DAY)?;
+
+    test_value("daily", THURSDAY_00_00, THURSDAY_00_00 + DAY)?;
+    test_value("daily", THURSDAY_00_00 + 1, THURSDAY_00_00 + DAY)?;
+
+    let n = test_value("5/2:0", THURSDAY_00_00, THURSDAY_00_00 + 5 * HOUR)?;
+    let n = test_value("5/2:0", n, THURSDAY_00_00 + 7 * HOUR)?;
+    let n = test_value("5/2:0", n, THURSDAY_00_00 + 9 * HOUR)?;
+    test_value("5/2:0", n, THURSDAY_00_00 + 11 * HOUR)?;
+
+    let mut n = test_value("*:*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
+    for i in 2..100 {
+        n = test_value("*:*", n, THURSDAY_00_00 + i * MIN)?;
+    }
+
+    let mut n = test_value("*:0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
+    for i in 2..100 {
+        n = test_value("*:0", n, THURSDAY_00_00 + i * HOUR)?;
+    }
+
+    let mut n = test_value("1:0", THURSDAY_15_00, THURSDAY_00_00 + DAY + HOUR)?;
+    for i in 2..100 {
+        n = test_value("1:0", n, THURSDAY_00_00 + i * DAY + HOUR)?;
+    }
+
+    // test date functionality
+
+    test_value("2020-07-31", 0, JUL_31_2020)?;
+    test_value("02-28", 0, (31 + 27) * DAY)?;
+    test_value("02-29", 0, 2 * 365 * DAY + (31 + 28) * DAY)?; // 1972-02-29
+    test_value("1965/5-01-01", -1, THURSDAY_00_00)?;
+    test_value("2020-7..9-2/2", JUL_31_2020, JUL_31_2020 + 2 * DAY)?;
+    test_value("2020,2021-12-31", JUL_31_2020, DEC_31_2020)?;
+
+    test_value("monthly", 0, 31 * DAY)?;
+    test_value("quarterly", 0, (31 + 28 + 31) * DAY)?;
+    test_value("semiannually", 0, (31 + 28 + 31 + 30 + 31 + 30) * DAY)?;
+    test_value("yearly", 0, (365) * DAY)?;
+
+    test_never("2021-02-29", 0)?;
+    test_never("02-30", 0)?;
+
+    Ok(())
+}
+
+#[test]
+fn test_calendar_event_weekday() -> Result<(), Error> {
+    test_event("mon,wed..fri")?;
+    test_event("fri..mon")?;
+
+    test_event("mon")?;
+    test_event("MON")?;
+    test_event("monDay")?;
+    test_event("tue")?;
+    test_event("Tuesday")?;
+    test_event("wed")?;
+    test_event("wednesday")?;
+    test_event("thu")?;
+    test_event("thursday")?;
+    test_event("fri")?;
+    test_event("friday")?;
+    test_event("sat")?;
+    test_event("saturday")?;
+    test_event("sun")?;
+    test_event("sunday")?;
+
+    test_event("mon..fri")?;
+    test_event("mon,tue,fri")?;
+    test_event("mon,tue..wednesday,fri..sat")?;
+
+    Ok(())
+}
+
+#[test]
+fn test_time_span_parser() -> Result<(), Error> {
+    let test_value = |ts_str: &str, expect: f64| -> Result<(), Error> {
+        let ts = parse_time_span(ts_str)?;
+        assert_eq!(f64::from(ts), expect, "{}", ts_str);
+        Ok(())
+    };
+
+    test_value("2", 2.0)?;
+    test_value("2s", 2.0)?;
+    test_value("2sec", 2.0)?;
+    test_value("2second", 2.0)?;
+    test_value("2seconds", 2.0)?;
+
+    test_value(" 2s 2 s 2", 6.0)?;
+
+    test_value("1msec 1ms", 0.002)?;
+    test_value("1usec 1us 1µs", 0.000_003)?;
+    test_value("1nsec 1ns", 0.000_000_002)?;
+    test_value("1minutes 1minute 1min 1m", 4.0 * 60.0)?;
+    test_value("1hours 1hour 1hr 1h", 4.0 * 3600.0)?;
+    test_value("1days 1day 1d", 3.0 * 86400.0)?;
+    test_value("1weeks 1 week 1w", 3.0 * 86400.0 * 7.0)?;
+    test_value("1months 1month 1M", 3.0 * 86400.0 * 30.44)?;
+    test_value("1years 1year 1y", 3.0 * 86400.0 * 365.25)?;
+
+    test_value("2h", 7200.0)?;
+    test_value(" 2 h", 7200.0)?;
+    test_value("2hours", 7200.0)?;
+    test_value("48hr", 48.0 * 3600.0)?;
+    test_value(
+        "1y 12month",
+        365.25 * 24.0 * 3600.0 + 12.0 * 30.44 * 24.0 * 3600.0,
+    )?;
+    test_value("55s500ms", 55.5)?;
+    test_value("300ms20s 5day", 5.0 * 24.0 * 3600.0 + 20.0 + 0.3)?;
+
+    Ok(())
+}
diff --git a/proxmox-time/src/time.rs b/proxmox-time/src/time.rs
deleted file mode 100644
index ac2f510..0000000
--- a/proxmox-time/src/time.rs
+++ /dev/null
@@ -1,231 +0,0 @@
-#[cfg(test)]
-mod test {
-
-    use anyhow::bail;
-    use crate::*;
-
-
-    fn test_event(v: &'static str) -> Result<(), Error> {
-        match parse_calendar_event(v) {
-            Ok(event) => println!("CalendarEvent '{}' => {:?}", v, event),
-            Err(err) => bail!("parsing '{}' failed - {}", v, err),
-        }
-
-        Ok(())
-    }
-
-    const fn make_test_time(mday: i32, hour: i32, min: i32) -> i64 {
-        (mday*3600*24 + hour*3600 + min*60) as i64
-    }
-
-    #[test]
-    #[allow(clippy::identity_op)]
-    fn test_compute_next_event() -> Result<(), Error> {
-
-        let test_value = |v: &'static str, last: i64, expect: i64| -> Result<i64, Error> {
-            let event = match parse_calendar_event(v) {
-                Ok(event) => event,
-                Err(err) => bail!("parsing '{}' failed - {}", v, err),
-            };
-
-            match compute_next_event(&event, last, true) {
-                Ok(Some(next)) => {
-                    if next == expect {
-                        println!("next {:?} => {}", event, next);
-                    } else {
-                        bail!(
-                            "next {:?} failed\nnext:  {:?}\nexpect: {:?}",
-                            event,
-                            crate::gmtime(next),
-                            crate::gmtime(expect),
-                        );
-                    }
-                }
-                Ok(None) => bail!("next {:?} failed to find a timestamp", event),
-                Err(err) => bail!("compute next for '{}' failed - {}", v, err),
-            }
-
-            Ok(expect)
-        };
-
-        let test_never = |v: &'static str, last: i64| -> Result<(), Error> {
-            let event = match parse_calendar_event(v) {
-                Ok(event) => event,
-                Err(err) => bail!("parsing '{}' failed - {}", v, err),
-            };
-
-            match compute_next_event(&event, last, true)? {
-                None => Ok(()),
-                Some(next) => bail!("compute next for '{}' succeeded, but expected fail - result {}", v, next),
-            }
-        };
-
-        const MIN: i64 = 60;
-        const HOUR: i64 = 3600;
-        const DAY: i64 = 3600*24;
-
-        const THURSDAY_00_00: i64 = make_test_time(0, 0, 0);
-        const THURSDAY_15_00: i64 = make_test_time(0, 15, 0);
-
-        const JUL_31_2020: i64 = 1596153600; // Friday, 2020-07-31 00:00:00
-        const DEC_31_2020: i64 = 1609372800; // Thursday, 2020-12-31 00:00:00
-
-        // minute only syntax
-        test_value("0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
-        test_value("*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
-
-        test_value("*:0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
-        test_value("*:*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
-        test_value("*:*:*", THURSDAY_00_00, THURSDAY_00_00 + 1)?;
-        test_value("*:3:5", THURSDAY_00_00, THURSDAY_00_00 + 3*MIN + 5)?;
-
-        test_value("mon *:*", THURSDAY_00_00, THURSDAY_00_00 + 4*DAY)?;
-        test_value("mon 2:*", THURSDAY_00_00, THURSDAY_00_00 + 4*DAY + 2*HOUR)?;
-        test_value("mon 2:50", THURSDAY_00_00, THURSDAY_00_00 + 4*DAY + 2*HOUR + 50*MIN)?;
-
-        test_value("tue", THURSDAY_00_00, THURSDAY_00_00 + 5*DAY)?;
-        test_value("wed", THURSDAY_00_00, THURSDAY_00_00 + 6*DAY)?;
-        test_value("thu", THURSDAY_00_00, THURSDAY_00_00 + 7*DAY)?;
-        test_value("fri", THURSDAY_00_00, THURSDAY_00_00 + 1*DAY)?;
-        test_value("sat", THURSDAY_00_00, THURSDAY_00_00 + 2*DAY)?;
-        test_value("sun", THURSDAY_00_00, THURSDAY_00_00 + 3*DAY)?;
-
-        // test repeated ranges
-        test_value("4..10/2:0", THURSDAY_00_00, THURSDAY_00_00 + 4*HOUR)?;
-        test_value("4..10/2:0", THURSDAY_00_00 + 5*HOUR, THURSDAY_00_00 + 6*HOUR)?;
-        test_value("4..10/2:0", THURSDAY_00_00 + 11*HOUR, THURSDAY_00_00 + 1*DAY + 4*HOUR)?;
-
-        // test multiple values for a single field
-        // and test that the order does not matter
-        test_value("5,10:4,8", THURSDAY_00_00, THURSDAY_00_00 + 5*HOUR + 4*MIN)?;
-        test_value("10,5:8,4", THURSDAY_00_00, THURSDAY_00_00 + 5*HOUR + 4*MIN)?;
-        test_value("6,4..10:23,5/5", THURSDAY_00_00, THURSDAY_00_00 + 4*HOUR + 5*MIN)?;
-        test_value("4..10,6:5/5,23", THURSDAY_00_00, THURSDAY_00_00 + 4*HOUR + 5*MIN)?;
-
-        // test month wrapping
-        test_value("sat", JUL_31_2020, JUL_31_2020 + 1*DAY)?;
-        test_value("sun", JUL_31_2020, JUL_31_2020 + 2*DAY)?;
-        test_value("mon", JUL_31_2020, JUL_31_2020 + 3*DAY)?;
-        test_value("tue", JUL_31_2020, JUL_31_2020 + 4*DAY)?;
-        test_value("wed", JUL_31_2020, JUL_31_2020 + 5*DAY)?;
-        test_value("thu", JUL_31_2020, JUL_31_2020 + 6*DAY)?;
-        test_value("fri", JUL_31_2020, JUL_31_2020 + 7*DAY)?;
-
-        // test year wrapping
-        test_value("fri", DEC_31_2020, DEC_31_2020 + 1*DAY)?;
-        test_value("sat", DEC_31_2020, DEC_31_2020 + 2*DAY)?;
-        test_value("sun", DEC_31_2020, DEC_31_2020 + 3*DAY)?;
-        test_value("mon", DEC_31_2020, DEC_31_2020 + 4*DAY)?;
-        test_value("tue", DEC_31_2020, DEC_31_2020 + 5*DAY)?;
-        test_value("wed", DEC_31_2020, DEC_31_2020 + 6*DAY)?;
-        test_value("thu", DEC_31_2020, DEC_31_2020 + 7*DAY)?;
-
-        test_value("daily", THURSDAY_00_00, THURSDAY_00_00 + DAY)?;
-        test_value("daily", THURSDAY_00_00+1, THURSDAY_00_00 + DAY)?;
-
-        let n = test_value("5/2:0", THURSDAY_00_00, THURSDAY_00_00 + 5*HOUR)?;
-        let n = test_value("5/2:0", n, THURSDAY_00_00 + 7*HOUR)?;
-        let n = test_value("5/2:0", n, THURSDAY_00_00 + 9*HOUR)?;
-        test_value("5/2:0", n, THURSDAY_00_00 + 11*HOUR)?;
-
-        let mut n = test_value("*:*", THURSDAY_00_00, THURSDAY_00_00 + MIN)?;
-        for i in 2..100 {
-            n = test_value("*:*", n, THURSDAY_00_00 + i*MIN)?;
-        }
-
-        let mut n = test_value("*:0", THURSDAY_00_00, THURSDAY_00_00 + HOUR)?;
-        for i in 2..100 {
-            n = test_value("*:0", n, THURSDAY_00_00 + i*HOUR)?;
-        }
-
-        let mut n = test_value("1:0", THURSDAY_15_00, THURSDAY_00_00 + DAY + HOUR)?;
-        for i in 2..100 {
-            n = test_value("1:0", n, THURSDAY_00_00 + i*DAY + HOUR)?;
-        }
-
-        // test date functionality
-
-        test_value("2020-07-31", 0, JUL_31_2020)?;
-        test_value("02-28", 0, (31+27)*DAY)?;
-        test_value("02-29", 0, 2*365*DAY + (31+28)*DAY)?; // 1972-02-29
-        test_value("1965/5-01-01", -1, THURSDAY_00_00)?;
-        test_value("2020-7..9-2/2", JUL_31_2020, JUL_31_2020 + 2*DAY)?;
-        test_value("2020,2021-12-31", JUL_31_2020, DEC_31_2020)?;
-
-        test_value("monthly", 0, 31*DAY)?;
-        test_value("quarterly", 0, (31+28+31)*DAY)?;
-        test_value("semiannually", 0, (31+28+31+30+31+30)*DAY)?;
-        test_value("yearly", 0, (365)*DAY)?;
-
-        test_never("2021-02-29", 0)?;
-        test_never("02-30", 0)?;
-
-        Ok(())
-    }
-
-    #[test]
-    fn test_calendar_event_weekday() -> Result<(), Error> {
-        test_event("mon,wed..fri")?;
-        test_event("fri..mon")?;
-
-        test_event("mon")?;
-        test_event("MON")?;
-        test_event("monDay")?;
-        test_event("tue")?;
-        test_event("Tuesday")?;
-        test_event("wed")?;
-        test_event("wednesday")?;
-        test_event("thu")?;
-        test_event("thursday")?;
-        test_event("fri")?;
-        test_event("friday")?;
-        test_event("sat")?;
-        test_event("saturday")?;
-        test_event("sun")?;
-        test_event("sunday")?;
-
-        test_event("mon..fri")?;
-        test_event("mon,tue,fri")?;
-        test_event("mon,tue..wednesday,fri..sat")?;
-
-        Ok(())
-    }
-
-    #[test]
-    fn test_time_span_parser() -> Result<(), Error> {
-
-        let test_value = |ts_str: &str, expect: f64| -> Result<(), Error> {
-            let ts = parse_time_span(ts_str)?;
-            assert_eq!(f64::from(ts), expect, "{}", ts_str);
-            Ok(())
-        };
-
-        test_value("2", 2.0)?;
-        test_value("2s", 2.0)?;
-        test_value("2sec", 2.0)?;
-        test_value("2second", 2.0)?;
-        test_value("2seconds", 2.0)?;
-
-        test_value(" 2s 2 s 2", 6.0)?;
-
-        test_value("1msec 1ms", 0.002)?;
-        test_value("1usec 1us 1µs", 0.000_003)?;
-        test_value("1nsec 1ns", 0.000_000_002)?;
-        test_value("1minutes 1minute 1min 1m", 4.0*60.0)?;
-        test_value("1hours 1hour 1hr 1h", 4.0*3600.0)?;
-        test_value("1days 1day 1d", 3.0*86400.0)?;
-        test_value("1weeks 1 week 1w", 3.0*86400.0*7.0)?;
-        test_value("1months 1month 1M", 3.0*86400.0*30.44)?;
-        test_value("1years 1year 1y", 3.0*86400.0*365.25)?;
-
-        test_value("2h", 7200.0)?;
-        test_value(" 2 h", 7200.0)?;
-        test_value("2hours", 7200.0)?;
-        test_value("48hr", 48.0*3600.0)?;
-        test_value("1y 12month", 365.25*24.0*3600.0 + 12.0*30.44*24.0*3600.0)?;
-        test_value("55s500ms", 55.5)?;
-        test_value("300ms20s 5day", 5.0*24.0*3600.0 + 20.0 + 0.3)?;
-
-        Ok(())
-    }
-}
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 11/14] proxmox-time: lib.rs: rustfmt
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (9 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 10/14] proxmox-time: move tests from time.rs to test.rs Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 12/14] proxmox-time: calendar-events: make compute_next_event a method Dominik Csapak
                   ` (4 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/lib.rs | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/proxmox-time/src/lib.rs b/proxmox-time/src/lib.rs
index fa59eee..53ba11e 100644
--- a/proxmox-time/src/lib.rs
+++ b/proxmox-time/src/lib.rs
@@ -145,8 +145,7 @@ extern "C" {
 
 /// Safe bindings to libc strftime
 pub fn strftime(format: &str, t: &libc::tm) -> Result<String, Error> {
-    let format = CString::new(format)
-        .map_err(|err| format_err!("{}", err))?;
+    let format = CString::new(format).map_err(|err| format_err!("{}", err))?;
     let mut buf = vec![0u8; 8192];
 
     let res = unsafe {
@@ -157,7 +156,8 @@ pub fn strftime(format: &str, t: &libc::tm) -> Result<String, Error> {
             t as *const libc::tm,
         )
     };
-    if res == !0 { // -1,, it's unsigned
+    if res == !0 {
+        // -1,, it's unsigned
         bail!("strftime failed");
     }
     let len = res as usize;
@@ -166,8 +166,7 @@ pub fn strftime(format: &str, t: &libc::tm) -> Result<String, Error> {
         bail!("strftime: result len is 0 (string too large)");
     };
 
-    let c_str = CStr::from_bytes_with_nul(&buf[..len + 1])
-        .map_err(|err| format_err!("{}", err))?;
+    let c_str = CStr::from_bytes_with_nul(&buf[..len + 1]).map_err(|err| format_err!("{}", err))?;
     let str_slice: &str = c_str.to_str().unwrap();
     Ok(str_slice.to_owned())
 }
@@ -229,10 +228,13 @@ pub fn epoch_to_rfc3339(epoch: i64) -> Result<String, Error> {
 
 /// Parse RFC3339 into Unix epoch
 pub fn parse_rfc3339(input_str: &str) -> Result<i64, Error> {
-    parse_rfc3339_do(input_str)
-        .map_err(|err| {
-            format_err!("failed to parse rfc3339 timestamp ({:?}) - {}", input_str, err)
-        })
+    parse_rfc3339_do(input_str).map_err(|err| {
+        format_err!(
+            "failed to parse rfc3339 timestamp ({:?}) - {}",
+            input_str,
+            err
+        )
+    })
 }
 
 fn parse_rfc3339_do(input_str: &str) -> Result<i64, Error> {
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 12/14] proxmox-time: calendar-events: make compute_next_event a method
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (10 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 11/14] proxmox-time: lib.rs: rustfmt Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 13/14] proxmox-time: calendar_events: implement FromStr Dominik Csapak
                   ` (3 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

and deprecated the standalone function

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/calendar_event.rs | 221 +++++++++++++++--------------
 proxmox-time/src/test.rs           |   4 +-
 2 files changed, 117 insertions(+), 108 deletions(-)

diff --git a/proxmox-time/src/calendar_event.rs b/proxmox-time/src/calendar_event.rs
index 1c21a84..a839f9d 100644
--- a/proxmox-time/src/calendar_event.rs
+++ b/proxmox-time/src/calendar_event.rs
@@ -35,141 +35,150 @@ pub struct CalendarEvent {
     pub(crate) year: Vec<DateTimeValue>,
 }
 
-/// Verify the format of the [CalendarEvent]
-pub fn verify_calendar_event(i: &str) -> Result<(), Error> {
-    parse_calendar_event(i)?;
-    Ok(())
-}
-
-/// Compute the next event
-pub fn compute_next_event(
-    event: &CalendarEvent,
-    last: i64,
-    utc: bool,
-) -> Result<Option<i64>, Error> {
+impl CalendarEvent {
+    /// Computes the next timestamp after `last`. If `utc` is false, the local
+    /// timezone will be used for the calculation.
+    pub fn compute_next_event(&self, last: i64, utc: bool) -> Result<Option<i64>, Error> {
+        let last = last + 1; // at least one second later
 
-    let last = last + 1; // at least one second later
+        let all_days = self.days.is_empty() || self.days.is_all();
 
-    let all_days = event.days.is_empty() || event.days.is_all();
+        let mut t = TmEditor::with_epoch(last, utc)?;
 
-    let mut t = TmEditor::with_epoch(last, utc)?;
+        let mut count = 0;
 
-    let mut count = 0;
-
-    loop {
-        // cancel after 1000 loops
-        if count > 1000 {
-            return Ok(None);
-        } else {
-            count += 1;
-        }
+        loop {
+            // cancel after 1000 loops
+            if count > 1000 {
+                return Ok(None);
+            } else {
+                count += 1;
+            }
 
-        if !event.year.is_empty() {
-            let year: u32 = t.year().try_into()?;
-            if !DateTimeValue::list_contains(&event.year, year) {
-                if let Some(n) = DateTimeValue::find_next(&event.year, year) {
-                    t.add_years((n - year).try_into()?)?;
-                    continue;
-                } else {
-                    // if we have no valid year, we cannot find a correct timestamp
-                    return Ok(None);
+            if !self.year.is_empty() {
+                let year: u32 = t.year().try_into()?;
+                if !DateTimeValue::list_contains(&self.year, year) {
+                    if let Some(n) = DateTimeValue::find_next(&self.year, year) {
+                        t.add_years((n - year).try_into()?)?;
+                        continue;
+                    } else {
+                        // if we have no valid year, we cannot find a correct timestamp
+                        return Ok(None);
+                    }
                 }
             }
-        }
 
-        if !event.month.is_empty() {
-            let month: u32 = t.month().try_into()?;
-            if !DateTimeValue::list_contains(&event.month, month) {
-                if let Some(n) = DateTimeValue::find_next(&event.month, month) {
-                    t.add_months((n - month).try_into()?)?;
-                } else {
-                    // if we could not find valid month, retry next year
-                    t.add_years(1)?;
+            if !self.month.is_empty() {
+                let month: u32 = t.month().try_into()?;
+                if !DateTimeValue::list_contains(&self.month, month) {
+                    if let Some(n) = DateTimeValue::find_next(&self.month, month) {
+                        t.add_months((n - month).try_into()?)?;
+                    } else {
+                        // if we could not find valid month, retry next year
+                        t.add_years(1)?;
+                    }
+                    continue;
                 }
-                continue;
             }
-        }
 
-        if !event.day.is_empty() {
-            let day: u32 = t.day().try_into()?;
-            if !DateTimeValue::list_contains(&event.day, day) {
-                if let Some(n) = DateTimeValue::find_next(&event.day, day) {
-                    t.add_days((n - day).try_into()?)?;
-                } else {
-                    // if we could not find valid mday, retry next month
-                    t.add_months(1)?;
+            if !self.day.is_empty() {
+                let day: u32 = t.day().try_into()?;
+                if !DateTimeValue::list_contains(&self.day, day) {
+                    if let Some(n) = DateTimeValue::find_next(&self.day, day) {
+                        t.add_days((n - day).try_into()?)?;
+                    } else {
+                        // if we could not find valid mday, retry next month
+                        t.add_months(1)?;
+                    }
+                    continue;
                 }
-                continue;
             }
-        }
 
-        if !all_days { // match day first
-            let day_num: u32 = t.day_num().try_into()?;
-            let day = WeekDays::from_bits(1<<day_num).unwrap();
-            if !event.days.contains(day) {
-                if let Some(n) = ((day_num+1)..7)
-                    .find(|d| event.days.contains(WeekDays::from_bits(1<<d).unwrap()))
-                {
-                    // try next day
-                    t.add_days((n - day_num).try_into()?)?;
-                } else {
-                    // try next week
-                    t.add_days((7 - day_num).try_into()?)?;
+            if !all_days {
+                // match day first
+                let day_num: u32 = t.day_num().try_into()?;
+                let day = WeekDays::from_bits(1 << day_num).unwrap();
+                if !self.days.contains(day) {
+                    if let Some(n) = ((day_num + 1)..7)
+                        .find(|d| self.days.contains(WeekDays::from_bits(1 << d).unwrap()))
+                    {
+                        // try next day
+                        t.add_days((n - day_num).try_into()?)?;
+                    } else {
+                        // try next week
+                        t.add_days((7 - day_num).try_into()?)?;
+                    }
+                    continue;
                 }
-                continue;
             }
-        }
 
-        // this day
-        if !event.hour.is_empty() {
-            let hour = t.hour().try_into()?;
-            if !DateTimeValue::list_contains(&event.hour, hour) {
-                if let Some(n) = DateTimeValue::find_next(&event.hour, hour) {
-                    // test next hour
-                    t.set_time(n.try_into()?, 0, 0)?;
-                } else {
-                    // test next day
-                    t.add_days(1)?;
+            // this day
+            if !self.hour.is_empty() {
+                let hour = t.hour().try_into()?;
+                if !DateTimeValue::list_contains(&self.hour, hour) {
+                    if let Some(n) = DateTimeValue::find_next(&self.hour, hour) {
+                        // test next hour
+                        t.set_time(n.try_into()?, 0, 0)?;
+                    } else {
+                        // test next day
+                        t.add_days(1)?;
+                    }
+                    continue;
                 }
-                continue;
             }
-        }
 
-        // this hour
-        if !event.minute.is_empty() {
-            let minute = t.min().try_into()?;
-            if !DateTimeValue::list_contains(&event.minute, minute) {
-                if let Some(n) = DateTimeValue::find_next(&event.minute, minute) {
-                    // test next minute
-                    t.set_min_sec(n.try_into()?, 0)?;
-                } else {
-                    // test next hour
-                    t.set_time(t.hour() + 1, 0, 0)?;
+            // this hour
+            if !self.minute.is_empty() {
+                let minute = t.min().try_into()?;
+                if !DateTimeValue::list_contains(&self.minute, minute) {
+                    if let Some(n) = DateTimeValue::find_next(&self.minute, minute) {
+                        // test next minute
+                        t.set_min_sec(n.try_into()?, 0)?;
+                    } else {
+                        // test next hour
+                        t.set_time(t.hour() + 1, 0, 0)?;
+                    }
+                    continue;
                 }
-                continue;
             }
-        }
 
-        // this minute
-        if !event.second.is_empty() {
-            let second = t.sec().try_into()?;
-            if !DateTimeValue::list_contains(&event.second, second) {
-                if let Some(n) = DateTimeValue::find_next(&event.second, second) {
-                    // test next second
-                    t.set_sec(n.try_into()?)?;
-                } else {
-                    // test next min
-                    t.set_min_sec(t.min() + 1, 0)?;
+            // this minute
+            if !self.second.is_empty() {
+                let second = t.sec().try_into()?;
+                if !DateTimeValue::list_contains(&self.second, second) {
+                    if let Some(n) = DateTimeValue::find_next(&self.second, second) {
+                        // test next second
+                        t.set_sec(n.try_into()?)?;
+                    } else {
+                        // test next min
+                        t.set_min_sec(t.min() + 1, 0)?;
+                    }
+                    continue;
                 }
-                continue;
             }
-        }
 
-        let next = t.into_epoch()?;
-        return Ok(Some(next))
+            let next = t.into_epoch()?;
+            return Ok(Some(next));
+        }
     }
 }
 
+/// Verify the format of the [CalendarEvent]
+pub fn verify_calendar_event(i: &str) -> Result<(), Error> {
+    parse_calendar_event(i)?;
+    Ok(())
+}
+
+/// Compute the next event. Use [CalendarEvent::compute_next_event] instead.
+#[deprecated="use method 'compute_next_event' of CalendarEvent instead"]
+pub fn compute_next_event(
+    event: &CalendarEvent,
+    last: i64,
+    utc: bool,
+) -> Result<Option<i64>, Error> {
+    event.compute_next_event(last, utc)
+}
+
 /// Parse a [CalendarEvent]
 pub fn parse_calendar_event(i: &str) -> Result<CalendarEvent, Error> {
     parse_complete_line("calendar event", i, parse_calendar_event_incomplete)
diff --git a/proxmox-time/src/test.rs b/proxmox-time/src/test.rs
index 3007156..8c5e1cf 100644
--- a/proxmox-time/src/test.rs
+++ b/proxmox-time/src/test.rs
@@ -23,7 +23,7 @@ fn test_compute_next_event() -> Result<(), Error> {
             Err(err) => bail!("parsing '{}' failed - {}", v, err),
         };
 
-        match compute_next_event(&event, last, true) {
+        match event.compute_next_event(last, true) {
             Ok(Some(next)) => {
                 if next == expect {
                     println!("next {:?} => {}", event, next);
@@ -49,7 +49,7 @@ fn test_compute_next_event() -> Result<(), Error> {
             Err(err) => bail!("parsing '{}' failed - {}", v, err),
         };
 
-        match compute_next_event(&event, last, true)? {
+        match event.compute_next_event(last, true)? {
             None => Ok(()),
             Some(next) => bail!(
                 "compute next for '{}' succeeded, but expected fail - result {}",
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 13/14] proxmox-time: calendar_events: implement FromStr
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (11 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 12/14] proxmox-time: calendar-events: make compute_next_event a method Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 14/14] proxmox-time: time-span: " Dominik Csapak
                   ` (2 subsequent siblings)
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

and deprecate 'parse_calendar_event'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/calendar_event.rs | 13 +++++++++++--
 proxmox-time/src/test.rs           |  6 +++---
 2 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/proxmox-time/src/calendar_event.rs b/proxmox-time/src/calendar_event.rs
index a839f9d..d1d74a2 100644
--- a/proxmox-time/src/calendar_event.rs
+++ b/proxmox-time/src/calendar_event.rs
@@ -163,9 +163,17 @@ impl CalendarEvent {
     }
 }
 
+impl std::str::FromStr for CalendarEvent {
+    type Err = Error;
+
+    fn from_str(i: &str) -> Result<Self, Self::Err> {
+        parse_complete_line("calendar event", i, parse_calendar_event_incomplete)
+    }
+}
+
 /// Verify the format of the [CalendarEvent]
 pub fn verify_calendar_event(i: &str) -> Result<(), Error> {
-    parse_calendar_event(i)?;
+    let _: CalendarEvent = i.parse()?;
     Ok(())
 }
 
@@ -180,8 +188,9 @@ pub fn compute_next_event(
 }
 
 /// Parse a [CalendarEvent]
+#[deprecated="use std::str::FromStr trait instead"]
 pub fn parse_calendar_event(i: &str) -> Result<CalendarEvent, Error> {
-    parse_complete_line("calendar event", i, parse_calendar_event_incomplete)
+    i.parse()
 }
 
 fn parse_calendar_event_incomplete(mut i: &str) -> IResult<&str, CalendarEvent> {
diff --git a/proxmox-time/src/test.rs b/proxmox-time/src/test.rs
index 8c5e1cf..0476cc1 100644
--- a/proxmox-time/src/test.rs
+++ b/proxmox-time/src/test.rs
@@ -3,7 +3,7 @@ use anyhow::bail;
 use super::*;
 
 fn test_event(v: &'static str) -> Result<(), Error> {
-    match parse_calendar_event(v) {
+    match v.parse::<CalendarEvent>() {
         Ok(event) => println!("CalendarEvent '{}' => {:?}", v, event),
         Err(err) => bail!("parsing '{}' failed - {}", v, err),
     }
@@ -18,7 +18,7 @@ const fn make_test_time(mday: i32, hour: i32, min: i32) -> i64 {
 #[test]
 fn test_compute_next_event() -> Result<(), Error> {
     let test_value = |v: &'static str, last: i64, expect: i64| -> Result<i64, Error> {
-        let event = match parse_calendar_event(v) {
+        let event: CalendarEvent = match v.parse() {
             Ok(event) => event,
             Err(err) => bail!("parsing '{}' failed - {}", v, err),
         };
@@ -44,7 +44,7 @@ fn test_compute_next_event() -> Result<(), Error> {
     };
 
     let test_never = |v: &'static str, last: i64| -> Result<(), Error> {
-        let event = match parse_calendar_event(v) {
+        let event: CalendarEvent = match v.parse() {
             Ok(event) => event,
             Err(err) => bail!("parsing '{}' failed - {}", v, err),
         };
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox 14/14] proxmox-time: time-span: implement FromStr
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (12 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 13/14] proxmox-time: calendar_events: implement FromStr Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox-backup 1/1] remove use of deprecated functions from proxmox-time Dominik Csapak
  2021-12-01  6:20 ` [pbs-devel] applied: [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dietmar Maurer
  15 siblings, 0 replies; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

and deprecate 'parse_time_span'

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 proxmox-time/src/test.rs      |  2 +-
 proxmox-time/src/time_span.rs | 13 +++++++++++--
 2 files changed, 12 insertions(+), 3 deletions(-)

diff --git a/proxmox-time/src/test.rs b/proxmox-time/src/test.rs
index 0476cc1..ec63a37 100644
--- a/proxmox-time/src/test.rs
+++ b/proxmox-time/src/test.rs
@@ -225,7 +225,7 @@ fn test_calendar_event_weekday() -> Result<(), Error> {
 #[test]
 fn test_time_span_parser() -> Result<(), Error> {
     let test_value = |ts_str: &str, expect: f64| -> Result<(), Error> {
-        let ts = parse_time_span(ts_str)?;
+        let ts: TimeSpan = ts_str.parse()?;
         assert_eq!(f64::from(ts), expect, "{}", ts_str);
         Ok(())
     };
diff --git a/proxmox-time/src/time_span.rs b/proxmox-time/src/time_span.rs
index 2f8cf59..0a6c9ca 100644
--- a/proxmox-time/src/time_span.rs
+++ b/proxmox-time/src/time_span.rs
@@ -198,9 +198,18 @@ fn parse_time_unit(i: &str) -> IResult<&str, &str> {
     }
 }
 
+impl std::str::FromStr for TimeSpan {
+    type Err = Error;
+
+    fn from_str(i: &str) -> Result<Self, Self::Err> {
+        parse_complete_line("calendar event", i, parse_time_span_incomplete)
+    }
+}
+
 /// Parse a [TimeSpan]
+#[deprecated="Use std::str::FromStr trait instead."]
 pub fn parse_time_span(i: &str) -> Result<TimeSpan, Error> {
-    parse_complete_line("time span", i, parse_time_span_incomplete)
+    i.parse()
 }
 
 fn parse_time_span_incomplete(mut i: &str) -> IResult<&str, TimeSpan> {
@@ -259,7 +268,7 @@ fn parse_time_span_incomplete(mut i: &str) -> IResult<&str, TimeSpan> {
 
 /// Verify the format of the [TimeSpan]
 pub fn verify_time_span(i: &str) -> Result<(), Error> {
-    parse_time_span(i)?;
+    let _: TimeSpan = i.parse()?;
     Ok(())
 }
 
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] [PATCH proxmox-backup 1/1] remove use of deprecated functions from proxmox-time
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (13 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 14/14] proxmox-time: time-span: " Dominik Csapak
@ 2021-11-30 12:12 ` Dominik Csapak
  2021-12-01  6:26   ` [pbs-devel] applied: " Dietmar Maurer
  2021-12-01  6:20 ` [pbs-devel] applied: [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dietmar Maurer
  15 siblings, 1 reply; 18+ messages in thread
From: Dominik Csapak @ 2021-11-30 12:12 UTC (permalink / raw)
  To: pbs-devel

Signed-off-by: Dominik Csapak <d.csapak@proxmox.com>
---
 pbs-api-types/src/tape/media_pool.rs |  6 +++---
 src/bin/proxmox-backup-proxy.rs      | 10 +++++-----
 src/server/jobstate.rs               |  6 +++---
 src/tape/inventory.rs                |  3 +--
 src/tape/media_pool.rs               |  3 +--
 src/tape/test/compute_media_state.rs |  4 ++--
 6 files changed, 15 insertions(+), 17 deletions(-)

diff --git a/pbs-api-types/src/tape/media_pool.rs b/pbs-api-types/src/tape/media_pool.rs
index 3b1cb0f5..c0cba2bd 100644
--- a/pbs-api-types/src/tape/media_pool.rs
+++ b/pbs-api-types/src/tape/media_pool.rs
@@ -11,7 +11,7 @@ use serde::{Deserialize, Serialize};
 
 use proxmox_schema::{api, Schema, StringSchema, ApiStringFormat, Updater};
 
-use proxmox_time::{parse_calendar_event, parse_time_span, CalendarEvent, TimeSpan};
+use proxmox_time::{CalendarEvent, TimeSpan};
 
 use crate::{
     PROXMOX_SAFE_ID_FORMAT,
@@ -62,7 +62,7 @@ impl std::str::FromStr for MediaSetPolicy {
             return Ok(MediaSetPolicy::AlwaysCreate);
         }
 
-        let event = parse_calendar_event(s)?;
+        let event = s.parse()?;
 
         Ok(MediaSetPolicy::CreateAt(event))
     }
@@ -97,7 +97,7 @@ impl std::str::FromStr for RetentionPolicy {
             return Ok(RetentionPolicy::KeepForever);
         }
 
-        let time_span = parse_time_span(s)?;
+        let time_span = s.parse()?;
 
         Ok(RetentionPolicy::ProtectFor(time_span))
     }
diff --git a/src/bin/proxmox-backup-proxy.rs b/src/bin/proxmox-backup-proxy.rs
index 0d852a7a..16ae67bb 100644
--- a/src/bin/proxmox-backup-proxy.rs
+++ b/src/bin/proxmox-backup-proxy.rs
@@ -47,7 +47,7 @@ use proxmox_backup::{
 };
 
 use pbs_buildcfg::configdir;
-use proxmox_time::{compute_next_event, parse_calendar_event};
+use proxmox_time::CalendarEvent;
 
 use pbs_api_types::{
     Authid, TapeBackupJobConfig, VerificationJobConfig, SyncJobConfig, DataStoreConfig,
@@ -565,7 +565,7 @@ async fn schedule_datastore_garbage_collection() {
             None => continue,
         };
 
-        let event = match parse_calendar_event(&event_str) {
+        let event: CalendarEvent = match event_str.parse() {
             Ok(event) => event,
             Err(err) => {
                 eprintln!("unable to parse schedule '{}' - {}", event_str, err);
@@ -585,7 +585,7 @@ async fn schedule_datastore_garbage_collection() {
             }
         };
 
-        let next = match compute_next_event(&event, last, false) {
+        let next = match event.compute_next_event(last, false) {
             Ok(Some(next)) => next,
             Ok(None) => continue,
             Err(err) => {
@@ -1024,7 +1024,7 @@ fn generate_host_stats_sync() {
 }
 
 fn check_schedule(worker_type: &str, event_str: &str, id: &str) -> bool {
-    let event = match parse_calendar_event(event_str) {
+    let event: CalendarEvent = match event_str.parse() {
         Ok(event) => event,
         Err(err) => {
             eprintln!("unable to parse schedule '{}' - {}", event_str, err);
@@ -1040,7 +1040,7 @@ fn check_schedule(worker_type: &str, event_str: &str, id: &str) -> bool {
         }
     };
 
-    let next = match compute_next_event(&event, last, false) {
+    let next = match event.compute_next_event(last, false) {
         Ok(Some(next)) => next,
         Ok(None) => return false,
         Err(err) => {
diff --git a/src/server/jobstate.rs b/src/server/jobstate.rs
index adb1bbe2..f874725a 100644
--- a/src/server/jobstate.rs
+++ b/src/server/jobstate.rs
@@ -46,7 +46,7 @@ use proxmox_sys::fs::{
     create_path, file_read_optional_string, replace_file, CreateOptions,
 };
 
-use proxmox_time::{compute_next_event, parse_calendar_event};
+use proxmox_time::CalendarEvent;
 
 use pbs_buildcfg::PROXMOX_BACKUP_STATE_DIR_M;
 use pbs_config::{open_backup_lockfile, BackupLockGuard};
@@ -339,9 +339,9 @@ pub fn compute_schedule_status(
     status.last_run_endtime = endtime;
 
     if let Some(schedule) = schedule {
-        if let Ok(event) = parse_calendar_event(&schedule) {
+        if let Ok(event) = schedule.parse::<CalendarEvent>() {
             // ignore errors
-            status.next_run = compute_next_event(&event, last, false).unwrap_or(None);
+            status.next_run = event.compute_next_event(last, false).unwrap_or(None);
         }
     }
 
diff --git a/src/tape/inventory.rs b/src/tape/inventory.rs
index 4ecbb425..1f0b41f8 100644
--- a/src/tape/inventory.rs
+++ b/src/tape/inventory.rs
@@ -33,7 +33,6 @@ use serde_json::json;
 use proxmox_sys::fs::{replace_file, file_get_json, CreateOptions};
 use proxmox_uuid::Uuid;
 
-use proxmox_time::compute_next_event;
 use pbs_config::BackupLockGuard;
 use pbs_api_types::{MediaSetPolicy, RetentionPolicy, MediaStatus, MediaLocation};
 
@@ -534,7 +533,7 @@ impl Inventory {
                         set_start_time
                     }
                     MediaSetPolicy::CreateAt(ref event) => {
-                        match compute_next_event(event, set_start_time, false) {
+                        match event.compute_next_event(set_start_time, false) {
                             Ok(Some(next)) => next,
                             Ok(None) | Err(_) => return i64::MAX,
                         }
diff --git a/src/tape/media_pool.rs b/src/tape/media_pool.rs
index 68908768..70713cc3 100644
--- a/src/tape/media_pool.rs
+++ b/src/tape/media_pool.rs
@@ -18,7 +18,6 @@ use pbs_api_types::{
     Fingerprint, MediaStatus, MediaLocation, MediaSetPolicy, RetentionPolicy,
     MediaPoolConfig,
 };
-use proxmox_time::compute_next_event;
 use pbs_config::BackupLockGuard;
 
 use crate::tape::{
@@ -291,7 +290,7 @@ impl MediaPool {
                 }
                 MediaSetPolicy::CreateAt(event) => {
                     if let Some(set_start_time) = self.inventory.media_set_start_time(&self.current_media_set.uuid()) {
-                        if let Ok(Some(alloc_time)) = compute_next_event(event, set_start_time as i64, false) {
+                        if let Ok(Some(alloc_time)) = event.compute_next_event(set_start_time as i64, false) {
                             if current_time >= alloc_time {
                                 create_new_set = Some(String::from("policy CreateAt event triggered"));
                             }
diff --git a/src/tape/test/compute_media_state.rs b/src/tape/test/compute_media_state.rs
index c1dfdba7..78e32190 100644
--- a/src/tape/test/compute_media_state.rs
+++ b/src/tape/test/compute_media_state.rs
@@ -100,8 +100,8 @@ fn test_media_expire_time() -> Result<(), Error> {
     let sl2= MediaSetLabel::with_data("p1", Uuid::generate(), 0, ctime + 120, None);
     let tape2_uuid = inventory.generate_used_tape("tape2", sl2, 0);
 
-    let event = proxmox_time::parse_calendar_event("*:0/2")?;
-    let span = proxmox_time::parse_time_span("120 seconds")?;
+    let event = "*:0/2".parse()?;
+    let span = "120 seconds".parse()?;
 
     let pool = MediaPool::new(
         "p1",
-- 
2.30.2





^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] applied: [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time
  2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
                   ` (14 preceding siblings ...)
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox-backup 1/1] remove use of deprecated functions from proxmox-time Dominik Csapak
@ 2021-12-01  6:20 ` Dietmar Maurer
  15 siblings, 0 replies; 18+ messages in thread
From: Dietmar Maurer @ 2021-12-01  6:20 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Dominik Csapak

applied




^ permalink raw reply	[flat|nested] 18+ messages in thread

* [pbs-devel] applied: [PATCH proxmox-backup 1/1] remove use of deprecated functions from proxmox-time
  2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox-backup 1/1] remove use of deprecated functions from proxmox-time Dominik Csapak
@ 2021-12-01  6:26   ` Dietmar Maurer
  0 siblings, 0 replies; 18+ messages in thread
From: Dietmar Maurer @ 2021-12-01  6:26 UTC (permalink / raw)
  To: Proxmox Backup Server development discussion, Dominik Csapak

applied




^ permalink raw reply	[flat|nested] 18+ messages in thread

end of thread, other threads:[~2021-12-01  6:26 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-11-30 12:11 [pbs-devel] [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dominik Csapak
2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 01/14] proxmox-time: calendar-events: implement repeated ranges Dominik Csapak
2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 02/14] proxmox-time: calendar-events: make hour optional Dominik Csapak
2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 03/14] proxmox-time: move common parse functions to parse_helpers Dominik Csapak
2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 04/14] proxmox-time: move WeekDays into own file Dominik Csapak
2021-11-30 12:11 ` [pbs-devel] [PATCH proxmox 05/14] proxmox-time: split DateTimeValue " Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 06/14] proxmox-time: move parse_daily_duration to daily_duration.rs Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 07/14] proxmox-time: daily_duration.rs: rustfmt Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 08/14] proxmox-time: move CalendarEvent into calendar_events.rs Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 09/14] proxmox-time: move TimeSpan into time_span.rs Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 10/14] proxmox-time: move tests from time.rs to test.rs Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 11/14] proxmox-time: lib.rs: rustfmt Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 12/14] proxmox-time: calendar-events: make compute_next_event a method Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 13/14] proxmox-time: calendar_events: implement FromStr Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox 14/14] proxmox-time: time-span: " Dominik Csapak
2021-11-30 12:12 ` [pbs-devel] [PATCH proxmox-backup 1/1] remove use of deprecated functions from proxmox-time Dominik Csapak
2021-12-01  6:26   ` [pbs-devel] applied: " Dietmar Maurer
2021-12-01  6:20 ` [pbs-devel] applied: [PATCH proxmox/proxmox-backup 00/14] improve & cleanup proxmox-time Dietmar Maurer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
Service provided by Proxmox Server Solutions GmbH | Privacy | Legal