sync: Report custom errors
This commit is contained in:
parent
08ea1bac0d
commit
c78545c18b
17 changed files with 510 additions and 137 deletions
|
@ -98,16 +98,16 @@ func getRawKey(input interface{}, validatorRe string, ordered bool, showLines bo
|
|||
|
||||
func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f *fic.FlagLabel, errs []error) {
|
||||
if len(flag.Label) == 0 {
|
||||
errs = append(errs, fmt.Errorf("Label cannot be empty."))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("Label cannot be empty.")))
|
||||
return
|
||||
}
|
||||
|
||||
if flag.Raw != nil {
|
||||
errs = append(errs, fmt.Errorf("raw cannot be defined."))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("raw cannot be defined.")))
|
||||
}
|
||||
|
||||
if len(flag.Choice) != 0 {
|
||||
errs = append(errs, fmt.Errorf("choices cannot be defined."))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("choices cannot be defined.")))
|
||||
}
|
||||
|
||||
f = &fic.FlagLabel{
|
||||
|
@ -115,6 +115,13 @@ func buildLabelFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int) (f
|
|||
Label: flag.Label,
|
||||
Variant: flag.Variant,
|
||||
}
|
||||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagLabelHooks {
|
||||
for _, e := range h(f) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -124,43 +131,35 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
|||
}
|
||||
|
||||
if len(flag.Variant) != 0 {
|
||||
errs = append(errs, fmt.Errorf("variant is not defined for this kind of flag."))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("variant is not defined for this kind of flag.")))
|
||||
}
|
||||
|
||||
if flag.Label[0] == '`' {
|
||||
errs = append(errs, fmt.Errorf("Label should not begin with `."))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, 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.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.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 {
|
||||
errs = append(errs, terrs...)
|
||||
for _, terr := range terrs {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, terr))
|
||||
}
|
||||
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.Errorf("WARNING non-printable characters in flag, is this really expected?"))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, 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, err)
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, err))
|
||||
return
|
||||
}
|
||||
fl := fic.Flag(&fic.FlagKey{
|
||||
fk := &fic.FlagKey{
|
||||
Type: flag.Type,
|
||||
IdExercice: exercice.Id,
|
||||
Order: int8(flagline),
|
||||
|
@ -175,7 +174,16 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
|||
Checksum: hashedFlag[:],
|
||||
ChoicesCost: flag.ChoicesCost,
|
||||
BonusGain: flag.BonusGain,
|
||||
})
|
||||
}
|
||||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagKeyHooks {
|
||||
for _, e := range h(fk, raw) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
|
||||
fl := fic.Flag(fk)
|
||||
f = &fl
|
||||
|
||||
if len(flag.Choice) > 0 || (flag.Type == "ucq" || flag.Type == "radio") {
|
||||
|
@ -191,7 +199,9 @@ 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 {
|
||||
errs = append(errs, terrs...)
|
||||
for _, terr := range terrs {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, terr))
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -204,13 +214,22 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
|||
val = strings.ToLower(val)
|
||||
}
|
||||
|
||||
choices = append(choices, &fic.FlagChoice{
|
||||
fc := &fic.FlagChoice{
|
||||
Label: choice.Label,
|
||||
Value: val,
|
||||
})
|
||||
}
|
||||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagChoiceHooks {
|
||||
for _, e := range h(fc) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
|
||||
choices = append(choices, fc)
|
||||
|
||||
if val == "true" || val == "false" {
|
||||
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))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, 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)) {
|
||||
|
@ -218,7 +237,14 @@ func buildKeyFlag(exercice *fic.Exercice, flag ExerciceFlag, flagline int, defau
|
|||
}
|
||||
}
|
||||
if !hasOne {
|
||||
errs = append(errs, fmt.Errorf("no valid answer defined."))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, fmt.Errorf("no valid answer defined.")))
|
||||
}
|
||||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagKeyWithChoicesHooks {
|
||||
for _, e := range h(fk, raw, choices) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, flagline, e))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
|
@ -259,17 +285,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.Errorf("%q: flag #%d: min %s.", path.Base(exercice.Path), nline+1, err.Error()))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("min %w", err)))
|
||||
}
|
||||
|
||||
err = iface2Number(flag.NumberMax, &smax)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("%q: flag #%d: max %s.", path.Base(exercice.Path), nline+1, err.Error()))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("max %w", err)))
|
||||
}
|
||||
|
||||
err = iface2Number(flag.NumberStep, &sstep)
|
||||
if err != nil {
|
||||
errs = append(errs, fmt.Errorf("%q: flag #%d: step %s.", path.Base(exercice.Path), nline+1, err.Error()))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("step %w", err)))
|
||||
}
|
||||
flag.Type = fmt.Sprintf("number,%s,%s,%s", smin, smax, sstep)
|
||||
case "text":
|
||||
|
@ -283,23 +309,23 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
case "mcq":
|
||||
flag.Type = "mcq"
|
||||
default:
|
||||
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))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("invalid type of flag: should be 'key', 'number', 'text', 'mcq', 'ucq', 'radio' or 'vector'")))
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(flag.Type, "number") {
|
||||
if flag.NumberMin != nil {
|
||||
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))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("property min undefined for this kind of flag: should the type be 'number'")))
|
||||
} else if flag.NumberMax != nil {
|
||||
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))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("property max undefined for this kind of flag: should the type be 'number'")))
|
||||
} else if flag.NumberStep != nil {
|
||||
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))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("property step undefined for this kind of flag: should the type be 'number'")))
|
||||
}
|
||||
}
|
||||
|
||||
if len(flag.Help) > 0 {
|
||||
if mdhelp, err := ProcessMarkdown(i, flag.Help, exercice.Path); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%q: flag #%d: unable to parse property help as Markdown: %w", path.Base(exercice.Path), nline+1, err))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("unable to parse property help as Markdown: %w", err)))
|
||||
} else {
|
||||
flag.Help = mdhelp[3 : len(mdhelp)-4]
|
||||
}
|
||||
|
@ -307,9 +333,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
|
||||
if flag.Type == "label" {
|
||||
addedFlag, berrs := buildLabelFlag(exercice, flag, nline+1)
|
||||
for _, e := range berrs {
|
||||
errs = append(errs, fmt.Errorf("%q: flag #%d: %w", path.Base(exercice.Path), nline+1, e))
|
||||
}
|
||||
errs = append(errs, berrs...)
|
||||
if addedFlag != nil {
|
||||
ret = append(ret, importFlag{
|
||||
Line: nline + 1,
|
||||
|
@ -318,9 +342,7 @@ 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")
|
||||
for _, e := range berrs {
|
||||
errs = append(errs, fmt.Errorf("%q: flag #%d: %w", path.Base(exercice.Path), nline+1, e))
|
||||
}
|
||||
errs = append(errs, berrs...)
|
||||
if addedFlag != nil {
|
||||
ret = append(ret, importFlag{
|
||||
Line: nline + 1,
|
||||
|
@ -340,7 +362,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
isJustified := false
|
||||
|
||||
if len(flag.Variant) != 0 {
|
||||
errs = append(errs, fmt.Errorf("%q: flag #%d: variant is not defined for this kind of flag.", path.Base(exercice.Path), nline+1))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("variant is not defined for this kind of flag")))
|
||||
}
|
||||
|
||||
if !flag.NoShuffle {
|
||||
|
@ -354,7 +376,7 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
|
||||
if choice.Raw != nil {
|
||||
if hasOne && !isJustified {
|
||||
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))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("all true items has to be justified in this MCQ")))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -363,13 +385,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.Errorf("%q: error MCQ #%d: all true items has to be justified in this MCQ.", path.Base(exercice.Path), nline+1))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("all true items has to be justified in this MCQ")))
|
||||
continue
|
||||
}
|
||||
} else if choice.Value == nil {
|
||||
val = false
|
||||
} else {
|
||||
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))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("choice %d: incorrect type for value: %T is not boolean.", cid, choice.Value)))
|
||||
continue
|
||||
}
|
||||
|
||||
|
@ -393,6 +415,13 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
|
|||
}
|
||||
}
|
||||
|
||||
// Call checks hooks
|
||||
for _, h := range hooks.flagMCQHooks {
|
||||
for _, e := range h(&addedFlag, addedFlag.Entries) {
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, e))
|
||||
}
|
||||
}
|
||||
|
||||
ret = append(ret, importFlag{
|
||||
Line: nline + 1,
|
||||
Flag: &addedFlag,
|
||||
|
@ -418,7 +447,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.Errorf("%q: flag #%d: identifier already used (%d), using a random one.", path.Base(exercice.Path), nline+1, flag.Id))
|
||||
errs = append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("identifier already used (%d), using a random one.", flag.Id)))
|
||||
flag.Id = rand.Int63()
|
||||
}
|
||||
|
||||
|
@ -462,7 +491,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.Errorf("%q: error flag #%d dependency to flag id=%d: id not defined", path.Base(exercice.Path), flag.Line, nf))
|
||||
errs = append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag depend on flag id=%d: id not defined", nf)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,7 +505,7 @@ func CheckExerciceFlags(i Importer, exercice *fic.Exercice, files []string) (rf
|
|||
}
|
||||
}
|
||||
if !found {
|
||||
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to %s: No such file", path.Base(exercice.Path), flag.Line, lf))
|
||||
errs = append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("flag depend on %s: No such file", lf)))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -520,12 +549,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.Errorf("%q: error flag #%d: %w", path.Base(exercice.Path), flag.Line, err))
|
||||
errs = append(errs, NewFlagError(exercice, nil, 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.Errorf("%q: error in flag #%d choice #FIXME: %w", path.Base(exercice.Path), flag.Line, err))
|
||||
errs = append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("choice #FIXME: %w", err)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -535,18 +564,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.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))
|
||||
errs = append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to flag id=%d: id not defined, perhaps not available at time of processing", nf)))
|
||||
} else if err := addedFlag.AddDepend(rf); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to id=%d: %w", path.Base(exercice.Path), flag.Line, nf, err))
|
||||
errs = append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to id=%d: %w", nf, err)))
|
||||
}
|
||||
}
|
||||
|
||||
// Import dependency to file
|
||||
for _, lf := range flag.FilesDeps {
|
||||
if rf, err := exercice.GetFileByFilename(lf); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to %s: %w", path.Base(exercice.Path), flag.Line, lf, err))
|
||||
errs = append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to %s: %w", lf, err)))
|
||||
} else if err := rf.AddDepend(addedFlag); err != nil {
|
||||
errs = append(errs, fmt.Errorf("%q: error flag #%d dependency to %s: %w", path.Base(exercice.Path), flag.Line, lf, err))
|
||||
errs = append(errs, NewFlagError(exercice, nil, flag.Line, fmt.Errorf("dependency to %s: %w", lf, err)))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue