package menus /* ---------------------------------------------------------------- * * IMPORTS * ---------------------------------------------------------------- */ import ( "fmt" "reflect" "strconv" "strings" "ads/internal/core/logging" "gopkg.in/yaml.v3" ) /* ---------------------------------------------------------------- * * METHOD menu class methods * ---------------------------------------------------------------- */ func (menu *Menu) SetDefault(index int) { menu.Default = index } /* ---------------------------------------------------------------- * * METHOD show menu * ---------------------------------------------------------------- */ func (menu Menu) ShowMenu() (bool, error) { var ( choice string index int meta bool quit bool cancel bool err error ) var promptMessages []string var optionsFlattened []MenuOption var optionsKeyValue [][2]string var defaultOption string var breaks []int // Headline einfügen promptMessages = make([]string, len(menu.PromptMessages)) copy(promptMessages, menu.PromptMessages) head := fmt.Sprintf("\033[2m%s\033[0m", strings.Join(menu.Path, " > ")) promptMessages = append([]string{head}, promptMessages...) // Zurück-Option einfügen defaultOption = "" if menu.Default >= 0 { defaultOption = fmt.Sprintf("%v", menu.Default+1) } breaks = []int{} optionsFlattened = []MenuOption{} optionsKeyValue = []([2]string){} index = 0 for _, suboptions := range menu.Options { for _, opt := range suboptions { optionsFlattened = append(optionsFlattened, opt) key := fmt.Sprintf("%v", index+1) subLabel := opt.SubLabel label := opt.Label if !(subLabel == "") { label = fmt.Sprintf("%v (\033[2m%v\033[0m)", opt.Label, subLabel) } optionsKeyValue = append(optionsKeyValue, [2]string{key, label}) index++ } breaks = append(breaks, index-1) } if !menu.IsRoot { optionsKeyValue = append(optionsKeyValue, [2]string{"z", "Zurück zum vorherigen Menü"}) } optionsKeyValue = append(optionsKeyValue, [2]string{"q", "Programm schließen"}) // User Response immer abfragen und abarbeiten, bis quit/return. performClearScreen := !menu.IsRoot for { if performClearScreen { logging.ClearScreen() } performClearScreen = true choice, meta = promptListOfOptions(promptMessages, optionsKeyValue, breaks, defaultOption) // Falls quit wählt, -> quit: if (menu.IsRoot && meta) || choice == "q" { return true, nil } // Falls User back wählt, -> return: if (!menu.IsRoot && meta) || choice == "z" { return false, nil } // sonst führe die assoziierte Methode aus index64, _ := strconv.ParseInt(choice, 10, 64) index = int(index64) - 1 if 0 <= index && index < len(optionsFlattened) { opt := optionsFlattened[index] // Entweder Untermenü öffnen oder Action ausführen if opt.Submenu != nil { quit, err = opt.Submenu.ShowMenu() if quit { return true, err } } else if opt.Action != nil { cancel, err = opt.Action() if err != nil { logging.Error(err) } // Falls ForceReturn, dann nach Ausführung der Action, -> return if menu.ForceReturn { return false, nil } else { // Falls während der Action der User Meta+D klickt, -> return: if cancel { continue } quit := logging.PromptAnyKeyToContinue() // Falls nach der Action der User Meta+D klickt, -> quit: if quit { return true, nil } } } else { logging.Warn("Option noch nicht implementiert.") quit := logging.PromptAnyKeyToContinue() if quit { return true, nil } } } } } /* ---------------------------------------------------------------- * * METHOD prompt values * ---------------------------------------------------------------- */ func (query *PromptValueQuery) Prompt() (bool, error) { var lines = []interface{}{} var responsePtr = query.Response var ( line string cancel bool err error ) // prüfen, dass response ein Ptr auf eine Struct ist: if !(reflect.ValueOf(responsePtr).Kind() == reflect.Ptr) || !(reflect.ValueOf(responsePtr).Elem().Kind() == reflect.Struct) { panic("Input muss ein Pointer auf einen struct sein") } // prompt message vorbereiten: lines = append(lines, fmt.Sprintf("%s, \033[1;3m%s\033[0m, als \033[1m%s\033[0m bitte eingeben.", query.Description, query.Name, query.Type)) if query.ValidExamples != nil && len(*query.ValidExamples) > 0 { if len(*query.ValidExamples) == 1 { example := (*query.ValidExamples)[0] lines = append(lines, fmt.Sprintf(" \033[3;4mBeispiel von Input im gültigen Format:\033[0m \033[2m%s\033[0m", example)) } else { lines = append(lines, fmt.Sprintf(" \033[3;4mBeispiele von Inputs im gültigen Format:\033[0m")) for _, example := range *query.ValidExamples { lines = append(lines, fmt.Sprintf(" - \033[2m%s\033[0m", example)) } lines = append(lines, fmt.Sprintf(" - \033[2;3metc.\033[0m")) } } if query.Requirements != nil && len(*query.Requirements) > 0 { if len(*query.Requirements) == 1 { requirement := (*query.Requirements)[0] lines = append(lines, fmt.Sprintf(" \033[3;4mAnforderung(en) auf Input:\033[0m \033[2m%s\033[0m", requirement)) } else { lines = append(lines, fmt.Sprintf(" \033[3;4mAnforderung(en) auf Input:\033[0m")) for _, requirement := range *query.Requirements { lines = append(lines, fmt.Sprintf(" - \033[2m%s\033[0m", requirement)) } } } // prompt Eingabe eines Werts: for { line, cancel, err = logging.Prompt(lines...) if cancel { return true, nil } if err != nil { logging.Error(err) logging.Plain("") continue } line = fmt.Sprintf("\"response\": %s", line) err = yaml.Unmarshal([]byte(line), query.Response) if err != nil { logging.Error(err) logging.Plain("") continue } break } return cancel, err }