Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ci: update golangci-lint configuration #155

Merged
merged 1 commit into from
Feb 4, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,4 @@ jobs:
- name: Run golangci-lint
uses: golangci/golangci-lint-action@v6
with:
version: v1.61.0
version: v1.63.4
1 change: 1 addition & 0 deletions .golangci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ linters:
- misspell
- nolintlint
- prealloc
- reassign
- revive
- staticcheck
- stylecheck
Expand Down
4 changes: 2 additions & 2 deletions internal/csm/common_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ type CommonNode struct {

var _ csmNode = (*CommonNode)(nil)

func NewCommonNode(value, min, max int, values []int) *CommonNode {
return &CommonNode{value, min, max, values}
func NewCommonNode(value, lowerBound, upperBound int, values []int) *CommonNode {
return &CommonNode{value, lowerBound, upperBound, values}
}

func (n *CommonNode) Value() int {
Expand Down
10 changes: 6 additions & 4 deletions internal/csm/day_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,19 +17,21 @@ type DayNode struct {

var _ csmNode = (*DayNode)(nil)

func NewMonthDayNode(value, min, max, n int, dayOfMonthValues []int, month, year csmNode) *DayNode {
func NewMonthDayNode(value, lowerBound, upperBound, n int, dayOfMonthValues []int,
month, year csmNode) *DayNode {
return &DayNode{
c: CommonNode{value, min, max, dayOfMonthValues},
c: CommonNode{value, lowerBound, upperBound, dayOfMonthValues},
weekdayValues: make([]int, 0),
n: n,
month: month,
year: year,
}
}

func NewWeekDayNode(value, min, max, n int, dayOfWeekValues []int, month, year csmNode) *DayNode {
func NewWeekDayNode(value, lowerBound, upperBound, n int, dayOfWeekValues []int,
month, year csmNode) *DayNode {
return &DayNode{
c: CommonNode{value, min, max, make([]int, 0)},
c: CommonNode{value, lowerBound, upperBound, make([]int, 0)},
weekdayValues: dayOfWeekValues,
n: n,
month: month,
Expand Down
118 changes: 56 additions & 62 deletions quartz/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ func newCronFieldN(values []int, n int) *cronField {
return &cronField{values: values, n: n}
}

// add increments each element of the underlying values array by the given delta.
// add increments each element of the underlying values slice by the given delta.
func (cf *cronField) add(delta int) {
for i := range cf.values {
cf.values[i] += delta
Expand All @@ -123,6 +123,12 @@ func (cf *cronField) String() string {
return strings.Trim(strings.Join(strings.Fields(fmt.Sprint(cf.values)), ","), "[]")
}

// boundary represents inclusive range boundaries for cron field values.
type boundary struct {
lower int
upper int
}

var (
months = []string{"0", "JAN", "FEB", "MAR", "APR", "MAY", "JUN", "JUL", "AUG", "SEP", "OCT", "NOV", "DEC"}
days = []string{"0", "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"}
Expand Down Expand Up @@ -181,98 +187,83 @@ func buildCronField(tokens []string) ([]*cronField, error) {
var err error
fields := make([]*cronField, 7)
// second field
fields[0], err = parseField(tokens[0], 0, 59)
fields[0], err = parseField(tokens[0], boundary{0, 59}, nil)
if err != nil {
return nil, err
}
// minute field
fields[1], err = parseField(tokens[1], 0, 59)
fields[1], err = parseField(tokens[1], boundary{0, 59}, nil)
if err != nil {
return nil, err
}
// hour field
fields[2], err = parseField(tokens[2], 0, 23)
fields[2], err = parseField(tokens[2], boundary{0, 23}, nil)
if err != nil {
return nil, err
}
// day-of-month field
fields[3], err = parseDayOfMonthField(tokens[3], 1, 31)
fields[3], err = parseDayOfMonthField(tokens[3], boundary{1, 31}, nil)
if err != nil {
return nil, err
}
// month field
fields[4], err = parseField(tokens[4], 1, 12, months)
fields[4], err = parseField(tokens[4], boundary{1, 12}, months)
if err != nil {
return nil, err
}
// day-of-week field
fields[5], err = parseDayOfWeekField(tokens[5], 1, 7, days)
fields[5], err = parseDayOfWeekField(tokens[5], boundary{1, 7}, days)
if err != nil {
return nil, err
}
fields[5].add(-1)
// year field
fields[6], err = parseField(tokens[6], 1970, 1970*2)
fields[6], err = parseField(tokens[6], boundary{1970, 1970 * 2}, nil)
if err != nil {
return nil, err
}

return fields, nil
}

func parseField(field string, min, max int, translate ...[]string) (*cronField, error) {
var glossary []string
if len(translate) > 0 {
glossary = translate[0]
}
func parseField(field string, bound boundary, names []string) (*cronField, error) {
// any value
if field == "*" || field == "?" {
return newCronField([]int{}), nil
}
// simple value
i, err := strconv.Atoi(field)
if err == nil {
if inScope(i, min, max) {
return newCronField([]int{i}), nil
}
return nil, newInvalidCronFieldError("simple", field)
}
// list values
if strings.ContainsRune(field, listRune) {
return parseListField(field, min, max, glossary)
return parseListField(field, bound, names)
}
// step values
if strings.ContainsRune(field, stepRune) {
return parseStepField(field, min, max, glossary)
return parseStepField(field, bound, names)
}
// range values
if strings.ContainsRune(field, rangeRune) {
return parseRangeField(field, min, max, glossary)
return parseRangeField(field, bound, names)
}
// simple literal value
if glossary != nil {
intVal, err := translateLiteral(glossary, field)
if err != nil {
return nil, err
}
if inScope(intVal, min, max) {
return newCronField([]int{intVal}), nil
}
return nil, newInvalidCronFieldError("literal", field)
// simple value
numeric, err := normalize(field, names)
if err != nil {
return nil, err
}
if inScope(numeric, bound.lower, bound.upper) {
return newCronField([]int{numeric}), nil
}

return nil, newCronParseError(fmt.Sprintf("invalid field %s", field))
return nil, newInvalidCronFieldError("numeric", field)
}

var (
cronLastMonthDayRegex = regexp.MustCompile(`^L(-[0-9]+)?$`)
cronWeekdayRegex = regexp.MustCompile(`^[0-9]+W$`)

cronLastWeekdayRegex = regexp.MustCompile(`^[0-9]*L$`)
cronHashRegex = regexp.MustCompile(`^[0-9]+#[0-9]+$`)
cronLastWeekdayRegex = regexp.MustCompile(`^[a-zA-Z0-9]*L$`)
cronHashRegex = regexp.MustCompile(`^[a-zA-Z0-9]+#[0-9]+$`)
)

func parseDayOfMonthField(field string, min, max int, translate ...[]string) (*cronField, error) {
func parseDayOfMonthField(field string, bound boundary, names []string) (*cronField, error) {
if strings.ContainsRune(field, lastRune) && cronLastMonthDayRegex.MatchString(field) {
if field == string(lastRune) {
return newCronFieldN([]int{}, cronLastDayOfMonthN), nil
Expand All @@ -282,7 +273,7 @@ func parseDayOfMonthField(field string, min, max int, translate ...[]string) (*c
return nil, newInvalidCronFieldError("last", field)
}
n, err := strconv.Atoi(values[1])
if err != nil || !inScope(n, 1, 30) {
if err != nil || !inScope(n, bound.lower, bound.upper) {
return nil, newInvalidCronFieldError("last", field)
}
return newCronFieldN([]int{}, -n), nil
Expand All @@ -299,24 +290,24 @@ func parseDayOfMonthField(field string, min, max int, translate ...[]string) (*c
return nil, newInvalidCronFieldError("weekday", field)
}
dayOfMonth, err := strconv.Atoi(day)
if err != nil || !inScope(dayOfMonth, min, max) {
if err != nil || !inScope(dayOfMonth, bound.lower, bound.upper) {
return nil, newInvalidCronFieldError("weekday", field)
}
return newCronFieldN([]int{dayOfMonth}, cronWeekdayN), nil
}
}

return parseField(field, min, max, translate...)
return parseField(field, bound, names)
}

func parseDayOfWeekField(field string, min, max int, translate ...[]string) (*cronField, error) {
func parseDayOfWeekField(field string, bound boundary, names []string) (*cronField, error) {
if strings.ContainsRune(field, lastRune) && cronLastWeekdayRegex.MatchString(field) {
day := strings.TrimSuffix(field, string(lastRune))
if day == "" { // Saturday
return newCronFieldN([]int{7}, -1), nil
}
dayOfWeek, err := strconv.Atoi(day)
if err != nil || !inScope(dayOfWeek, min, max) {
dayOfWeek, err := normalize(day, names)
if err != nil || !inScope(dayOfWeek, bound.lower, bound.upper) {
return nil, newInvalidCronFieldError("last", field)
}
return newCronFieldN([]int{dayOfWeek}, -1), nil
Expand All @@ -327,8 +318,8 @@ func parseDayOfWeekField(field string, min, max int, translate ...[]string) (*cr
if len(values) != 2 {
return nil, newInvalidCronFieldError("hash", field)
}
dayOfWeek, err := strconv.Atoi(values[0])
if err != nil || !inScope(dayOfWeek, min, max) {
dayOfWeek, err := normalize(values[0], names)
if err != nil || !inScope(dayOfWeek, bound.lower, bound.upper) {
return nil, newInvalidCronFieldError("hash", field)
}
n, err := strconv.Atoi(values[1])
Expand All @@ -338,26 +329,26 @@ func parseDayOfWeekField(field string, min, max int, translate ...[]string) (*cr
return newCronFieldN([]int{dayOfWeek}, n), nil
}

return parseField(field, min, max, translate...)
return parseField(field, bound, names)
}

func parseListField(field string, min, max int, glossary []string) (*cronField, error) {
func parseListField(field string, bound boundary, names []string) (*cronField, error) {
t := strings.Split(field, string(listRune))
values, stepValues := extractStepValues(t)
values, rangeValues := extractRangeValues(values)
listValues, err := translateLiterals(glossary, values)
listValues, err := translateLiterals(names, values)
if err != nil {
return nil, err
}
for _, v := range stepValues {
stepField, err := parseStepField(v, min, max, glossary)
stepField, err := parseStepField(v, bound, names)
if err != nil {
return nil, err
}
listValues = append(listValues, stepField.values...)
}
for _, v := range rangeValues {
rangeField, err := parseRangeField(v, min, max, glossary)
rangeField, err := parseRangeField(v, bound, names)
if err != nil {
return nil, err
}
Expand All @@ -368,20 +359,20 @@ func parseListField(field string, min, max int, glossary []string) (*cronField,
return newCronField(listValues), nil
}

func parseRangeField(field string, min, max int, glossary []string) (*cronField, error) {
func parseRangeField(field string, bound boundary, names []string) (*cronField, error) {
t := strings.Split(field, string(rangeRune))
if len(t) != 2 {
return nil, newInvalidCronFieldError("range", field)
}
from, err := normalize(t[0], glossary)
from, err := normalize(t[0], names)
if err != nil {
return nil, err
}
to, err := normalize(t[1], glossary)
to, err := normalize(t[1], names)
if err != nil {
return nil, err
}
if !inScope(from, min, max) || !inScope(to, min, max) {
if !inScope(from, bound.lower, bound.upper) || !inScope(to, bound.lower, bound.upper) {
return nil, newInvalidCronFieldError("range", field)
}
rangeValues, err := fillRangeValues(from, to)
Expand All @@ -392,45 +383,48 @@ func parseRangeField(field string, min, max int, glossary []string) (*cronField,
return newCronField(rangeValues), nil
}

func parseStepField(field string, min, max int, glossary []string) (*cronField, error) {
func parseStepField(field string, bound boundary, names []string) (*cronField, error) {
t := strings.Split(field, string(stepRune))
if len(t) != 2 {
return nil, newInvalidCronFieldError("step", field)
}
to := max
to := bound.upper
var (
from int
err error
)
switch {
case t[0] == "*":
from = min
from = bound.lower
case strings.ContainsRune(t[0], rangeRune):
trange := strings.Split(t[0], string(rangeRune))
if len(trange) != 2 {
return nil, newInvalidCronFieldError("step", field)
}
from, err = normalize(trange[0], glossary)
from, err = normalize(trange[0], names)
if err != nil {
return nil, err
}
to, err = normalize(trange[1], glossary)
to, err = normalize(trange[1], names)
if err != nil {
return nil, err
}
default:
from, err = normalize(t[0], glossary)
from, err = normalize(t[0], names)
if err != nil {
return nil, err
}
}

step, err := strconv.Atoi(t[1])
if err != nil {
return nil, newInvalidCronFieldError("step", field)
}
if !inScope(from, min, max) || !inScope(step, 1, max) || !inScope(to, min, max) {
if !inScope(from, bound.lower, bound.upper) || !inScope(step, 1, bound.upper) ||
!inScope(to, bound.lower, bound.upper) {
return nil, newInvalidCronFieldError("step", field)
}

stepValues, err := fillStepValues(from, step, to)
if err != nil {
return nil, err
Expand Down
16 changes: 16 additions & 0 deletions quartz/cron_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,18 +297,34 @@ func TestCronExpressionDayOfWeek(t *testing.T) {
expression: "0 15 10 ? * 5L",
expected: "Thu Oct 31 10:15:00 2024",
},
{
expression: "0 15 10 ? * THUL",
expected: "Thu Oct 31 10:15:00 2024",
},
{
expression: "0 15 10 ? * 2#1",
expected: "Mon Nov 4 10:15:00 2024",
},
{
expression: "0 15 10 ? * MON#1",
expected: "Mon Nov 4 10:15:00 2024",
},
{
expression: "0 15 10 ? * 3#5",
expected: "Tue Mar 31 10:15:00 2026",
},
{
expression: "0 15 10 ? * Tue#5",
expected: "Tue Mar 31 10:15:00 2026",
},
{
expression: "0 15 10 ? * 7#5",
expected: "Sat May 30 10:15:00 2026",
},
{
expression: "0 15 10 ? * sat#5",
expected: "Sat May 30 10:15:00 2026",
},
}

prev := time.Date(2024, 1, 1, 12, 00, 00, 00, time.UTC).UnixNano()
Expand Down
Loading
Loading