sync: Use errors instead of string to report

This commit is contained in:
nemunaire 2022-07-02 00:01:05 +02:00
parent d8943ba1f3
commit b0129e5239
9 changed files with 186 additions and 178 deletions

View file

@ -35,11 +35,11 @@ func validatorRegexp(vre string) (validator_regexp *string) {
return
}
func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bool, separator string) (raw string, prep string, errs []string) {
func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bool, separator string) (raw string, prep string, errs []error) {
// Concatenate array
if f, ok := input.([]interface{}); ok {
if len(validatorRe) > 0 {
errs = append(errs, "ValidatorRe cannot be defined for this kind of flag.")
errs = append(errs, fmt.Errorf("ValidatorRe cannot be defined for this kind of flag."))
validatorRe = ""
}
@ -47,20 +47,20 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
separator = ","
} else if len(separator) > 1 {
separator = string(separator[0])
errs = append(errs, "separator truncated to %q")
errs = append(errs, fmt.Errorf("separator truncated to %q", separator))
}
var fitems []string
for _, v := range f {
for i, v := range f {
if g, ok := v.(string); ok {
if strings.Index(g, separator) != -1 {
errs = append(errs, "flag items cannot contain %q character as it is used as separator. Change the separator attribute for this flag.")
errs = append(errs, fmt.Errorf("flag items cannot contain %q character as it is used as separator. Change the separator attribute for this flag."))
return
} else {
fitems = append(fitems, g)
}
} else {
errs = append(errs, "item %d has an invalid type: can only be string, is %T.")
errs = append(errs, fmt.Errorf("item %d has an invalid type: can only be string, is %T.", i, g))
return
}
}
@ -74,7 +74,7 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
nbLines := 0
if showLines {
if len(fitems) > 9 {
errs = append(errs, "too much items in vector to use ShowLines features, max 9.")
errs = append(errs, fmt.Errorf("too much items in vector to use ShowLines features, max 9."))
} else {
nbLines = len(fitems)
}
@ -88,7 +88,7 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
} else if f, ok := input.(float64); ok {
raw = strconv.FormatFloat(f, 'f', -1, 64)
} else if f, ok := input.(string); !ok {
errs = append(errs, fmt.Sprintf("has an invalid type: can only be []string or string, not %T", input))
errs = append(errs, fmt.Errorf("has an invalid type: can only be []string or string, not %T", input))
return
} else {
raw = f
@ -96,18 +96,18 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
return
}
func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f *fic.FlagLabel, errs []string) {
func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f *fic.FlagLabel, errs []error) {
if len(flag.Label) == 0 {
errs = append(errs, fmt.Sprintf("%q: flag #%d: Label cannot be empty.", path.Base(exercice.Path), flagline))
errs = append(errs, fmt.Errorf("Label cannot be empty."))
return
}
if flag.Raw != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: raw cannot be defined.", path.Base(exercice.Path), flagline))
errs = append(errs, fmt.Errorf("raw cannot be defined."))
}
if len(flag.Choice) != 0 {
errs = append(errs, fmt.Sprintf("%q: flag #%d: choices cannot be defined.", path.Base(exercice.Path), flagline))
errs = append(errs, fmt.Errorf("choices cannot be defined."))
}
f = &fic.FlagLabel{
@ -118,48 +118,46 @@ func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f
return
}
func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defaultLabel string) (f *fic.Flag, choices []*fic.FlagChoice, errs []string) {
func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defaultLabel string) (f *fic.Flag, choices []*fic.FlagChoice, errs []error) {
if len(flag.Label) == 0 {
flag.Label = defaultLabel
}
if len(flag.Variant) != 0 {
errs = append(errs, fmt.Sprintf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), flagline))
errs = append(errs, fmt.Errorf("variant is not defined for this kind of flag."))
}
if flag.Label[0] == '`' {
errs = append(errs, fmt.Sprintf("%q: flag #%d: Label should not begin with `.", path.Base(exercice.Path), flagline))
errs = append(errs, fmt.Errorf("Label should not begin with `."))
flag.Label = flag.Label[1:]
}
if (flag.Label[0] == 'Q' || flag.Label[0] == 'q') && (flag.Label[1] == 'U' || flag.Label[1] == 'u') ||
(flag.Label[0] == 'W' || flag.Label[0] == 'w') && (flag.Label[1] == 'H' || flag.Label[1] == 'h') {
errs = append(errs, fmt.Sprintf("%q: flag #%d: Label should not begin with %s. This seem to be a question. Reword your label as a description of the expected flag, `:` are automatically appended.", path.Base(exercice.Path), flagline, flag.Label[0:2]))
errs = append(errs, fmt.Errorf("Label should not begin with %s. This seem to be a question. Reword your label as a description of the expected flag, `:` are automatically appended.", flag.Label[0:2]))
flag.Label = flag.Label[1:]
}
if flag.Label[len(flag.Label)-1] != ')' && flag.Label[len(flag.Label)-1] != '©' && !unicode.IsLetter(rune(flag.Label[len(flag.Label)-1])) && !unicode.IsDigit(rune(flag.Label[len(flag.Label)-1])) {
errs = append(errs, fmt.Sprintf("%q: flag #%d: Label should not end with punct (%q). Reword your label as a description of the expected flag, `:` are automatically appended.", path.Base(exercice.Path), flagline, flag.Label[len(flag.Label)-1]))
errs = append(errs, fmt.Errorf("Label should not end with punct (%q). Reword your label as a description of the expected flag, `:` are automatically appended.", flag.Label[len(flag.Label)-1]))
}
raw, prep, terrs := getRawKey(flag.Raw, flag.ValidatorRe, flag.Ordered, flag.ShowLines, flag.Separator)
if len(terrs) > 0 {
for _, err := range terrs {
errs = append(errs, fmt.Sprintf("%q: flag #%d: %s", path.Base(exercice.Path), flagline, err))
}
errs = append(errs, terrs...)
f = nil
return
}
flag.Label = prep + flag.Label
if (flag.Type == "text" && !isFullGraphic(strings.Replace(raw, "\n", "", -1))) || (flag.Type != "text" && !isFullGraphic(raw)) {
errs = append(errs, fmt.Sprintf("%q: WARNING flag #%d: non-printable characters in flag, is this really expected?", path.Base(exercice.Path), flagline))
errs = append(errs, fmt.Errorf("WARNING non-printable characters in flag, is this really expected?"))
}
hashedFlag, err := fic.ComputeHashedFlag([]byte(raw), !flag.CaseSensitive, flag.NoTrim, validatorRegexp(flag.ValidatorRe), flag.SortReGroups)
if err != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: %s", path.Base(exercice.Path), flagline, err.Error()))
errs = append(errs, err)
return
}
fl := fic.Flag(&fic.FlagKey{
@ -193,9 +191,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
for _, choice := range flag.Choice {
val, prep, terrs := getRawKey(choice.Value, "", false, false, "")
if len(terrs) > 0 {
for _, err := range terrs {
errs = append(errs, fmt.Sprintf("%q: flag #%d: %s", path.Base(exercice.Path), flagline, err))
}
errs = append(errs, terrs...)
continue
}
@ -214,7 +210,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
})
if val == "true" || val == "false" {
errs = append(errs, fmt.Sprintf("%q: flag #%d: value can't be %q, this is not a MCQ, the value has to be meaningful. The value is shown to players as response identifier.", path.Base(exercice.Path), flagline), val)
errs = append(errs, fmt.Errorf("value can't be %q, this is not a MCQ, the value has to be meaningful. The value is shown to players as response identifier.", val))
}
if val == raw || (!flag.CaseSensitive && val == strings.ToLower(raw)) {
@ -222,7 +218,7 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
}
}
if !hasOne {
errs = append(errs, fmt.Sprintf("%q: error in flag #%d: no valid answer defined.", path.Base(exercice.Path), flagline))
errs = append(errs, fmt.Errorf("no valid answer defined."))
}
}
return
@ -251,7 +247,7 @@ func iface2Number(input interface{}, output *string) error {
}
// buildExerciceFlags read challenge.txt and extract all flags.
func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nline int) (ret []importFlag, errs []string) {
func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nline int) (ret []importFlag, errs []error) {
switch strings.ToLower(flag.Type) {
case "":
flag.Type = "key"
@ -263,17 +259,17 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
var smin, smax, sstep string
err := iface2Number(flag.NumberMin, &smin)
if err != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: min %s.", path.Base(exercice.Path), nline+1, err.Error()))
errs = append(errs, fmt.Errorf("%q: flag #%d: min %s.", path.Base(exercice.Path), nline+1, err.Error()))
}
err = iface2Number(flag.NumberMax, &smax)
if err != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: max %s.", path.Base(exercice.Path), nline+1, err.Error()))
errs = append(errs, fmt.Errorf("%q: flag #%d: max %s.", path.Base(exercice.Path), nline+1, err.Error()))
}
err = iface2Number(flag.NumberStep, &sstep)
if err != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: step %s.", path.Base(exercice.Path), nline+1, err.Error()))
errs = append(errs, fmt.Errorf("%q: flag #%d: step %s.", path.Base(exercice.Path), nline+1, err.Error()))
}
flag.Type = fmt.Sprintf("number,%s,%s,%s", smin, smax, sstep)
case "text":
@ -287,23 +283,23 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
case "mcq":
flag.Type = "mcq"
default:
errs = append(errs, fmt.Sprintf("%q: flag #%d: invalid type of flag: should be 'key', 'number', 'text', 'mcq', 'ucq', 'radio' or 'vector'.", path.Base(exercice.Path), nline+1))
errs = append(errs, fmt.Errorf("%q: flag #%d: invalid type of flag: should be 'key', 'number', 'text', 'mcq', 'ucq', 'radio' or 'vector'.", path.Base(exercice.Path), nline+1))
return
}
if !strings.HasPrefix(flag.Type, "number") {
if flag.NumberMin != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: property min undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
errs = append(errs, fmt.Errorf("%q: flag #%d: property min undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
} else if flag.NumberMax != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: property max undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
errs = append(errs, fmt.Errorf("%q: flag #%d: property max undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
} else if flag.NumberStep != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: property step undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
errs = append(errs, fmt.Errorf("%q: flag #%d: property step undefined for this kind of flag: should the type be 'number'.", path.Base(exercice.Path), nline+1))
}
}
if len(flag.Help) > 0 {
if mdhelp, err := ProcessMarkdown(i, flag.Help, exercice.Path); err != nil {
errs = append(errs, fmt.Sprintf("%q: flag #%d: unable to parse property help as Markdown: %s", path.Base(exercice.Path), nline+1), err.Error())
errs = append(errs, fmt.Errorf("%q: flag #%d: unable to parse property help as Markdown: %w", path.Base(exercice.Path), nline+1, err))
} else {
flag.Help = mdhelp[3 : len(mdhelp)-4]
}
@ -311,8 +307,8 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
if flag.Type == "label" {
addedFlag, berrs := buildLabelFlag(exercice, flag, nline+1)
if len(berrs) > 0 {
errs = append(errs, berrs...)
for _, e := range berrs {
errs = append(errs, fmt.Errorf("%q: flag #%d: %w", path.Base(exercice.Path), nline+1), e)
}
if addedFlag != nil {
ret = append(ret, importFlag{
@ -322,8 +318,8 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
}
} else if flag.Type == "key" || strings.HasPrefix(flag.Type, "number") || flag.Type == "text" || flag.Type == "ucq" || flag.Type == "radio" || flag.Type == "vector" {
addedFlag, choices, berrs := buildKeyFlag(exercice, flag, nline+1, "Flag")
if len(berrs) > 0 {
errs = append(errs, berrs...)
for _, e := range berrs {
errs = append(errs, fmt.Errorf("%q: flag #%d: %w", path.Base(exercice.Path), nline+1), e)
}
if addedFlag != nil {
ret = append(ret, importFlag{
@ -344,7 +340,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
isJustified := false
if len(flag.Variant) != 0 {
errs = append(errs, fmt.Sprintf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), nline+1))
errs = append(errs, fmt.Errorf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), nline+1))
}
if !flag.NoShuffle {
@ -358,7 +354,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
if choice.Raw != nil {
if hasOne && !isJustified {
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1))
errs = append(errs, fmt.Errorf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1))
continue
}
@ -367,13 +363,13 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
} else if p, ok := choice.Value.(bool); ok {
val = p
if isJustified {
errs = append(errs, fmt.Sprintf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1))
errs = append(errs, fmt.Errorf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1))
continue
}
} else if choice.Value == nil {
val = false
} else {
errs = append(errs, fmt.Sprintf("%q: error in MCQ %d choice %d: incorrect type for value: %T is not boolean.", path.Base(exercice.Path), nline+1, cid, choice.Value))
errs = append(errs, fmt.Errorf("%q: error in MCQ %d choice %d: incorrect type for value: %T is not boolean.", path.Base(exercice.Path), nline+1, cid, choice.Value))
continue
}
@ -406,7 +402,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
}
// buildExerciceFlags read challenge.txt and extract all flags.
func buildExerciceFlags(i Importer, exercice *fic.Exercice) (flags map[int64]importFlag, flagids []int64, errs []string) {
func buildExerciceFlags(i Importer, exercice *fic.Exercice) (flags map[int64]importFlag, flagids []int64, errs []error) {
params, gerrs := getExerciceParams(i, exercice)
if len(gerrs) > 0 {
return flags, flagids, gerrs
@ -422,7 +418,7 @@ func buildExerciceFlags(i Importer, exercice *fic.Exercice) (flags map[int64]imp
// Ensure flag ID is unique
for _, ok := flags[flag.Id]; ok; _, ok = flags[flag.Id] {
errs = append(errs, fmt.Sprintf("%q: flag #%d: identifier already used (%d), using a random one.", path.Base(exercice.Path), nline+1, flag.Id))
errs = append(errs, fmt.Errorf("%q: flag #%d: identifier already used (%d), using a random one.", path.Base(exercice.Path), nline+1, flag.Id))
flag.Id = rand.Int63()
}
@ -457,7 +453,7 @@ func buildExerciceFlags(i Importer, exercice *fic.Exercice) (flags map[int64]imp
}
// CheckExerciceFlags checks if all flags for the given challenge are correct.
func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string) (rf []fic.Flag, errs []string) {
func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string) (rf []fic.Flag, errs []error) {
flags, flagsids, berrs := buildExerciceFlags(i, exercice)
errs = append(errs, berrs...)
@ -466,7 +462,7 @@ func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string) (rf
// Check dependency to flag
for _, nf := range flag.FlagsDeps {
if _, ok := flags[nf]; !ok {
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to flag id=%d: id not defined", path.Base(exercice.Path), flag.Line, nf))
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to flag id=%d: id not defined", path.Base(exercice.Path), flag.Line, nf))
}
}
@ -480,7 +476,7 @@ func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string) (rf
}
}
if !found {
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: No such file", path.Base(exercice.Path), flag.Line, lf))
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to %s: No such file", path.Base(exercice.Path), flag.Line, lf))
}
}
@ -509,11 +505,11 @@ func ExerciceFlagsMap(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.Fl
}
// SyncExerciceFlags imports all kind of flags for the given challenge.
func SyncExerciceFlags(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.Flag, errs []string) {
func SyncExerciceFlags(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.Flag, errs []error) {
if _, err := exercice.WipeFlags(); err != nil {
errs = append(errs, err.Error())
errs = append(errs, err)
} else if _, err := exercice.WipeMCQs(); err != nil {
errs = append(errs, err.Error())
errs = append(errs, err)
} else {
flags, flagids, berrs := buildExerciceFlags(i, exercice)
errs = append(errs, berrs...)
@ -524,12 +520,12 @@ func SyncExerciceFlags(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.F
for _, flagid := range flagids {
if flag, ok := flags[flagid]; ok {
if addedFlag, err := exercice.AddFlag(flag.Flag); err != nil {
errs = append(errs, fmt.Sprintf("%q: error flag #%d: %s", path.Base(exercice.Path), flag.Line, err))
errs = append(errs, fmt.Errorf("%q: error flag #%d: %w", path.Base(exercice.Path), flag.Line, err))
} else {
if f, ok := addedFlag.(*fic.FlagKey); ok {
for _, choice := range flag.Choices {
if _, err := f.AddChoice(choice); err != nil {
errs = append(errs, fmt.Sprintf("%q: error in flag #%d choice #FIXME: %s", path.Base(exercice.Path), flag.Line, err))
errs = append(errs, fmt.Errorf("%q: error in flag #%d choice #FIXME: %w", path.Base(exercice.Path), flag.Line, err))
}
}
}
@ -539,18 +535,18 @@ func SyncExerciceFlags(i Importer, exercice *fic.Exercice) (kmap map[int64]fic.F
// Import dependency to flag
for _, nf := range flag.FlagsDeps {
if rf, ok := kmap[nf]; !ok {
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to flag id=%d: id not defined, perhaps not available at time of processing", path.Base(exercice.Path), flag.Line, nf))
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to flag id=%d: id not defined, perhaps not available at time of processing", path.Base(exercice.Path), flag.Line, nf))
} else if err := addedFlag.AddDepend(rf); err != nil {
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to id=%d: %s", path.Base(exercice.Path), flag.Line, nf, err))
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to id=%d: %w", path.Base(exercice.Path), flag.Line, nf, err))
}
}
// Import dependency to file
for _, lf := range flag.FilesDeps {
if rf, err := exercice.GetFileByFilename(lf); err != nil {
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), flag.Line, lf, err))
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to %s: %w", path.Base(exercice.Path), flag.Line, lf, err))
} else if err := rf.AddDepend(addedFlag); err != nil {
errs = append(errs, fmt.Sprintf("%q: error flag #%d dependency to %s: %s", path.Base(exercice.Path), flag.Line, lf, err))
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to %s: %w", path.Base(exercice.Path), flag.Line, lf, err))
}
}
}