Skip to content

Commit

Permalink
ci: update golangci-lint configuration (#155)
Browse files Browse the repository at this point in the history
  • Loading branch information
reugn authored Feb 4, 2025
1 parent 49e33c3 commit 79df5a3
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 77 deletions.
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

0 comments on commit 79df5a3

Please sign in to comment.