rubric/catalog/catalog.go
2026-07-21 14:52:55 +03:00

223 lines
7 KiB
Go
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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,
DevOpsBook,
InfoSecBook,
}
// 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) {
RunInteractiveWithSearch(scanner)
}
// RunInteractiveWithSearch — главный цикл с поддержкой поиска
func RunInteractiveWithSearch(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
}
// Поиск: /ansible или search ansible
query := ""
if strings.HasPrefix(input, "/") {
query = strings.TrimSpace(input[1:])
} else if strings.HasPrefix(strings.ToLower(input), "search ") {
query = strings.TrimSpace(input[7:])
} else if strings.ToLower(input) == "search" {
fmt.Printf("\n %s%sВведите поисковый запрос:%s ", utils.ColorBold, utils.ColorGreen, utils.ColorReset)
if scanner.Scan() {
query = strings.TrimSpace(scanner.Text())
}
}
if query != "" {
results := Search(query)
ShowSearchResults(query, results)
if len(results) > 0 {
fmt.Printf(" %s%sВведите номер раздела чтобы открыть (Enter — назад в меню):%s ",
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
if scanner.Scan() {
choice := strings.TrimSpace(scanner.Text())
if n, err := strconv.Atoi(choice); err == nil && n >= 1 && n <= len(results) {
fmt.Println()
results[n-1].RunFunc()
fmt.Printf("\n %s%s[Enter — вернуться в меню]%s ", utils.ColorDim, utils.ColorCyan, utils.ColorReset)
scanner.Scan()
}
}
}
continue
}
// По номеру
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 -search ansible", "поиск по всем справочникам")
utils.Command("./rubric -search 'sql injection'", "поиск по нескольким словам")
utils.Command("./rubric -list", "список всех справочников и разделов")
utils.Command("./rubric -help", "эта справка")
fmt.Println()
utils.Section("Поиск в интерактивном режиме")
utils.Command("/ansible", "поиск по запросу (начать с /)")
utils.Command("search nmap", "поиск командой search")
utils.Command("search", "поиск (запросит строку)")
fmt.Println()
utils.Section("Доступные справочники")
for _, b := range All {
keys := append([]string{b.Key}, b.Aliases...)
fmt.Printf(" %s%s%-8s%s %-34s %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"
}