@@ -2,10 +2,10 @@ package main
2
2
3
3
import (
4
4
"encoding/json"
5
- "errors"
6
5
"flag"
7
6
"fmt"
8
7
"os"
8
+ "strings"
9
9
"time"
10
10
11
11
"github.com/orltom/on-call-schedule/internal/cli"
@@ -14,22 +14,17 @@ import (
14
14
"github.com/orltom/on-call-schedule/pkg/apis"
15
15
)
16
16
17
- var (
18
- ErrMissingArguments = errors .New ("missing required flag" )
19
- ErrInvalidArgument = errors .New ("invalid value" )
20
- )
21
-
22
- type Converter int
17
+ type Format int
23
18
24
19
const (
25
- CVS Converter = iota
20
+ CVS Format = iota
26
21
Table
27
22
JSON
28
23
)
29
24
30
25
func RunCreateShiftPlan (arguments []string ) error {
31
- enums := map [string ]Converter {"CVS" : CVS , "Table" : Table , "json" : JSON }
32
- converters := map [Converter ]func () apis.Exporter {
26
+ enums := map [string ]Format {"CVS" : CVS , "Table" : Table , "json" : JSON }
27
+ exporters := map [Format ]func () apis.Exporter {
33
28
Table : func () apis.Exporter {
34
29
return export .NewTableExporter ()
35
30
},
@@ -41,20 +36,24 @@ func RunCreateShiftPlan(arguments []string) error {
41
36
},
42
37
}
43
38
44
- str := new (time.Time )
39
+ start := new (time.Time )
45
40
end := new (time.Time )
41
+ primaryRules := new (string )
42
+ secondaryRules := new (string )
46
43
duration := new (int )
47
44
teamFilePath := new (string )
48
- transform := Table
45
+ outputFormat := Table
49
46
var showHelp bool
50
47
51
48
createCommand := flag .NewFlagSet ("create" , flag .ExitOnError )
52
49
createCommand .BoolVar (& showHelp , "h" , false , "help for ocsctl create" )
53
- createCommand .IntVar (duration , "duration" , 7 * 24 , "shift duration in hours. " )
54
- createCommand .Func ("start" , "(required) start time of the schedule plan" , cli .TimeValueVar (str ))
50
+ createCommand .IntVar (duration , "duration" , 7 * 24 , "shift duration in hours" )
51
+ createCommand .Func ("start" , "(required) start time of the schedule plan" , cli .TimeValueVar (start ))
55
52
createCommand .Func ("end" , "(required) end time of the schedule plan" , cli .TimeValueVar (end ))
56
53
createCommand .Func ("team-file" , "(required) path to the file that contain all on-call duties" , cli .FilePathVar (teamFilePath ))
57
- createCommand .Func ("output" , "output format. One of (cvs, table, json)" , cli .EnumValueVar (enums , & transform ))
54
+ createCommand .Func ("output" , "output format. One of (cvs, table, json)" , cli .EnumValueVar (enums , & outputFormat ))
55
+ createCommand .StringVar (primaryRules , "primary-rules" , "vacation" , "Rule to decide which employee should be on-call for the next shift" )
56
+ createCommand .StringVar (secondaryRules , "secondary-rules" , "vacation" , "Rule to decide which employee should be on-call for the next shift" )
58
57
createCommand .Usage = func () {
59
58
fmt .Fprintf (os .Stdout , "Create on-call schedule\n " )
60
59
fmt .Fprintf (os .Stdout , "\n Usage\n " )
@@ -74,56 +73,72 @@ func RunCreateShiftPlan(arguments []string) error {
74
73
return nil
75
74
}
76
75
77
- // check that the required flags are set...
78
- if ! cli .IsFlagPassed (createCommand , "start" ) {
79
- createCommand .Usage ()
80
- return fmt .Errorf ("%w: %s" , ErrMissingArguments , "start" )
81
- }
82
-
83
- if ! cli .IsFlagPassed (createCommand , "end" ) {
76
+ // validate CLI arguments...
77
+ if ok , missed := cli .RequiredFlagPassed (createCommand , "start" , "end" , "team-file" ); ! ok {
84
78
createCommand .Usage ()
85
- return fmt .Errorf ("%w : %s" , ErrMissingArguments , "end" )
79
+ return fmt .Errorf ("missing required flag : %s" , strings . Join ( missed , "," ) )
86
80
}
87
81
88
- if ! cli .IsFlagPassed (createCommand , "team-file" ) {
89
- createCommand .Usage ()
90
- return fmt .Errorf ("%w: %s" , ErrMissingArguments , "team-file" )
82
+ // initialize...
83
+ team , err := readTeamFile (* teamFilePath )
84
+ if err != nil {
85
+ return fmt .Errorf ("could not read %s: %w" , * teamFilePath , err )
91
86
}
92
87
93
- // validate input data...
94
- if str == end {
95
- createCommand .Usage ()
96
- return ErrInvalidArgument
88
+ pRules , err := mapToRules (* primaryRules )
89
+ if err != nil {
90
+ return fmt .Errorf ("invalid rules: %w" , err )
97
91
}
98
92
99
- // initialize and run...
100
- team , err := parse (* teamFilePath )
93
+ sRules , err := mapToRules (* secondaryRules )
101
94
if err != nil {
102
- return fmt .Errorf ("%w: invalid team config file : %w" , ErrInvalidArgument , err )
95
+ return fmt .Errorf ("invalid rules : %w" , err )
103
96
}
104
97
105
- plan , err := shiftplan .NewDefaultShiftPlanner (team .Employees ).Plan (* str , * end , time .Duration (* duration )* time .Hour )
98
+ // run...
99
+ plan , err := shiftplan .NewShiftPlanner (team .Employees , pRules , sRules ).Plan (* start , * end , time .Duration (* duration )* time .Hour )
106
100
if err != nil {
107
101
return fmt .Errorf ("can not create on-call schedule: %w" , err )
108
102
}
109
103
110
- if err := converters [ transform ]().Write (plan , os .Stdout ); err != nil {
104
+ if err := exporters [ outputFormat ]().Write (plan , os .Stdout ); err != nil {
111
105
return fmt .Errorf ("unexpecting error: %w" , err )
112
106
}
113
107
114
108
return nil
115
109
}
116
110
117
- func parse (path string ) (apis.Team , error ) {
111
+ func readTeamFile (path string ) (apis.Team , error ) {
118
112
content , err := os .ReadFile (path )
119
113
if err != nil {
120
114
return apis.Team {}, fmt .Errorf ("could not read file '%s': %w" , path , err )
121
115
}
122
116
123
117
var team apis.Team
124
118
if err := json .Unmarshal (content , & team ); err != nil {
125
- return apis.Team {}, fmt .Errorf ("could not pars json file '%s': %w" , path , err )
119
+ return apis.Team {}, fmt .Errorf ("could not parse json file '%s': %w" , path , err )
126
120
}
127
121
128
122
return team , nil
129
123
}
124
+
125
+ func mapToRules (value string ) ([]apis.Rule , error ) {
126
+ var rules []apis.Rule
127
+ for _ , s := range strings .Split (value , "," ) {
128
+ switch strings .ToLower (s ) {
129
+ case "vacation" :
130
+ rules = append (rules , shiftplan .NewNoVacationOverlap ())
131
+ case "minimumoneshiftgap" :
132
+ rules = append (rules , shiftplan .NewMinimumGapBetweenShifts (1 ))
133
+ case "minimumtwoshiftgap" :
134
+ rules = append (rules , shiftplan .NewMinimumGapBetweenShifts (2 ))
135
+ case "minimumthreeshiftgap" :
136
+ rules = append (rules , shiftplan .NewMinimumGapBetweenShifts (3 ))
137
+ case "minimumfourshiftgap" :
138
+ rules = append (rules , shiftplan .NewMinimumGapBetweenShifts (4 ))
139
+ default :
140
+ return nil , fmt .Errorf ("unknow rule: %s" , s )
141
+ }
142
+ }
143
+ return rules , nil
144
+ }
0 commit comments