package catalog import ( "bufio" "fmt" "rubric/utils" "strconv" "strings" ) // Book описывает один справочник верхнего уровня type Book struct { Key string Aliases []string Title string Description string Run func(scanner *bufio.Scanner) // передаём scanner чтобы не создавать новый } // All — список всех справочников var All = []Book{ GoBook, // LinuxBook — будет добавлен позже } // Find ищет книгу по ключу или алиасу func Find(key string) (Book, bool) { key = strings.ToLower(strings.TrimSpace(key)) for _, b := range All { if b.Key == key { return b, true } for _, a := range b.Aliases { if a == key { return b, true } } } return Book{}, false } // ShowMainMenu отображает главное меню func ShowMainMenu() { utils.Header("rubric — интерактивный справочник") fmt.Printf(" %s%sДоступные справочники:%s\n\n", utils.ColorBold, utils.ColorWhite, utils.ColorReset) for i, b := range All { aliases := "" if len(b.Aliases) > 0 { aliases = fmt.Sprintf("%s [%s]%s", utils.ColorDim, strings.Join(b.Aliases, ", "), utils.ColorReset) } fmt.Printf(" %s%s %2d.%s %s%-8s%s %s%-34s%s%s\n", utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset, utils.ColorYellow+utils.ColorBold, b.Key, utils.ColorReset, utils.ColorWhite, b.Title, utils.ColorReset, aliases, ) fmt.Printf(" %s%s%s\n\n", utils.ColorDim, b.Description, utils.ColorReset) } fmt.Printf(" %s%sВыберите справочник (номер / ключ / q — выход):%s ", utils.ColorBold, utils.ColorGreen, utils.ColorReset) } // RunInteractive — главный интерактивный цикл func RunInteractive(scanner *bufio.Scanner) { for { ShowMainMenu() if !scanner.Scan() { break } input := strings.TrimSpace(scanner.Text()) if input == "" { continue } if input == "q" || input == "quit" || input == "exit" { fmt.Printf("\n %s%sДо свидания!%s\n\n", utils.ColorBold, utils.ColorCyan, utils.ColorReset) return } // По номеру if n, err := strconv.Atoi(input); err == nil { if n >= 1 && n <= len(All) { All[n-1].Run(scanner) continue } fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n", utils.ColorBold, utils.ColorRed, n, len(All), utils.ColorReset) continue } // По ключу if b, ok := Find(input); ok { b.Run(scanner) continue } fmt.Printf("\n %s%sНе понял '%s'. Введите номер или ключ справочника.%s\n", utils.ColorBold, utils.ColorRed, input, utils.ColorReset) } } // ShowHelp — общая справка func ShowHelp() { utils.Header("rubric — справка по использованию") utils.Section("Запуск") utils.Command("./rubric", "главное меню") utils.Command("./rubric -book go", "открыть справочник напрямую") utils.Command("./rubric -book go -topic basics", "открыть конкретный раздел") utils.Command("./rubric -list", "список всех справочников и разделов") utils.Command("./rubric -help", "эта справка") fmt.Println() utils.Section("Доступные справочники") for _, b := range All { keys := append([]string{b.Key}, b.Aliases...) fmt.Printf(" %s%s%-8s%s %-30s %sключи: %s%s\n", utils.ColorBold, utils.ColorCyan, b.Key, utils.ColorReset, b.Title, utils.ColorDim, strings.Join(keys, ", "), utils.ColorReset, ) } utils.Footer() } // ListAll — краткий список всех справочников и их разделов func ListAll() { fmt.Printf("\n%s%srubric — все справочники:%s\n\n", utils.ColorBold, utils.ColorCyan, utils.ColorReset) for i, b := range All { fmt.Printf(" %s%d.%s %s%s%s — %s\n", utils.ColorDim, i+1, utils.ColorReset, utils.ColorYellow+utils.ColorBold, b.Key, utils.ColorReset, b.Title, ) } fmt.Println() } // PrintBackPrompt — подсказка "вернуться назад" func PrintBackPrompt(scanner *bufio.Scanner) { fmt.Printf("\n %s%s[Enter — вернуться в меню справочника, b — главное меню]%s ", utils.ColorDim, utils.ColorCyan, utils.ColorReset) if scanner.Scan() { text := strings.TrimSpace(scanner.Text()) _ = text // "b" обрабатывается в вызывающем коде } } // ReadLine читает строку из сканера func ReadLine(scanner *bufio.Scanner) (string, bool) { if !scanner.Scan() { return "", false } return strings.TrimSpace(scanner.Text()), true } // IsBack возвращает true если пользователь хочет вернуться на уровень выше func IsBack(s string) bool { return s == "b" || s == "back" || s == "назад" || s == ".." } // IsQuit возвращает true если пользователь хочет выйти func IsQuit(s string) bool { return s == "q" || s == "quit" || s == "exit" }