public inbox for pve-devel@lists.proxmox.com
 help / color / mirror / Atom feed
From: Dominik Csapak <d.csapak@proxmox.com>
To: pve-devel@lists.proxmox.com
Subject: [pve-devel] [PATCH common 1/1] CalendarEvent: use rust implementation
Date: Wed,  1 Dec 2021 09:55:14 +0100	[thread overview]
Message-ID: <20211201085514.1648428-3-d.csapak@proxmox.com> (raw)
In-Reply-To: <20211201085514.1648428-1-d.csapak@proxmox.com>

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@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





  parent reply	other threads:[~2021-12-01  8:55 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2021-12-01  8:55 [pve-devel] [PATCH proxmox-perl-rs/common] use calendar-events from rust Dominik Csapak
2021-12-01  8:55 ` [pve-devel] [PATCH 1/1] pve-rs: add PVE::RS::CalendarEvent Dominik Csapak
2021-12-01  8:55 ` Dominik Csapak [this message]
2022-01-13 16:20   ` [pve-devel] [PATCH common 1/1] CalendarEvent: use rust implementation Thomas Lamprecht
2022-01-13 14:45 ` [pve-devel] applied-series: [PATCH proxmox-perl-rs/common] use calendar-events from rust Thomas Lamprecht

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20211201085514.1648428-3-d.csapak@proxmox.com \
    --to=d.csapak@proxmox.com \
    --cc=pve-devel@lists.proxmox.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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