refactor: make cron.expression into cron module

Jef Roosens 2023-01-14 16:52:30 +01:00
parent dce00bfab6
commit 3f1aea13e2
15 changed files with 92 additions and 89 deletions

View File

@ -13,6 +13,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* Search in list of targets using API & CLI
* Allow filtering targets by arch value
### Changed
* Rewrote cron expression logic in C
## [0.5.0](https://git.rustybever.be/vieter-v/vieter/src/tag/0.5.0)
### Added

View File

@ -1,7 +1,7 @@
module build
import models { BuildConfig, Target }
import cron.expression { CronExpression, parse_expression }
import cron
import time
import datatypes { MinHeap }
import util
@ -13,7 +13,7 @@ pub mut:
// Next timestamp from which point this job is allowed to be executed
timestamp time.Time
// Required for calculating next timestamp after having pop'ed a job
ce &CronExpression = unsafe { nil }
ce &cron.Expression = unsafe { nil }
// Actual build config sent to the agent
config BuildConfig
// Whether this is a one-time job
@ -30,7 +30,7 @@ fn (r1 BuildJob) < (r2 BuildJob) bool {
// for each architecture. Agents receive jobs from this queue.
pub struct BuildJobQueue {
// Schedule to use for targets without explicitely defined cron expression
default_schedule &CronExpression
default_schedule &cron.Expression
// Base image to use for targets without defined base image
default_base_image string
mut:
@ -44,7 +44,7 @@ mut:
}
// new_job_queue initializes a new job queue
pub fn new_job_queue(default_schedule &CronExpression, default_base_image string) BuildJobQueue {
pub fn new_job_queue(default_schedule &cron.Expression, default_base_image string) BuildJobQueue {
return BuildJobQueue{
default_schedule: unsafe { default_schedule }
default_base_image: default_base_image
@ -85,7 +85,7 @@ pub fn (mut q BuildJobQueue) insert(input InsertConfig) ! {
if !input.now {
ce := if input.target.schedule != '' {
parse_expression(input.target.schedule) or {
cron.parse_expression(input.target.schedule) or {
return error("Error while parsing cron expression '$input.target.schedule' (id $input.target.id): $err.msg()")
}
} else {

View File

@ -1,7 +1,7 @@
module schedule
import cli
import cron.expression { parse_expression }
import cron
import time
// cmd returns the cli submodule for previewing a cron schedule.
@ -19,7 +19,7 @@ pub fn cmd() cli.Command {
},
]
execute: fn (cmd cli.Command) ! {
ce := parse_expression(cmd.args.join(' '))!
ce := cron.parse_expression(cmd.args.join(' '))!
count := cmd.flags.get_int('count')!
for t in ce.next_n(time.now(), count)! {

View File

@ -2,7 +2,7 @@ module targets
import cli
import conf as vconf
import cron.expression { parse_expression }
import cron
import client { NewTarget }
import console
import models { TargetFilter }
@ -295,7 +295,7 @@ fn patch(conf Config, id string, params map[string]string) ! {
// We check the cron expression first because it's useless to send an
// invalid one to the server.
if 'schedule' in params && params['schedule'] != '' {
parse_expression(params['schedule']) or {
cron.parse_expression(params['schedule']) or {
return error('Invalid cron expression: $err.msg()')
}
}

View File

@ -3,11 +3,11 @@
const uint8_t month_days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
struct cron_expression *ce_init() {
return malloc(sizeof(struct cron_expression));
cron_expression *ce_init() {
return malloc(sizeof(cron_expression));
}
void ce_free(struct cron_expression *ce) {
void ce_free(cron_expression *ce) {
free(ce->months);
free(ce->days);
free(ce->hours);
@ -15,7 +15,7 @@ void ce_free(struct cron_expression *ce) {
free(ce);
}
int ce_next(struct cron_simple_time *out, struct cron_expression *ce, struct cron_simple_time *ref) {
int ce_next(cron_simple_time *out, cron_expression *ce, cron_simple_time *ref) {
// For all of these values, the rule is the following: if their value is
// the length of their respective array in the CronExpression object, that
// means we've looped back around. This means that the "bigger" value has
@ -103,7 +103,7 @@ int ce_next(struct cron_simple_time *out, struct cron_expression *ce, struct cro
return 0;
}
int ce_next_from_now(struct cron_simple_time *out, struct cron_expression *ce) {
int ce_next_from_now(cron_simple_time *out, cron_expression *ce) {
time_t t = time(NULL);
struct tm gm;
gmtime_r(&t, &gm);

View File

@ -0,0 +1,40 @@
#include <time.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
typedef enum cron_parse_error {
cron_parse_ok = 0,
cron_parse_invalid_expression = 1,
cron_parse_invalid_number = 2,
cron_parse_out_of_range = 3
} cron_parse_error;
typedef struct cron_expression {
uint8_t *minutes;
uint8_t *hours;
uint8_t *days;
uint8_t *months;
uint8_t minute_count;
uint8_t hour_count;
uint8_t day_count;
uint8_t month_count;
} cron_expression;
typedef struct cron_simple_time {
int year;
int month;
int day;
int hour;
int minute;
} cron_simple_time;
cron_expression *ce_init();
void cron_ce_free(cron_expression *ce);
int cron_ce_next(cron_simple_time *out, cron_expression *ce, cron_simple_time *ref);
int cron_ce_next_from_now(cron_simple_time *out, cron_expression *ce);
enum cron_parse_error cron_ce_parse_expression(cron_expression *out, char *s);

View File

@ -9,10 +9,10 @@ const uint8_t max[4] = {59, 23, 31, 12};
#define SAFE_ATOI(v,s,min,max) \
int _##v = atoi(s); \
if ((_##v) == 0 && strcmp((s), "0") != 0) { \
return CPEParseInvalidNumber; \
return cron_parse_invalid_number; \
} \
if (v < (min) || v > (max)) { \
return CPEParseOutOfRange; \
return cron_parse_out_of_range; \
} \
v = (uint8_t) (_##v);
@ -37,12 +37,12 @@ enum cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min, uint8_
if (s[0] == '*') {
// A '*' is only valid on its own
if (s[1] != '\0') {
return CPEParseInvalidExpression;
return cron_parse_invalid_expression;
}
*out = ~0;
return CPEParseOk;
return cron_parse_ok;
}
size_t slash_index = 0;
@ -90,7 +90,7 @@ enum cron_parse_error ce_parse_range(uint64_t *out, char *s, uint8_t min, uint8_
}
}
return CPEParseOk;
return cron_parse_ok;
}
/*
@ -108,7 +108,7 @@ enum cron_parse_error ce_parse_part(uint64_t *out, char *s, uint8_t min, uint8_t
next[0] = '\0';
res = ce_parse_range(out, s, min, max);
if (res != CPEParseOk) {
if (res != cron_parse_ok) {
return res;
}
@ -170,7 +170,7 @@ uint8_t bf_to_nums(uint8_t **out, uint64_t bf, uint8_t min, uint8_t max) {
/*
* Parse a cron expression string into a cron_expression struct.
*/
enum cron_parse_error ce_parse_expression(struct cron_expression *out, char *s) {
enum cron_parse_error ce_parse_expression(cron_expression *out, char *s) {
// The parsing functions modify the input string in-place
s = strdup(s);
char *orig_s = s;
@ -178,7 +178,7 @@ enum cron_parse_error ce_parse_expression(struct cron_expression *out, char *s)
uint8_t part_count = 0;
char *next;
enum cron_parse_error res = CPEParseOk;
enum cron_parse_error res = cron_parse_ok;
uint64_t bfs[4];
// Skip leading spaces
@ -190,7 +190,7 @@ enum cron_parse_error ce_parse_expression(struct cron_expression *out, char *s)
next[0] = '\0';
res = ce_parse_part(&bfs[part_count], s, min[part_count], max[part_count]);
if (res != CPEParseOk) {
if (res != cron_parse_ok) {
goto end;
}
@ -209,7 +209,7 @@ enum cron_parse_error ce_parse_expression(struct cron_expression *out, char *s)
if (part_count < 4 && s[0] != '\0') {
res = ce_parse_part(&bfs[part_count], s, min[part_count], max[part_count]);
if (res != CPEParseOk) {
if (res != cron_parse_ok) {
goto end;
}
@ -218,7 +218,7 @@ enum cron_parse_error ce_parse_expression(struct cron_expression *out, char *s)
// At least two parts need to be provided
if (part_count < 2) {
res = CPEParseInvalidExpression;
res = cron_parse_invalid_expression;
goto end;
}

View File

@ -1,4 +1,4 @@
module expression
module cron
#flag -I @VMODROOT/c
#flag @VMODROOT/c/parse.o
@ -16,7 +16,7 @@ pub struct C.cron_expression {
month_count u8
}
pub type CronExpression = C.cron_expression
pub type Expression = C.cron_expression
struct C.cron_simple_time {
year int
@ -26,6 +26,8 @@ struct C.cron_simple_time {
minute int
}
type SimpleTime = C.cron_simple_time
fn C.ce_init() &C.cron_expression
fn C.ce_free(ce &C.cron_expression)

View File

@ -1,8 +1,8 @@
module expression
module cron
import time
pub fn parse_expression(exp string) !&CronExpression {
pub fn parse_expression(exp string) !&Expression {
out := C.ce_init()
res := C.ce_parse_expression(out, exp.str)
@ -13,12 +13,12 @@ pub fn parse_expression(exp string) !&CronExpression {
return out
}
pub fn (ce &CronExpression) free() {
pub fn (ce &Expression) free() {
C.ce_free(ce)
}
pub fn (ce &CronExpression) next(ref time.Time) !time.Time {
st := C.cron_simple_time{
pub fn (ce &Expression) next(ref time.Time) !time.Time {
st := SimpleTime{
year: ref.year
month: ref.month
day: ref.day
@ -26,7 +26,7 @@ pub fn (ce &CronExpression) next(ref time.Time) !time.Time {
minute: ref.minute
}
out := C.cron_simple_time{}
out := SimpleTime{}
res := C.ce_next(&out, ce, &st)
if res != 0 {
@ -42,8 +42,8 @@ pub fn (ce &CronExpression) next(ref time.Time) !time.Time {
})
}
pub fn (ce &CronExpression) next_from_now() !time.Time {
out := C.cron_simple_time{}
pub fn (ce &Expression) next_from_now() !time.Time {
out := SimpleTime{}
res := C.ce_next_from_now(&out, ce)
if res != 0 {
@ -61,7 +61,7 @@ pub fn (ce &CronExpression) next_from_now() !time.Time {
// next_n returns the n next occurences of the expression, given a starting
// time.
pub fn (ce &CronExpression) next_n(ref time.Time, n int) ![]time.Time {
pub fn (ce &Expression) next_n(ref time.Time, n int) ![]time.Time {
mut times := []time.Time{cap: n}
times << ce.next(ref)!

View File

@ -1,43 +0,0 @@
#include <time.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
enum cron_parse_error {
CPEParseOk = 0,
CPEParseInvalidExpression = 1,
CPEParseInvalidNumber = 2,
CPEParseOutOfRange = 3
};
struct cron_expression {
uint8_t *minutes;
uint8_t *hours;
uint8_t *days;
uint8_t *months;
uint8_t minute_count;
uint8_t hour_count;
uint8_t day_count;
uint8_t month_count;
};
struct cron_simple_time {
int year;
int month;
int day;
int hour;
int minute;
};
struct cron_expression *ce_init();
void cron_ce_free(struct cron_expression *ce);
/**
* Given a
*/
int cron_ce_next(struct cron_simple_time *out, struct cron_expression *ce, struct ce_simple_time *ref);
int cron_ce_next_from_now(struct simple_time *out, struct cron_expression *ce);
enum cron_parse_error cron_ce_parse_expression(struct cron_expression *out, char *s);

View File

@ -1,4 +1,4 @@
module expression
module cron
// parse_range_error returns the returned error message. If the result is '',
// that means the function didn't error.

View File

@ -1,4 +1,4 @@
module expression
module cron
import time { parse }
@ -19,7 +19,7 @@ fn util_test_time(exp string, t1_str string, t2_str string) ! {
fn test_next_simple() ! {
// Very simple
/* util_test_time('0 3', '2002-01-01 00:00:00', '2002-01-01 03:00:00')! */
// util_test_time('0 3', '2002-01-01 00:00:00', '2002-01-01 03:00:00')!
// Overlap to next day
mut exp := '0 3 '
@ -28,9 +28,9 @@ fn test_next_simple() ! {
util_test_time('0 3/4', '2002-01-01 04:00:00', '2002-01-01 07:00:00')!
/* // Overlap to next month */
//// Overlap to next month
util_test_time('0 3', '2002-11-31 04:00:00', '2002-12-01 03:00:00')!
/* // Overlap to next year */
//// Overlap to next year
util_test_time('0 3', '2002-12-31 04:00:00', '2003-01-01 03:00:00')!
}

View File

@ -3,12 +3,12 @@ module server
import time
import models { BuildLog }
import os
import cron.expression { CronExpression }
import cron
const fallback_log_removal_frequency = 24 * time.hour
// log_removal_daemon removes old build logs every `log_removal_frequency`.
fn (mut app App) log_removal_daemon(schedule CronExpression) {
fn (mut app App) log_removal_daemon(schedule cron.Expression) {
mut start_time := time.Time{}
for {

View File

@ -7,7 +7,7 @@ import repo
import util
import db
import build { BuildJobQueue }
import cron.expression
import cron
import metrics
const (
@ -43,11 +43,11 @@ pub fn server(conf Config) ! {
util.exit_with_message(1, "'any' is not allowed as the value for default_arch.")
}
global_ce := expression.parse_expression(conf.global_schedule) or {
global_ce := cron.parse_expression(conf.global_schedule) or {
util.exit_with_message(1, 'Invalid global cron expression: $err.msg()')
}
log_removal_ce := expression.parse_expression(conf.log_removal_schedule) or {
log_removal_ce := cron.parse_expression(conf.log_removal_schedule) or {
util.exit_with_message(1, 'Invalid log removal cron expression: $err.msg()')
}