mirror of
https://github.com/crawlab-team/crawlab.git
synced 2026-01-22 17:31:03 +01:00
177 lines
3.9 KiB
Go
177 lines
3.9 KiB
Go
package utils
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// cronBounds provides a range of acceptable values (plus a map of name to value).
|
|
type cronBounds struct {
|
|
min, max uint
|
|
names map[string]uint
|
|
}
|
|
|
|
type cronUtils struct {
|
|
// The cronBounds for each field.
|
|
seconds cronBounds
|
|
minutes cronBounds
|
|
hours cronBounds
|
|
dom cronBounds
|
|
months cronBounds
|
|
dow cronBounds
|
|
|
|
// Set the top bit if a star was included in the expression.
|
|
starBit uint64
|
|
}
|
|
|
|
// getRange returns the bits indicated by the given expression:
|
|
// number | number "-" number [ "/" number ]
|
|
// or error parsing range.
|
|
func (u *cronUtils) getRange(expr string, r cronBounds) (uint64, error) {
|
|
var (
|
|
start, end, step uint
|
|
rangeAndStep = strings.Split(expr, "/")
|
|
lowAndHigh = strings.Split(rangeAndStep[0], "-")
|
|
singleDigit = len(lowAndHigh) == 1
|
|
err error
|
|
)
|
|
|
|
var extra uint64
|
|
if lowAndHigh[0] == "*" || lowAndHigh[0] == "?" {
|
|
start = r.min
|
|
end = r.max
|
|
extra = CronUtils.starBit
|
|
} else {
|
|
start, err = u.parseIntOrName(lowAndHigh[0], r.names)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
switch len(lowAndHigh) {
|
|
case 1:
|
|
end = start
|
|
case 2:
|
|
end, err = u.parseIntOrName(lowAndHigh[1], r.names)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
default:
|
|
return 0, fmt.Errorf("too many hyphens: %s", expr)
|
|
}
|
|
}
|
|
|
|
switch len(rangeAndStep) {
|
|
case 1:
|
|
step = 1
|
|
case 2:
|
|
step, err = u.mustParseInt(rangeAndStep[1])
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// Special handling: "N/step" means "N-max/step".
|
|
if singleDigit {
|
|
end = r.max
|
|
}
|
|
if step > 1 {
|
|
extra = 0
|
|
}
|
|
default:
|
|
return 0, fmt.Errorf("too many slashes: %s", expr)
|
|
}
|
|
|
|
if start < r.min {
|
|
return 0, fmt.Errorf("beginning of range (%d) below minimum (%d): %s", start, r.min, expr)
|
|
}
|
|
if end > r.max {
|
|
return 0, fmt.Errorf("end of range (%d) above maximum (%d): %s", end, r.max, expr)
|
|
}
|
|
if start > end {
|
|
return 0, fmt.Errorf("beginning of range (%d) beyond end of range (%d): %s", start, end, expr)
|
|
}
|
|
if step == 0 {
|
|
return 0, fmt.Errorf("step of range should be a positive number: %s", expr)
|
|
}
|
|
|
|
return u.getBits(start, end, step) | extra, nil
|
|
}
|
|
|
|
// parseIntOrName returns the (possibly-named) integer contained in expr.
|
|
func (u *cronUtils) parseIntOrName(expr string, names map[string]uint) (uint, error) {
|
|
if names != nil {
|
|
if namedInt, ok := names[strings.ToLower(expr)]; ok {
|
|
return namedInt, nil
|
|
}
|
|
}
|
|
return u.mustParseInt(expr)
|
|
}
|
|
|
|
// mustParseInt parses the given expression as an int or returns an error.
|
|
func (u *cronUtils) mustParseInt(expr string) (uint, error) {
|
|
num, err := strconv.Atoi(expr)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("failed to parse int from %s: %s", expr, err)
|
|
}
|
|
if num < 0 {
|
|
return 0, fmt.Errorf("negative number (%d) not allowed: %s", num, expr)
|
|
}
|
|
|
|
return uint(num), nil
|
|
}
|
|
|
|
// getBits sets all bits in the range [min, max], modulo the given step size.
|
|
func (u *cronUtils) getBits(min, max, step uint) uint64 {
|
|
var bits uint64
|
|
|
|
// If step is 1, use shifts.
|
|
if step == 1 {
|
|
return ^(math.MaxUint64 << (max + 1)) & (math.MaxUint64 << min)
|
|
}
|
|
|
|
// Else, use a simple loop.
|
|
for i := min; i <= max; i += step {
|
|
bits |= 1 << i
|
|
}
|
|
return bits
|
|
}
|
|
|
|
// all returns all bits within the given cronBounds. (plus the star bit)
|
|
func (u *cronUtils) all(r cronBounds) uint64 {
|
|
return u.getBits(r.min, r.max, 1) | CronUtils.starBit
|
|
}
|
|
|
|
var CronUtils = cronUtils{
|
|
// The cronBounds for each field.
|
|
seconds: cronBounds{0, 59, nil},
|
|
minutes: cronBounds{0, 59, nil},
|
|
hours: cronBounds{0, 23, nil},
|
|
dom: cronBounds{1, 31, nil},
|
|
months: cronBounds{1, 12, map[string]uint{
|
|
"jan": 1,
|
|
"feb": 2,
|
|
"mar": 3,
|
|
"apr": 4,
|
|
"may": 5,
|
|
"jun": 6,
|
|
"jul": 7,
|
|
"aug": 8,
|
|
"sep": 9,
|
|
"oct": 10,
|
|
"nov": 11,
|
|
"dec": 12,
|
|
}},
|
|
dow: cronBounds{0, 6, map[string]uint{
|
|
"sun": 0,
|
|
"mon": 1,
|
|
"tue": 2,
|
|
"wed": 3,
|
|
"thu": 4,
|
|
"fri": 5,
|
|
"sat": 6,
|
|
}},
|
|
|
|
// Set the top bit if a star was included in the expression.
|
|
starBit: 1 << 63,
|
|
}
|