[pve-devel] [PATCH common 1/1] CalendarEvent: use rust implementation

Dominik Csapak d.csapak at proxmox.com
Wed Dec 1 09:55:14 CET 2021


by replacing the parsing code and 'compute_next_event' by their
PVE::RS::CalendarEvent equivalent

adapt the tests, since we do not have access to the internal structure
(and even if we had, it would be different) and the error messages
are different

the 'compute_next_event' and parsing tests still pass though

Signed-off-by: Dominik Csapak <d.csapak at proxmox.com>
---
 src/PVE/CalendarEvent.pm    | 251 +-----------------------------------
 test/calendar_event_test.pl |  42 +++---
 2 files changed, 23 insertions(+), 270 deletions(-)

diff --git a/src/PVE/CalendarEvent.pm b/src/PVE/CalendarEvent.pm
index 56e9923..e2bf53a 100644
--- a/src/PVE/CalendarEvent.pm
+++ b/src/PVE/CalendarEvent.pm
@@ -6,6 +6,7 @@ use Data::Dumper;
 use Time::Local;
 use PVE::JSONSchema;
 use PVE::Tools qw(trim);
+use PVE::RS::CalendarEvent;
 
 # Note: This class implements a parser/utils for systemd like calendar exents
 # Date specification is currently not implemented
@@ -43,259 +44,13 @@ sub parse_calendar_event {
 	die "unable to parse calendar event - event is empty\n";
     }
 
-    my $parse_single_timespec = sub {
-	my ($p, $max, $matchall_ref, $res_hash) = @_;
-
-	if ($p =~ m/^((?:\*|[0-9]+))(?:\/([1-9][0-9]*))?$/) {
-	    my ($start, $repetition) = ($1, $2);
-	    if (defined($repetition)) {
-		$repetition = int($repetition);
-		$start = $start eq '*' ? 0 : int($start);
-		die "value '$start' out of range\n" if $start >= $max;
-		die "repetition '$repetition' out of range\n" if $repetition >= $max;
-		while ($start < $max) {
-		    $res_hash->{$start} = 1;
-		    $start += $repetition;
-		}
-	    } else {
-		if ($start eq '*') {
-		    $$matchall_ref = 1;
-		} else {
-		    $start = int($start);
-		    die "value '$start' out of range\n" if $start >= $max;
-		    $res_hash->{$start} = 1;
-		}
-	    }
-	} elsif ($p =~ m/^([0-9]+)\.\.([1-9][0-9]*)$/) {
-	    my ($start, $end) = (int($1), int($2));
-	    die "range start '$start' out of range\n" if $start >= $max;
-	    die "range end '$end' out of range\n" if $end >= $max || $end < $start;
-	    for (my $i = $start; $i <= $end; $i++) {
-		$res_hash->{$i} = 1;
-	    }
-	} else {
-	    die "unable to parse calendar event '$p'\n";
-	}
-    };
-
-    my $h = undef;
-    my $m = undef;
-
-    my $matchall_minutes = 0;
-    my $matchall_hours = 0;
-    my $minutes_hash = {};
-    my $hours_hash = {};
-
-    my $dowsel = join('|', keys %$dow_names);
-
-    my $dow_hash;
-
-    my $parse_dowspec = sub {
-	my ($p) = @_;
-
-	if ($p =~ m/^($dowsel)$/i) {
-	    $dow_hash->{$dow_names->{lc($1)}} = 1;
-	} elsif ($p =~ m/^($dowsel)\.\.($dowsel)$/i) {
-	    my $start = $dow_names->{lc($1)};
-	    my $end = $dow_names->{lc($2)} || 7;
-	    die "wrong order in range '$p'\n" if $end < $start;
-	    for (my $i = $start; $i <= $end; $i++) {
-		$dow_hash->{($i % 7)} = 1;
-	    }
-	} else {
-	    die "unable to parse weekday specification '$p'\n";
-	}
-    };
-
-    my @parts = split(/\s+/, $event);
-    my $utc = (@parts && uc($parts[-1]) eq 'UTC');
-    pop @parts if $utc;
-
-
-    if ($parts[0] =~ m/$dowsel/i) {
-	my $dow_spec = shift @parts;
-	foreach my $p (split(',', $dow_spec)) {
-	    $parse_dowspec->($p);
-	}
-    } else {
-	$dow_hash = { 0 => 1, 1 => 1, 2 => 1, 3 => 1, 4 => 1, 5=> 1, 6 => 1 };
-    }
-
-    if (scalar(@parts) && $parts[0] =~ m/\-/) {
-	my $date_spec = shift @parts;
-	die "date specification not implemented";
-    }
-
-    my $time_spec = shift(@parts) // "00:00";
-    my $chars = '[0-9*/.,]';
-
-    if ($time_spec =~ m/^($chars+):($chars+)$/) {
-	my ($p1, $p2) = ($1, $2);
-	foreach my $p (split(',', $p1)) {
-	    $parse_single_timespec->($p, 24, \$matchall_hours, $hours_hash);
-	}
-	foreach my $p (split(',', $p2)) {
-	    $parse_single_timespec->($p, 60, \$matchall_minutes, $minutes_hash);
-	}
-    } elsif ($time_spec =~ m/^($chars)+$/) { # minutes only
-	$matchall_hours = 1;
-	foreach my $p (split(',', $time_spec)) {
-	    $parse_single_timespec->($p, 60, \$matchall_minutes, $minutes_hash);
-	}
-
-    } else {
-	die "unable to parse calendar event\n";
-    }
-
-    die "unable to parse calendar event - unused parts\n" if scalar(@parts);
-
-    if ($matchall_hours) {
-	$h = '*';
-    } else {
-	$h = [ sort { $a <=> $b } keys %$hours_hash ];
-    }
-
-    if ($matchall_minutes) {
-	$m = '*';
-    } else {
-	$m = [ sort { $a <=> $b } keys %$minutes_hash ];
-    }
-
-    return { h => $h, m => $m, dow => [ sort keys %$dow_hash ], utc => $utc };
-}
-
-sub is_leap_year($) {
-    return 0 if $_[0] % 4;
-    return 1 if $_[0] % 100;
-    return 0 if $_[0] % 400;
-    return 1;
-}
-
-# mon = 0.. (Jan = 0)
-sub days_in_month($$) {
-    my ($mon, $year) = @_;
-    return 28 + is_leap_year($year) if $mon == 1;
-    return (31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31)[$mon];
-}
-
-# day = 1..
-# mon = 0.. (Jan = 0)
-sub wrap_time($) {
-    my ($time) = @_;
-    my ($sec, $min, $hour, $day, $mon, $year, $wday) = @$time;
-
-    use integer;
-    if ($sec >= 60) {
-	$min += $sec / 60;
-	$sec %= 60;
-    }
-
-    if ($min >= 60) {
-	$hour += $min / 60;
-	$min %= 60;
-    }
-
-    if ($hour >= 24) {
-	$day  += $hour / 24;
-	$wday += $hour / 24;
-	$hour %= 24;
-    }
-
-    # Translate to 0..($days_in_mon-1)
-    --$day;
-    while (1) {
-	my $days_in_mon = days_in_month($mon % 12, $year);
-	last if $day < $days_in_mon;
-	# Wrap one month
-	$day -= $days_in_mon;
-	++$mon;
-    }
-    # Translate back to 1..$days_in_mon
-    ++$day;
-
-    if ($mon >= 12) {
-	$year += $mon / 12;
-	$mon %= 12;
-    }
-
-    $wday %= 7;
-    return [$sec, $min, $hour, $day, $mon, $year, $wday];
-}
-
-# helper as we need to keep weekdays in sync
-sub time_add_days($$) {
-    my ($time, $inc) = @_;
-    my ($sec, $min, $hour, $day, $mon, $year, $wday) = @$time;
-    return wrap_time([$sec, $min, $hour, $day + $inc, $mon, $year, $wday + $inc]);
+    return PVE::RS::CalendarEvent->new($event);
 }
 
 sub compute_next_event {
     my ($calspec, $last) = @_;
 
-    my $hspec = $calspec->{h};
-    my $mspec = $calspec->{m};
-    my $dowspec = $calspec->{dow};
-    my $utc = $calspec->{utc};
-
-    $last += 60; # at least one minute later
-
-    my $t = [$utc ? gmtime($last) : localtime($last)];
-    $t->[0] = 0;     # we're not interested in seconds, actually
-    $t->[5] += 1900; # real years for clarity
-
-    outer: for (my $i = 0; $i < 1000; ++$i) {
-	my $wday = $t->[6];
-	foreach my $d (@$dowspec) {
-	    goto this_wday if $d == $wday;
-	    if ($d > $wday) {
-		$t->[0] = $t->[1] = $t->[2] = 0; # sec = min = hour = 0
-		$t = time_add_days($t, $d - $wday);
-		next outer;
-	    }
-	}
-	# Test next week:
-	$t->[0] = $t->[1] = $t->[2] = 0; # sec = min = hour = 0
-	$t = time_add_days($t, 7 - $wday);
-	next outer;
-    this_wday:
-
-	goto this_hour if $hspec eq '*';
-	my $hour = $t->[2];
-	foreach my $h (@$hspec) {
-	    goto this_hour if $h == $hour;
-	    if ($h > $hour) {
-		$t->[0] = $t->[1] = 0; # sec = min = 0
-		$t->[2] = $h;          # hour = $h
-		next outer;
-	    }
-	}
-	# Test next day:
-	$t->[0] = $t->[1] = $t->[2] = 0; # sec = min = hour = 0
-	$t = time_add_days($t, 1);
-	next outer;
-    this_hour:
-
-	goto this_min if $mspec eq '*';
-	my $min = $t->[1];
-	foreach my $m (@$mspec) {
-	    goto this_min if $m == $min;
-	    if ($m > $min) {
-		$t->[0] = 0;  # sec = 0
-		$t->[1] = $m; # min = $m
-		next outer;
-	    }
-	}
-	# Test next hour:
-	$t->[0] = $t->[1] = 0; # sec = min = hour = 0
-	$t->[2]++;
-	$t = wrap_time($t);
-	next outer;
-    this_min:
-
-	return $utc ? timegm(@$t) : timelocal(@$t);
-    }
-
-    die "unable to compute next calendar event\n";
+    return $calspec->compute_next_event($last);
 }
 
 1;
diff --git a/test/calendar_event_test.pl b/test/calendar_event_test.pl
index abbd74c..4572965 100755
--- a/test/calendar_event_test.pl
+++ b/test/calendar_event_test.pl
@@ -18,7 +18,7 @@ my $alldays = [0,1,2,3,4,5,6];
 my $tests = [
     [
      '*',
-     { h => '*', m => '*', dow => $alldays },
+     undef,
      [
       [0, 60],
       [30, 60],
@@ -28,7 +28,7 @@ my $tests = [
     ],
     [
      '*/10',
-     { h => '*', m => [0, 10, 20, 30, 40, 50], dow => $alldays },
+     undef,
      [
       [0, 600],
       [599, 600],
@@ -38,7 +38,7 @@ my $tests = [
     ],
     [
      '*/12:0' ,
-     { h => [0, 12], m => [0], dow => $alldays },
+     undef,
      [
       [ 10, 43200],
       [ 13*3600, 24*3600],
@@ -46,7 +46,7 @@ my $tests = [
     ],
     [
      '1/12:0/15' ,
-     { h => [1, 13], m => [0, 15, 30, 45], dow => $alldays },
+     undef,
      [
       [0, 3600],
       [3600, 3600+15*60],
@@ -61,7 +61,7 @@ my $tests = [
     ],
     [
      '1,4,6',
-     { h => '*', m => [1, 4, 6], dow => $alldays},
+     undef,
      [
       [0, 60],
       [60, 4*60],
@@ -71,15 +71,15 @@ my $tests = [
     ],
     [
      '0..3',
-     { h => '*', m => [ 0, 1, 2, 3 ], dow => $alldays },
+     undef,
     ],
     [
      '23..23:0..3',
-     { h => [ 23 ], m => [ 0, 1, 2, 3 ], dow => $alldays },
+     undef,
     ],
     [
      'Mon',
-     { h => [0], m => [0], dow => [1] },
+     undef,
      [
       [0, 4*86400], # Note: Epoch 0 is Thursday, 1. January 1970
       [4*86400, 11*86400],
@@ -88,7 +88,7 @@ my $tests = [
     ],
     [
      'sat..sun',
-     { h => [0], m => [0], dow => [0, 6] },
+     undef,
      [
       [0, 2*86400],
       [2*86400, 3*86400],
@@ -97,7 +97,7 @@ my $tests = [
     ],
     [
      'sun..sat',
-     { h => [0], m => [0], dow => $alldays },
+     undef,
     ],
     [
      'Fri..Mon',
@@ -105,15 +105,15 @@ my $tests = [
     ],
     [
      'wed,mon..tue,fri',
-     { h => [0], m => [0], dow => [ 1, 2, 3, 5] },
+     undef,
     ],
     [
      'mon */15',
-     { h => '*', m =>  [0, 15, 30, 45], dow => [1]},
+     undef,
     ],
     [
     '22/1:0',
-     { h => [22, 23], m => [0], dow => $alldays },
+    undef,
      [
 	[0, 22*60*60],
 	[22*60*60, 23*60*60],
@@ -122,7 +122,7 @@ my $tests = [
     ],
     [
      '*/2:*',
-     { h => [0,2,4,6,8,10,12,14,16,18,20,22], m => '*', dow => $alldays },
+     undef,
      [
 	[0, 60],
 	[60*60, 2*60*60],
@@ -131,7 +131,7 @@ my $tests = [
     ],
     [
      '20..22:*/30',
-     { h => [20,21,22], m => [0,30], dow => $alldays },
+     undef,
      [
 	[0, 20*60*60],
 	[20*60*60, 20*60*60 + 30*60],
@@ -164,7 +164,7 @@ my $tests = [
     ],
     [
      '0,1,3..5',
-     { h => '*', m => [0,1,3,4,5], dow => $alldays },
+     undef,
      [
 	[0, 60],
 	[60, 3*60],
@@ -173,7 +173,7 @@ my $tests = [
     ],
     [
      '2,4:0,1,3..5',
-     { h => [2,4], m => [0,1,3,4,5], dow => $alldays },
+     undef,
      [
 	[0, 2*60*60],
 	[2*60*60 + 60, 2*60*60 + 3*60],
@@ -185,18 +185,16 @@ my $tests = [
 foreach my $test (@$tests) {
     my ($t, $expect, $nextsync) = @$test;
 
+    $expect //= {};
+
     my $timespec;
     eval { $timespec = PVE::CalendarEvent::parse_calendar_event($t); };
     my $err = $@;
-    delete $timespec->{utc};
 
     if ($expect->{error}) {
 	chomp $err if $err;
-	$timespec = { error => $err } if $err;
-	is_deeply($timespec, $expect, "expect parse error on '$t' - $expect->{error}");
+	ok(defined($err) == defined($expect->{error}), "parsing '$t' failed expectedly");
 	die "unable to execute nextsync tests" if $nextsync;
-    } else {
-	is_deeply($timespec, $expect, "parse '$t'");
     }
 
     next if !$nextsync;
-- 
2.30.2






More information about the pve-devel mailing list