Started
This commit is contained in:
commit
fcc336bf3f
10 changed files with 1413 additions and 0 deletions
221
README.md
Normal file
221
README.md
Normal file
|
|
@ -0,0 +1,221 @@
|
|||
# rubric
|
||||
|
||||
Интерактивный терминальный справочник для разработчика. Цветной вывод, структурированные разделы с примерами кода, быстрый доступ по ключам.
|
||||
|
||||
```
|
||||
./rubric
|
||||
```
|
||||
|
||||
```
|
||||
╔════════════════════════════════════════════════════════════╗
|
||||
║ rubric — интерактивный справочник ║
|
||||
╚════════════════════════════════════════════════════════════╝
|
||||
|
||||
Доступные справочники:
|
||||
|
||||
1. go Справочник по языку Go
|
||||
Установка, синтаксис, типы, функции, горутины...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Установка
|
||||
|
||||
**Требования:** Go 1.21+
|
||||
|
||||
```bash
|
||||
git clone https://github.com/user/rubric
|
||||
cd rubric
|
||||
go build -o rubric .
|
||||
```
|
||||
|
||||
Или сразу установить глобально:
|
||||
|
||||
```bash
|
||||
go install github.com/user/rubric@latest
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Использование
|
||||
|
||||
### Интерактивный режим
|
||||
|
||||
```bash
|
||||
./rubric
|
||||
```
|
||||
|
||||
Открывается главное меню со списком справочников. Навигация двухуровневая:
|
||||
|
||||
```
|
||||
rubric → выбор справочника → выбор раздела
|
||||
```
|
||||
|
||||
Вводить можно **номер** или **ключ**:
|
||||
|
||||
```
|
||||
Выберите справочник: 1
|
||||
Выберите справочник: go
|
||||
```
|
||||
|
||||
Внутри справочника:
|
||||
|
||||
```
|
||||
Раздел: 2
|
||||
Раздел: structs
|
||||
Раздел: s ← сокращённый алиас
|
||||
```
|
||||
|
||||
Для возврата на уровень выше — `b`. Для выхода — `q`.
|
||||
|
||||
### Быстрый доступ (без меню)
|
||||
|
||||
```bash
|
||||
# Открыть справочник Go
|
||||
./rubric -book go
|
||||
|
||||
# Открыть конкретный раздел сразу
|
||||
./rubric -book go -topic basics
|
||||
./rubric -book go -topic structs
|
||||
|
||||
# По алиасу
|
||||
./rubric -book g -topic s
|
||||
```
|
||||
|
||||
### Вспомогательные флаги
|
||||
|
||||
```bash
|
||||
./rubric -list # список всех справочников
|
||||
./rubric -help # справка по флагам и ключам
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Структура проекта
|
||||
|
||||
```
|
||||
rubric/
|
||||
├── go.mod
|
||||
├── main.go # точка входа, разбор флагов
|
||||
├── utils/
|
||||
│ └── display.go # цвета, форматирование вывода
|
||||
├── catalog/
|
||||
│ ├── catalog.go # главное меню, реестр справочников
|
||||
│ └── gobook.go # справочник Go: меню и навигация
|
||||
└── modules/
|
||||
└── gomodules/
|
||||
├── registry.go # реестр разделов Go
|
||||
├── basics.go # раздел: установка и команды
|
||||
└── structures.go # раздел: структура программы
|
||||
```
|
||||
|
||||
Каждый **справочник** — отдельный файл в `catalog/`. Каждый **раздел** внутри справочника — отдельный файл в соответствующем подпакете `modules/`.
|
||||
|
||||
---
|
||||
|
||||
## Справочники и разделы
|
||||
|
||||
### go — Язык Go
|
||||
|
||||
| Ключ | Алиасы | Раздел |
|
||||
|------|--------|--------|
|
||||
| `basics` | `b`, `base`, `install` | Установка и основные команды |
|
||||
| `structs` | `s`, `struct`, `vars`, `types` | Структура программы |
|
||||
|
||||
#### basics — Установка и основные команды
|
||||
|
||||
- Установка Go на Linux / macOS / Windows
|
||||
- Структура проекта, `go mod init`
|
||||
- `go build` — сборка и кросс-компиляция
|
||||
- `go run` — запуск без сборки
|
||||
- `go test` — тесты, покрытие, бенчмарки, race-детектор
|
||||
- `go fmt`, `go vet`, `golangci-lint` — форматирование и анализ
|
||||
- `go doc` — документация
|
||||
- `go get`, `go mod tidy` — управление зависимостями
|
||||
- `go env`, `go clean`, `go install` — окружение и утилиты
|
||||
|
||||
#### structs — Структура программы
|
||||
|
||||
- Имена, ключевые слова, предопределённые идентификаторы
|
||||
- Четыре вида объявлений: `const`, `type`, `var`, `func`
|
||||
- Переменные: `var`, `:=`, нулевые значения, `var`-блоки
|
||||
- Указатели: `&`, `*`, nil, сравнение адресов
|
||||
- Функция `new` и когда её использовать
|
||||
- Время жизни переменных, escape analysis
|
||||
- Присваивания: простые, составные, tuple assignment, `_`
|
||||
- Объявления типов, алиасы, конверсии
|
||||
- Пакеты, файлы, все формы импорта
|
||||
- Инициализация пакетов, функция `init()`
|
||||
- Область видимости, затенение (shadowing)
|
||||
|
||||
---
|
||||
|
||||
## Как добавить новый раздел в справочник Go
|
||||
|
||||
1. Создать файл `modules/gomodules/mymodule.go`:
|
||||
|
||||
```go
|
||||
package gomodules
|
||||
|
||||
import "rubric/utils"
|
||||
|
||||
var MyModule = Module{
|
||||
Key: "mykey",
|
||||
Aliases: []string{"mk"},
|
||||
Title: "Название раздела",
|
||||
Description: "Краткое описание",
|
||||
Run: showMyModule,
|
||||
}
|
||||
|
||||
func showMyModule() {
|
||||
utils.Header("Заголовок")
|
||||
utils.Section("Подраздел")
|
||||
utils.Code(`// пример кода`)
|
||||
utils.Note("Важное замечание")
|
||||
utils.Footer()
|
||||
}
|
||||
```
|
||||
|
||||
2. Добавить в `modules/gomodules/registry.go` в срез `All`:
|
||||
|
||||
```go
|
||||
var All = []Module{
|
||||
BasicsModule,
|
||||
StructuresModule,
|
||||
MyModule, // ← добавить сюда
|
||||
}
|
||||
```
|
||||
|
||||
Больше ничего не требуется — модуль автоматически появится в меню, поиске и навигации.
|
||||
|
||||
## Как добавить новый справочник (например, linux)
|
||||
|
||||
1. Создать `catalog/linuxbook.go` по образцу `catalog/gobook.go`
|
||||
2. Создать `modules/linuxmodules/` с нужными разделами
|
||||
3. Добавить `LinuxBook` в срез `catalog.All` в `catalog/catalog.go`
|
||||
4. При необходимости расширить `switch *book` в `main.go` для флага `-book linux -topic ...`
|
||||
|
||||
---
|
||||
|
||||
## Вспомогательные функции форматирования
|
||||
|
||||
Все модули используют пакет `utils` для единообразного вывода:
|
||||
|
||||
| Функция | Описание |
|
||||
|---------|----------|
|
||||
| `utils.Header(title)` | Заголовок раздела в рамке |
|
||||
| `utils.Section(title)` | Подзаголовок с линией |
|
||||
| `utils.Code(code)` | Блок кода в рамке |
|
||||
| `utils.Command(cmd, desc)` | Команда с описанием |
|
||||
| `utils.Note(text)` | Замечание с иконкой 💡 |
|
||||
| `utils.Info(label, value)` | Пара ключ — значение |
|
||||
| `utils.Println(text)` | Обычная строка с отступом |
|
||||
| `utils.Footer()` | Нижняя линия раздела |
|
||||
|
||||
---
|
||||
|
||||
## Цветовой вывод
|
||||
|
||||
Использует ANSI escape-коды. Работает в большинстве современных терминалов: iTerm2, Terminal.app, GNOME Terminal, Windows Terminal, tmux и других.
|
||||
|
||||
В средах без поддержки цвета (например, при перенаправлении вывода в файл) escape-коды будут видны как есть. При необходимости отключения цвета — установите переменную `NO_COLOR=1` перед запуском или реализуйте проверку `os.Getenv("NO_COLOR")` в `utils/display.go`.
|
||||
176
catalog/catalog.go
Normal file
176
catalog/catalog.go
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
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"
|
||||
}
|
||||
122
catalog/gobook.go
Normal file
122
catalog/gobook.go
Normal file
|
|
@ -0,0 +1,122 @@
|
|||
package catalog
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"rubric/modules/gomodules"
|
||||
"rubric/utils"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GoBook — запись справочника Go в главном меню
|
||||
var GoBook = Book{
|
||||
Key: "go",
|
||||
Aliases: []string{"g", "golang"},
|
||||
Title: "Справочник по языку Go",
|
||||
Description: "Установка, синтаксис, типы, функции, горутины, интерфейсы и многое другое",
|
||||
Run: runGoBook,
|
||||
}
|
||||
|
||||
func runGoBook(scanner *bufio.Scanner) {
|
||||
for {
|
||||
showGoMenu()
|
||||
|
||||
input, ok := ReadLine(scanner)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if input == "" {
|
||||
continue
|
||||
}
|
||||
if IsQuit(input) {
|
||||
fmt.Printf("\n %s%sДо свидания!%s\n\n",
|
||||
utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||
// сигнализируем главному циклу о выходе через os.Exit
|
||||
// чтобы не усложнять — просто выходим
|
||||
return
|
||||
}
|
||||
if IsBack(input) {
|
||||
return // назад в главное меню
|
||||
}
|
||||
|
||||
// По номеру
|
||||
if n, err := strconv.Atoi(input); err == nil {
|
||||
if n >= 1 && n <= len(gomodules.All) {
|
||||
gomodules.All[n-1].Run()
|
||||
waitBack(scanner)
|
||||
continue
|
||||
}
|
||||
fmt.Printf("\n %s%sНомер %d вне диапазона (1–%d)%s\n",
|
||||
utils.ColorBold, utils.ColorRed, n, len(gomodules.All), utils.ColorReset)
|
||||
continue
|
||||
}
|
||||
|
||||
// По ключу
|
||||
if m, ok := gomodules.Find(input); ok {
|
||||
m.Run()
|
||||
waitBack(scanner)
|
||||
continue
|
||||
}
|
||||
|
||||
fmt.Printf("\n %s%sНе понял '%s'. Номер, ключ раздела, b — назад.%s\n",
|
||||
utils.ColorBold, utils.ColorRed, input, utils.ColorReset)
|
||||
}
|
||||
}
|
||||
|
||||
func showGoMenu() {
|
||||
utils.Header("Справочник Go")
|
||||
|
||||
fmt.Printf(" %s%sРазделы:%s\n\n",
|
||||
utils.ColorBold, utils.ColorWhite, utils.ColorReset)
|
||||
|
||||
for i, m := range gomodules.All {
|
||||
aliases := ""
|
||||
if len(m.Aliases) > 0 {
|
||||
aliases = fmt.Sprintf("%s [%s]%s",
|
||||
utils.ColorDim, strings.Join(m.Aliases, ", "), utils.ColorReset)
|
||||
}
|
||||
fmt.Printf(" %s%s %2d.%s %s%-10s%s %s%-32s%s%s\n",
|
||||
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold, m.Key, utils.ColorReset,
|
||||
utils.ColorWhite, m.Title, utils.ColorReset,
|
||||
aliases,
|
||||
)
|
||||
fmt.Printf(" %s%s%s\n\n",
|
||||
utils.ColorDim, m.Description, utils.ColorReset)
|
||||
}
|
||||
|
||||
fmt.Printf(" %s%sРаздел (номер / ключ / b — главное меню / q — выход):%s ",
|
||||
utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
func waitBack(scanner *bufio.Scanner) {
|
||||
fmt.Printf("\n %s%s[Enter — назад в меню Go, b — главное меню]%s ",
|
||||
utils.ColorDim, utils.ColorCyan, utils.ColorReset)
|
||||
if scanner.Scan() {
|
||||
if IsBack(strings.TrimSpace(scanner.Text())) {
|
||||
// вернёмся на уровень выше — используем специальный возврат
|
||||
// реализуем через панику+recover для чистоты навигации
|
||||
panic(backSignal{})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// backSignal — сигнал для возврата на уровень выше из waitBack
|
||||
type backSignal struct{}
|
||||
|
||||
func init() {
|
||||
// Оборачиваем runGoBook чтобы перехватывать backSignal
|
||||
orig := GoBook.Run
|
||||
GoBook.Run = func(scanner *bufio.Scanner) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
if _, ok := r.(backSignal); !ok {
|
||||
panic(r) // пробрасываем не наши паники
|
||||
}
|
||||
// backSignal — просто возвращаемся в главное меню, ничего не делаем
|
||||
}
|
||||
}()
|
||||
orig(scanner)
|
||||
}
|
||||
}
|
||||
3
go.mod
Normal file
3
go.mod
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
module rubric
|
||||
|
||||
go 1.21
|
||||
68
main.go
Normal file
68
main.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"rubric/catalog"
|
||||
"rubric/modules/gomodules"
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
func main() {
|
||||
book := flag.String("book", "", "открыть справочник напрямую (например: -book go)")
|
||||
topic := flag.String("topic", "", "открыть раздел (используется вместе с -book)")
|
||||
list := flag.Bool("list", false, "список всех справочников и разделов")
|
||||
help := flag.Bool("help", false, "справка по использованию")
|
||||
flag.Parse()
|
||||
|
||||
scanner := bufio.NewScanner(os.Stdin)
|
||||
|
||||
if *help {
|
||||
catalog.ShowHelp()
|
||||
return
|
||||
}
|
||||
|
||||
if *list {
|
||||
catalog.ListAll()
|
||||
return
|
||||
}
|
||||
|
||||
// -book go -topic basics
|
||||
if *book != "" && *topic != "" {
|
||||
switch *book {
|
||||
case "go", "g", "golang":
|
||||
m, ok := gomodules.Find(*topic)
|
||||
if !ok {
|
||||
fmt.Printf("\n %s%sРаздел '%s' не найден в справочнике Go.%s\n",
|
||||
utils.ColorBold, utils.ColorRed, *topic, utils.ColorReset)
|
||||
gomodules.ListModules()
|
||||
os.Exit(1)
|
||||
}
|
||||
m.Run()
|
||||
default:
|
||||
fmt.Printf("\n %s%sСправочник '%s' не найден.%s\n",
|
||||
utils.ColorBold, utils.ColorRed, *book, utils.ColorReset)
|
||||
catalog.ListAll()
|
||||
os.Exit(1)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// -book go
|
||||
if *book != "" {
|
||||
b, ok := catalog.Find(*book)
|
||||
if !ok {
|
||||
fmt.Printf("\n %s%sСправочник '%s' не найден.%s\n",
|
||||
utils.ColorBold, utils.ColorRed, *book, utils.ColorReset)
|
||||
catalog.ListAll()
|
||||
os.Exit(1)
|
||||
}
|
||||
b.Run(scanner)
|
||||
return
|
||||
}
|
||||
|
||||
// Интерактивный режим
|
||||
catalog.RunInteractive(scanner)
|
||||
}
|
||||
185
modules/gomodules/go_basics.go
Normal file
185
modules/gomodules/go_basics.go
Normal file
|
|
@ -0,0 +1,185 @@
|
|||
package gomodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var BasicsModule = Module{
|
||||
Key: "basics",
|
||||
Aliases: []string{"b", "base", "install"},
|
||||
Title: "Установка и основные команды",
|
||||
Description: "Установка Go, go build, go test, go doc и базовые инструменты",
|
||||
Run: showBasics,
|
||||
}
|
||||
|
||||
func showBasics() {
|
||||
utils.Header("Go — Установка и основные команды")
|
||||
|
||||
// ─── Установка ───────────────────────────────────────────────
|
||||
utils.Section("Установка Go")
|
||||
|
||||
utils.Println("Официальный сайт: https://go.dev/dl/")
|
||||
fmt.Println()
|
||||
utils.Info("Linux / macOS", "скачать архив и распаковать в /usr/local/go")
|
||||
utils.Code(`# Linux (пример для версии 1.22)
|
||||
wget https://go.dev/dl/go1.22.0.linux-amd64.tar.gz
|
||||
sudo tar -C /usr/local -xzf go1.22.0.linux-amd64.tar.gz
|
||||
|
||||
# Добавить в ~/.bashrc или ~/.zshrc:
|
||||
export PATH=$PATH:/usr/local/go/bin`)
|
||||
|
||||
utils.Info("macOS (Homebrew)", "brew install go")
|
||||
utils.Info("Windows", "скачать .msi установщик с go.dev/dl")
|
||||
utils.Code(`# Проверить установку:
|
||||
go version
|
||||
# go version go1.22.0 linux/amd64`)
|
||||
|
||||
utils.Note("Переменная GOPATH по умолчанию — ~/go. Код проектов хранится в ~/go/src/")
|
||||
|
||||
// ─── Структура проекта ───────────────────────────────────────
|
||||
utils.Section("Структура проекта и модули")
|
||||
|
||||
utils.Code(`# Создать новый модуль
|
||||
go mod init github.com/user/myproject
|
||||
|
||||
# Структура типичного проекта:
|
||||
myproject/
|
||||
├── go.mod # описание модуля и зависимостей
|
||||
├── go.sum # контрольные суммы зависимостей
|
||||
├── main.go # точка входа
|
||||
├── internal/ # внутренние пакеты (недоступны снаружи)
|
||||
└── pkg/ # переиспользуемые пакеты`)
|
||||
|
||||
// ─── go build ────────────────────────────────────────────────
|
||||
utils.Section("Команды: сборка")
|
||||
|
||||
utils.Command("go build", "скомпилировать текущий пакет")
|
||||
utils.Command("go build ./...", "скомпилировать все пакеты проекта")
|
||||
utils.Command("go build -o myapp", "указать имя выходного файла")
|
||||
utils.Command("go build -v", "показать компилируемые пакеты")
|
||||
utils.Command("go build -race", "включить детектор гонок данных")
|
||||
fmt.Println()
|
||||
utils.Code(`# Кросс-компиляция (собрать под Windows на Linux):
|
||||
GOOS=windows GOARCH=amd64 go build -o app.exe
|
||||
|
||||
# Основные пары GOOS/GOARCH:
|
||||
# linux/amd64 linux/arm64
|
||||
# windows/amd64
|
||||
# darwin/amd64 darwin/arm64 (macOS Intel / Apple Silicon)`)
|
||||
|
||||
// ─── go run ──────────────────────────────────────────────────
|
||||
utils.Section("Команды: запуск")
|
||||
|
||||
utils.Command("go run main.go", "скомпилировать и сразу запустить файл")
|
||||
utils.Command("go run .", "запустить пакет в текущей директории")
|
||||
utils.Command("go run ./cmd/app", "запустить конкретный пакет")
|
||||
|
||||
// ─── go test ─────────────────────────────────────────────────
|
||||
utils.Section("Команды: тестирование")
|
||||
|
||||
utils.Command("go test ./...", "запустить все тесты проекта")
|
||||
utils.Command("go test -v", "подробный вывод (имена тестов)")
|
||||
utils.Command("go test -run TestName", "запустить конкретный тест")
|
||||
utils.Command("go test -run TestName/SubTest", "запустить под-тест")
|
||||
utils.Command("go test -count=3", "запустить каждый тест 3 раза")
|
||||
utils.Command("go test -timeout 30s", "таймаут на все тесты")
|
||||
utils.Command("go test -race", "тесты с детектором гонок")
|
||||
utils.Command("go test -cover", "показать покрытие кода")
|
||||
utils.Command("go test -coverprofile=c.out", "сохранить отчёт о покрытии")
|
||||
utils.Command("go tool cover -html=c.out", "открыть покрытие в браузере")
|
||||
utils.Command("go test -bench=.", "запустить бенчмарки")
|
||||
utils.Command("go test -bench=. -benchmem", "бенчмарки + аллокации памяти")
|
||||
fmt.Println()
|
||||
utils.Code(`// Пример тестового файла (math_test.go):
|
||||
package mypackage
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
result := Add(2, 3)
|
||||
if result != 5 {
|
||||
t.Errorf("Add(2,3) = %d, хотели 5", result)
|
||||
}
|
||||
}
|
||||
|
||||
// Под-тесты (subtests):
|
||||
func TestOperations(t *testing.T) {
|
||||
t.Run("сложение", func(t *testing.T) { ... })
|
||||
t.Run("вычитание", func(t *testing.T) { ... })
|
||||
}
|
||||
|
||||
// Бенчмарк:
|
||||
func BenchmarkAdd(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Add(2, 3)
|
||||
}
|
||||
}`)
|
||||
|
||||
// ─── go fmt / vet / lint ─────────────────────────────────────
|
||||
utils.Section("Команды: форматирование и анализ")
|
||||
|
||||
utils.Command("go fmt ./...", "автоформатирование всех файлов")
|
||||
utils.Command("go vet ./...", "статический анализ на типичные ошибки")
|
||||
utils.Command("go vet -shadow ./...", "поиск затенения переменных")
|
||||
utils.Command("gofmt -d main.go", "показать diff без записи")
|
||||
utils.Command("gofmt -w main.go", "записать форматирование в файл")
|
||||
fmt.Println()
|
||||
utils.Note("golangci-lint — популярный агрегатор линтеров: golangci-lint.run")
|
||||
utils.Code(`# Установка golangci-lint:
|
||||
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
||||
golangci-lint run ./...`)
|
||||
|
||||
// ─── go doc ──────────────────────────────────────────────────
|
||||
utils.Section("Документация: go doc")
|
||||
|
||||
utils.Command("go doc fmt", "документация пакета fmt")
|
||||
utils.Command("go doc fmt.Println", "документация конкретной функции")
|
||||
utils.Command("go doc -all fmt", "вся документация пакета")
|
||||
utils.Command("go doc -src fmt.Println", "исходный код функции")
|
||||
utils.Command("go doc builtin", "встроенные функции и типы")
|
||||
fmt.Println()
|
||||
utils.Code(`// Комментарий к функции становится документацией:
|
||||
// Add складывает два целых числа и возвращает результат.
|
||||
// Оба аргумента должны быть неотрицательными.
|
||||
func Add(a, b int) int {
|
||||
return a + b
|
||||
}
|
||||
|
||||
// Запустить веб-сервер документации:
|
||||
// godoc -http=:6060`)
|
||||
|
||||
// ─── go get / mod ────────────────────────────────────────────
|
||||
utils.Section("Управление зависимостями")
|
||||
|
||||
utils.Command("go get github.com/pkg/errors", "добавить зависимость")
|
||||
utils.Command("go get github.com/pkg/errors@v0.9.1", "конкретная версия")
|
||||
utils.Command("go get github.com/pkg/errors@latest", "последняя версия")
|
||||
utils.Command("go mod tidy", "удалить лишние / добавить нужные зависимости")
|
||||
utils.Command("go mod vendor", "скопировать зависимости в ./vendor")
|
||||
utils.Command("go mod graph", "граф зависимостей")
|
||||
utils.Command("go mod why pkg", "почему пакет является зависимостью")
|
||||
utils.Command("go list -m all", "список всех зависимостей")
|
||||
|
||||
// ─── go install / env ─────────────────────────────────────────
|
||||
utils.Section("Прочие полезные команды")
|
||||
|
||||
utils.Command("go install tool@latest", "установить CLI-инструмент глобально")
|
||||
utils.Command("go env", "показать все переменные окружения Go")
|
||||
utils.Command("go env GOPATH", "показать конкретную переменную")
|
||||
utils.Command("go env -w GONOSUMCHECK=*", "установить переменную глобально")
|
||||
utils.Command("go clean -cache", "очистить кэш сборки")
|
||||
utils.Command("go clean -modcache", "очистить кэш модулей")
|
||||
utils.Command("go generate ./...", "запустить //go:generate директивы")
|
||||
utils.Command("go work init ./mod1 ./mod2", "workspace для нескольких модулей")
|
||||
fmt.Println()
|
||||
utils.Code(`# Ключевые переменные окружения:
|
||||
GOROOT — где установлен Go (/usr/local/go)
|
||||
GOPATH — рабочая директория (~/go)
|
||||
GOBIN — куда устанавливаются бинарники (~/go/bin)
|
||||
GOPROXY — прокси для модулей (https://proxy.golang.org)
|
||||
GONOSUMCHECK — пакеты без проверки checksumdb
|
||||
CGO_ENABLED — включить/отключить cgo (0 или 1)`)
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
101
modules/gomodules/go_registry.go
Normal file
101
modules/gomodules/go_registry.go
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
package gomodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Module описывает один раздел справочника
|
||||
type Module struct {
|
||||
Key string // основной ключ для -topic
|
||||
Aliases []string // сокращения
|
||||
Title string // название в меню
|
||||
Description string // краткое описание
|
||||
Run func() // функция вывода
|
||||
}
|
||||
|
||||
// All — список всех модулей в порядке меню
|
||||
var All = []Module{
|
||||
BasicsModule,
|
||||
StructuresModule,
|
||||
// сюда будут добавляться новые модули
|
||||
}
|
||||
|
||||
// Find ищет модуль по ключу или алиасу
|
||||
func Find(key string) (Module, bool) {
|
||||
key = strings.ToLower(strings.TrimSpace(key))
|
||||
for _, m := range All {
|
||||
if m.Key == key {
|
||||
return m, true
|
||||
}
|
||||
for _, a := range m.Aliases {
|
||||
if a == key {
|
||||
return m, true
|
||||
}
|
||||
}
|
||||
}
|
||||
return Module{}, false
|
||||
}
|
||||
|
||||
// ShowMenu отображает интерактивное меню
|
||||
func ShowMenu() {
|
||||
utils.Header("Справочник Go")
|
||||
|
||||
fmt.Printf(" %s%sДоступные разделы:%s\n\n", utils.ColorBold, utils.ColorWhite, utils.ColorReset)
|
||||
|
||||
for i, m := range All {
|
||||
aliases := ""
|
||||
if len(m.Aliases) > 0 {
|
||||
aliases = fmt.Sprintf("%s [%s]%s", utils.ColorDim, strings.Join(m.Aliases, ", "), utils.ColorReset)
|
||||
}
|
||||
fmt.Printf(" %s%s%2d.%s %-10s %s%-36s%s%s\n",
|
||||
utils.ColorBold, utils.ColorCyan, i+1, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold+m.Key+utils.ColorReset,
|
||||
utils.ColorWhite, m.Title, utils.ColorReset,
|
||||
aliases,
|
||||
)
|
||||
fmt.Printf(" %s%s%s\n", utils.ColorDim, m.Description, utils.ColorReset)
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
fmt.Printf(" %s%sВведите номер или ключ раздела (q — выход):%s ", utils.ColorBold, utils.ColorGreen, utils.ColorReset)
|
||||
}
|
||||
|
||||
// ShowHelp выводит справку по ключам
|
||||
func ShowHelp() {
|
||||
utils.Header("rubric — справка по использованию")
|
||||
|
||||
utils.Section("Запуск")
|
||||
utils.Command("./rubric", "интерактивное меню")
|
||||
utils.Command("./rubric -topic basics", "открыть раздел напрямую")
|
||||
utils.Command("./rubric -topic b", "по короткому алиасу")
|
||||
utils.Command("./rubric -list", "список всех разделов")
|
||||
utils.Command("./rubric -help", "эта справка")
|
||||
fmt.Println()
|
||||
|
||||
utils.Section("Доступные разделы и ключи")
|
||||
for _, m := range All {
|
||||
keys := append([]string{m.Key}, m.Aliases...)
|
||||
fmt.Printf(" %s%-10s%s %-32s %sключи: %s%s\n",
|
||||
utils.ColorBold+utils.ColorCyan, m.Key, utils.ColorReset,
|
||||
m.Title,
|
||||
utils.ColorDim, strings.Join(keys, ", "), utils.ColorReset,
|
||||
)
|
||||
}
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
|
||||
// ListModules выводит краткий список модулей
|
||||
func ListModules() {
|
||||
fmt.Printf("\n%s%sРазделы Go:%s\n\n", utils.ColorBold, utils.ColorCyan, utils.ColorReset)
|
||||
for i, m := range All {
|
||||
fmt.Printf(" %s%d.%s %-10s — %s\n",
|
||||
utils.ColorDim, i+1, utils.ColorReset,
|
||||
utils.ColorYellow+utils.ColorBold+m.Key+utils.ColorReset,
|
||||
m.Title,
|
||||
)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
461
modules/gomodules/go_structures.go
Normal file
461
modules/gomodules/go_structures.go
Normal file
|
|
@ -0,0 +1,461 @@
|
|||
package gomodules
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rubric/utils"
|
||||
)
|
||||
|
||||
var StructuresModule = Module{
|
||||
Key: "structs",
|
||||
Aliases: []string{"s", "struct", "vars", "types"},
|
||||
Title: "Структура программы",
|
||||
Description: "Имена, объявления, переменные, указатели, типы, пакеты, область видимости",
|
||||
Run: showStructures,
|
||||
}
|
||||
|
||||
func showStructures() {
|
||||
utils.Header("Go — Структура программы")
|
||||
|
||||
// ─── Имена и ключевые слова ───────────────────────────────────
|
||||
utils.Section("Имена и ключевые слова")
|
||||
|
||||
utils.Println("Имя начинается с буквы (Unicode) или символа подчёркивания, затем — буквы, цифры, _.")
|
||||
utils.Println("Регистр имеет значение: " +
|
||||
utils.ColorBold + utils.ColorCyan + "Name" + utils.ColorReset +
|
||||
" и " +
|
||||
utils.ColorBold + utils.ColorCyan + "name" + utils.ColorReset +
|
||||
" — разные идентификаторы.")
|
||||
fmt.Println()
|
||||
|
||||
utils.Println(utils.ColorBold + utils.ColorYellow + "25 ключевых слов языка Go:" + utils.ColorReset)
|
||||
fmt.Println()
|
||||
keywords := [][]string{
|
||||
{"break", "case", "chan", "const", "continue"},
|
||||
{"default", "defer", "else", "fallthrough", "for"},
|
||||
{"func", "go", "goto", "if", "import"},
|
||||
{"interface", "map", "package", "range", "return"},
|
||||
{"select", "struct", "switch", "type", "var"},
|
||||
}
|
||||
for _, row := range keywords {
|
||||
fmt.Print(" ")
|
||||
for _, kw := range row {
|
||||
fmt.Printf("%s%-14s%s", utils.ColorCyan+utils.ColorBold, kw, utils.ColorReset)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
fmt.Println()
|
||||
|
||||
utils.Println(utils.ColorBold + utils.ColorYellow + "Предопределённые имена (не ключевые слова — можно переопределить, но не стоит):" + utils.ColorReset)
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorDim + " Типы:" + utils.ColorReset)
|
||||
utils.Println(" bool byte rune string error")
|
||||
utils.Println(" int int8 int16 int32 int64")
|
||||
utils.Println(" uint uint8 uint16 uint32 uint64 uintptr")
|
||||
utils.Println(" float32 float64 complex64 complex128")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorDim + " Константы:" + utils.ColorReset)
|
||||
utils.Println(" true false iota")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorDim + " Нулевое значение:" + utils.ColorReset)
|
||||
utils.Println(" nil")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorDim + " Встроенные функции:" + utils.ColorReset)
|
||||
utils.Println(" append cap close complex copy delete imag")
|
||||
utils.Println(" len make new panic print println real recover")
|
||||
fmt.Println()
|
||||
utils.Note("Если имя начинается с заглавной буквы — оно экспортируется из пакета (публичное).")
|
||||
utils.Note("Стиль: camelCase для локальных, CamelCase для экспортируемых. Аббревиатуры — заглавными: URL, ID, HTTP.")
|
||||
|
||||
// ─── Объявления ───────────────────────────────────────────────
|
||||
utils.Section("Объявления")
|
||||
|
||||
utils.Println("В Go четыре вида объявлений верхнего уровня:")
|
||||
fmt.Println()
|
||||
utils.Code(`package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// const — константа верхнего уровня
|
||||
const Greeting = "Привет"
|
||||
|
||||
// type — новый тип
|
||||
type Celsius float64
|
||||
|
||||
// var — переменная верхнего уровня
|
||||
var boilingPoint Celsius = 100
|
||||
|
||||
// func — функция
|
||||
func main() {
|
||||
fmt.Println(Greeting, boilingPoint)
|
||||
}`)
|
||||
utils.Println("Порядок объявлений на верхнем уровне не важен — компилятор разрешает зависимости сам.")
|
||||
|
||||
// ─── Переменные ───────────────────────────────────────────────
|
||||
utils.Section("Переменные и способы объявления")
|
||||
|
||||
utils.Println(utils.ColorBold + "1. Полное объявление через var:" + utils.ColorReset)
|
||||
utils.Code(`var имя тип = выражение
|
||||
|
||||
var x int // x == 0 (нулевое значение)
|
||||
var s string // s == ""
|
||||
var b bool // b == false
|
||||
var p *int // p == nil
|
||||
|
||||
// Несколько переменных одного типа:
|
||||
var i, j, k int
|
||||
|
||||
// Несколько переменных разных типов:
|
||||
var (
|
||||
name string = "Gopher"
|
||||
age int = 3
|
||||
active bool = true
|
||||
)`)
|
||||
|
||||
utils.Println(utils.ColorBold + "2. Краткое объявление := (только внутри функций):" + utils.ColorReset)
|
||||
utils.Code(`имя := выражение // тип выводится автоматически
|
||||
|
||||
x := 42 // int
|
||||
s := "hello" // string
|
||||
f := 3.14 // float64
|
||||
b := true // bool
|
||||
|
||||
// Несколько переменных:
|
||||
i, j := 0, 1
|
||||
name, err := os.Open("file.txt") // часто используется для ошибок
|
||||
|
||||
// ВАЖНО: хотя бы одна переменная слева должна быть новой:
|
||||
x, y := 1, 2 // ok: обе новые
|
||||
x, y = 3, 4 // ok: присваивание (уже объявлены)
|
||||
x, z := 5, 6 // ok: z — новая, x — переприсваивается`)
|
||||
|
||||
utils.Println(utils.ColorBold + "3. Нулевые значения (zero values):" + utils.ColorReset)
|
||||
utils.Code(`// Каждый тип имеет нулевое значение при объявлении без инициализации:
|
||||
var i int // 0
|
||||
var f float64 // 0.0
|
||||
var b bool // false
|
||||
var s string // ""
|
||||
var p *int // nil
|
||||
var sl []int // nil
|
||||
var m map[string]int // nil
|
||||
var fn func() // nil
|
||||
var err error // nil`)
|
||||
utils.Note("В Go нет неинициализированных переменных — нулевое значение гарантировано.")
|
||||
|
||||
utils.Println(utils.ColorBold + "4. Объявление нескольких переменных с var-блоком:" + utils.ColorReset)
|
||||
utils.Code(`// Группировка для читаемости:
|
||||
var (
|
||||
debug = false
|
||||
version = "1.0.0"
|
||||
maxConn = 100
|
||||
)`)
|
||||
|
||||
// ─── Указатели ────────────────────────────────────────────────
|
||||
utils.Section("Указатели")
|
||||
|
||||
utils.Println("Указатель хранит " + utils.ColorBold + "адрес" + utils.ColorReset + " переменной в памяти.")
|
||||
utils.Code(`var x int = 42
|
||||
|
||||
p := &x // p — указатель на x, тип *int
|
||||
fmt.Println(p) // 0xc0000b4008 (адрес)
|
||||
fmt.Println(*p) // 42 (разыменование — значение по адресу)
|
||||
|
||||
*p = 100 // изменить значение x через указатель
|
||||
fmt.Println(x) // 100
|
||||
|
||||
// Указатель на указатель:
|
||||
pp := &p // тип **int
|
||||
fmt.Println(**pp) // 100`)
|
||||
|
||||
utils.Code(`// Нулевое значение указателя — nil:
|
||||
var p *int
|
||||
fmt.Println(p == nil) // true
|
||||
// *p = 1 — паника! нельзя разыменовывать nil-указатель
|
||||
|
||||
// Сравнение: указатели равны, если указывают на один адрес:
|
||||
a, b := 1, 1
|
||||
pa, pb := &a, &b
|
||||
fmt.Println(pa == pb) // false (разные переменные)
|
||||
pa2 := &a
|
||||
fmt.Println(pa == pa2) // true (один и тот же адрес)`)
|
||||
|
||||
utils.Note("Go — безопасный язык: нет арифметики указателей (без пакета unsafe).")
|
||||
utils.Note("Указатель позволяет функции изменять переменную вызывающего кода.")
|
||||
|
||||
// ─── Функция new ──────────────────────────────────────────────
|
||||
utils.Section("Функция new")
|
||||
|
||||
utils.Println("new(T) выделяет память для значения типа T, инициализирует нулевым значением,")
|
||||
utils.Println("возвращает указатель *T.")
|
||||
utils.Code(`p := new(int) // *int, *p == 0
|
||||
fmt.Println(*p) // 0
|
||||
*p = 7
|
||||
fmt.Println(*p) // 7
|
||||
|
||||
// Эквивалентно:
|
||||
var tmp int
|
||||
p2 := &tmp
|
||||
|
||||
// new для структур:
|
||||
type Point struct{ X, Y int }
|
||||
pt := new(Point) // *Point, pt.X==0, pt.Y==0
|
||||
pt.X = 10`)
|
||||
|
||||
utils.Note("new используется редко — чаще пишут &T{...} для структур или var x T; return &x.")
|
||||
utils.Code(`// Эти три записи эквивалентны:
|
||||
p1 := new(int)
|
||||
|
||||
var v int
|
||||
p2 := &v
|
||||
|
||||
p3 := &struct{ int }{} // анонимная структура — другой случай`)
|
||||
|
||||
// ─── Время жизни переменных ───────────────────────────────────
|
||||
utils.Section("Время жизни переменных")
|
||||
|
||||
utils.Println("Время жизни — период, в течение которого переменная существует в памяти.")
|
||||
fmt.Println()
|
||||
utils.Println(utils.ColorBold + "Переменные пакетного уровня:" + utils.ColorReset +
|
||||
" живут всё время работы программы.")
|
||||
utils.Println(utils.ColorBold + "Локальные переменные:" + utils.ColorReset +
|
||||
" создаются при каждом выполнении объявления, удаляются когда становятся недостижимы.")
|
||||
fmt.Println()
|
||||
utils.Code(`// Escape analysis — компилятор решает, где хранить переменную:
|
||||
// стек (быстро) или куча (heap, медленнее, но живёт дольше).
|
||||
|
||||
func newInt() *int {
|
||||
v := 42 // v "убегает" на кучу (heap escape),
|
||||
return &v // потому что возвращается её адрес.
|
||||
}
|
||||
|
||||
func sum(a, b int) int {
|
||||
result := a + b // result на стеке — не "убегает",
|
||||
return result // возвращается значение, не адрес.
|
||||
}`)
|
||||
utils.Note("Не нужно думать о стеке/куче вручную — GC Go управляет памятью автоматически.")
|
||||
utils.Note("go build -gcflags='-m' — показывает, какие переменные уходят на кучу.")
|
||||
|
||||
// ─── Присваивания ─────────────────────────────────────────────
|
||||
utils.Section("Присваивания")
|
||||
|
||||
utils.Println(utils.ColorBold + "Простое присваивание:" + utils.ColorReset)
|
||||
utils.Code(`x = 1
|
||||
*p = true
|
||||
person.name = "Алиса"
|
||||
count[x] = count[x] * scale`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Составные операторы присваивания:" + utils.ColorReset)
|
||||
utils.Code(`x += 1 x -= 1 x *= 2 x /= 2 x %= 3
|
||||
x &= y x |= y x ^= y x <<= 2 x >>= 1`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Инкремент и декремент (только постфиксные, только как операторы):" + utils.ColorReset)
|
||||
utils.Code(`x++ // x = x + 1
|
||||
x-- // x = x - 1
|
||||
|
||||
// ВАЖНО: в Go нет префиксных ++/-- и нет выражений с ++/--:
|
||||
// ++x — ошибка компиляции
|
||||
// y = x++ — ошибка компиляции`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Множественное присваивание (tuple assignment):" + utils.ColorReset)
|
||||
utils.Code(`// Все правые части вычисляются ДО записи в левые:
|
||||
x, y = y, x // обмен значениями
|
||||
|
||||
a, b, c := 1, 2, 3
|
||||
a, b, c = b, c, a // сдвиг
|
||||
|
||||
// Возврат нескольких значений из функции:
|
||||
quotient, remainder := divide(17, 5)
|
||||
|
||||
// Игнорирование значения через _:
|
||||
_, err = fmt.Println("hi")`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Присваиваемость (assignability):" + utils.ColorReset)
|
||||
utils.Code(`// Присваивание допустимо, если:
|
||||
// 1. Типы идентичны
|
||||
// 2. Типы имеют одинаковую базу и хотя бы один — безымянный
|
||||
// 3. T реализует интерфейс I
|
||||
// 4. Присваивание nil совместимому типу (chan, func, interface, map, pointer, slice)
|
||||
|
||||
var i interface{} = "строка" // string → interface{}
|
||||
var n int = 42
|
||||
var f float64 = float64(n) // явное преобразование типов`)
|
||||
|
||||
// ─── Объявления типов ────────────────────────────────────────
|
||||
utils.Section("Объявления типов")
|
||||
|
||||
utils.Println("type создаёт " + utils.ColorBold + "новый именованный тип" + utils.ColorReset + " с тем же базовым представлением.")
|
||||
utils.Code(`type Celsius float64
|
||||
type Fahrenheit float64
|
||||
|
||||
var boiling Celsius = 100
|
||||
|
||||
// Celsius и Fahrenheit — РАЗНЫЕ типы, хотя оба float64:
|
||||
// boiling = Fahrenheit(212) — ошибка компиляции!
|
||||
|
||||
// Явное преобразование разрешено (одно базовое представление):
|
||||
f := Fahrenheit(boiling*9/5 + 32)
|
||||
fmt.Println(f) // 212`)
|
||||
|
||||
utils.Code(`// Методы можно добавлять только к именованным типам:
|
||||
func (c Celsius) String() string {
|
||||
return fmt.Sprintf("%.2f°C", float64(c))
|
||||
}
|
||||
|
||||
fmt.Println(boiling) // 100.00°C (автоматически вызывается String())`)
|
||||
|
||||
utils.Code(`// type-алиас (псевдоним) — НЕ создаёт новый тип, это то же самое:
|
||||
type Rune = int32 // rune — именно так определён в builtin
|
||||
|
||||
// Отличие: алиас и оригинал взаимозаменяемы без конверсии.
|
||||
// Обычный type — нет.`)
|
||||
|
||||
utils.Note("type позволяет добавлять методы к базовым типам (int, string, slice и т.д.).")
|
||||
utils.Note("Явное преобразование T(v) работает если оба типа имеют одно представление.")
|
||||
|
||||
// ─── Пакеты, файлы и импорт ──────────────────────────────────
|
||||
utils.Section("Пакеты, файлы и импорт")
|
||||
|
||||
utils.Println("Пакет — единица инкапсуляции и переиспользования в Go.")
|
||||
utils.Println("Все .go-файлы в одной директории — один пакет. Имя объявляется в первой строке.")
|
||||
fmt.Println()
|
||||
utils.Code(`// Каждый файл начинается с объявления пакета:
|
||||
package main // исполняемая программа
|
||||
package utils // библиотечный пакет
|
||||
package http // пакет стандартной библиотеки`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Импорт пакетов:" + utils.ColorReset)
|
||||
utils.Code(`// Одиночный импорт:
|
||||
import "fmt"
|
||||
|
||||
// Групповой импорт (предпочтительный стиль):
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/user/project/utils" // внешний пакет
|
||||
)`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Формы импорта:" + utils.ColorReset)
|
||||
utils.Code(`import "fmt" // стандартный: fmt.Println(...)
|
||||
import f "fmt" // псевдоним: f.Println(...)
|
||||
import . "fmt" // слияние: Println(...) (не рекомендуется)
|
||||
import _ "image/png" // только для side-эффектов (init)`)
|
||||
|
||||
utils.Note("Неиспользуемый импорт — ошибка компиляции. Используйте _ для подавления.")
|
||||
utils.Note("Имя пакета и имя директории могут отличаться, но по соглашению совпадают.")
|
||||
utils.Code(`// Путь импорта vs имя пакета:
|
||||
import "math/rand" // путь: math/rand
|
||||
rand.Intn(10) // имя пакета: rand
|
||||
|
||||
import "crypto/rand" // другой пакет, то же имя!
|
||||
// Нужен алиас:
|
||||
import (
|
||||
mrand "math/rand"
|
||||
crand "crypto/rand"
|
||||
)`)
|
||||
|
||||
// ─── Инициализация пакетов ────────────────────────────────────
|
||||
utils.Section("Инициализация пакетов")
|
||||
|
||||
utils.Println("Инициализация переменных пакетного уровня происходит в порядке объявления,")
|
||||
utils.Println("с учётом зависимостей между ними.")
|
||||
utils.Code(`// Порядок гарантирован: a, b, c
|
||||
var a = 1
|
||||
var b = a + 1 // 2
|
||||
var c = b * 2 // 4
|
||||
|
||||
// Сложные случаи — через функцию init():
|
||||
var lookup = buildLookup()
|
||||
|
||||
func buildLookup() map[string]int {
|
||||
// ...
|
||||
return m
|
||||
}`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Функция init():" + utils.ColorReset)
|
||||
utils.Code(`// init — специальная функция инициализации пакета:
|
||||
// - вызывается автоматически при запуске программы
|
||||
// - нельзя вызвать явно
|
||||
// - в одном файле может быть несколько init()
|
||||
// - в одном пакете — несколько init() в разных файлах
|
||||
|
||||
func init() {
|
||||
// подготовка состояния, регистрация, настройка
|
||||
log.SetFlags(log.Lshortfile)
|
||||
}`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Порядок инициализации всей программы:" + utils.ColorReset)
|
||||
utils.Code(`// 1. Инициализируются импортируемые пакеты (рекурсивно, от листьев к корню)
|
||||
// 2. Переменные пакетного уровня в порядке объявления / зависимостей
|
||||
// 3. Вызываются все функции init() данного пакета
|
||||
// 4. ... переход к следующему пакету
|
||||
// 5. Последним инициализируется пакет main
|
||||
// 6. Вызывается main.main()
|
||||
|
||||
// import _ "pkg" — импортировать только ради init()
|
||||
// Пример: регистрация драйвера БД:
|
||||
import _ "github.com/lib/pq" // PostgreSQL driver`)
|
||||
|
||||
utils.Note("Порядок вызова init() между файлами одного пакета определяется алфавитным порядком имён файлов.")
|
||||
|
||||
// ─── Область видимости ───────────────────────────────────────
|
||||
utils.Section("Область видимости (Scope)")
|
||||
|
||||
utils.Println("Область видимости — часть кода, в которой объявление действует.")
|
||||
utils.Println("Это " + utils.ColorBold + "статическое" + utils.ColorReset + " свойство (на уровне компиляции), в отличие от времени жизни (runtime).")
|
||||
fmt.Println()
|
||||
utils.Code(`// Уровни области видимости (от широкого к узкому):
|
||||
// 1. Вселенная (universe) — встроенные имена: int, len, nil...
|
||||
// 2. Пакет — объявления на верхнем уровне файла
|
||||
// 3. Файл — только import-пути
|
||||
// 4. Локальный — блок { }, параметры функции, for, if, switch...
|
||||
|
||||
var x = "пакет" // область видимости — весь пакет
|
||||
|
||||
func f() {
|
||||
fmt.Println(x) // "пакет"
|
||||
x := "локальный" // новая переменная x (shadowing!)
|
||||
fmt.Println(x) // "локальный"
|
||||
{
|
||||
x := "вложенный"
|
||||
fmt.Println(x) // "вложенный"
|
||||
}
|
||||
fmt.Println(x) // "локальный"
|
||||
}`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Затенение (shadowing) в управляющих конструкциях:" + utils.ColorReset)
|
||||
utils.Code(`// if-инициализатор:
|
||||
if x := compute(); x > 0 {
|
||||
// x видна только здесь...
|
||||
} else {
|
||||
// ...и здесь
|
||||
}
|
||||
// x здесь уже не видна
|
||||
|
||||
// Распространённая ошибка с := в if:
|
||||
var err error
|
||||
if err := doA(); err != nil { // новая err!
|
||||
return err
|
||||
}
|
||||
// err (внешняя) всё ещё nil — doA не изменил её!
|
||||
fmt.Println(err) // nil`)
|
||||
|
||||
utils.Println(utils.ColorBold + "Экспортируемые имена — публичный API пакета:" + utils.ColorReset)
|
||||
utils.Code(`package geometry
|
||||
|
||||
// Экспортируется (заглавная буква):
|
||||
type Point struct {
|
||||
X, Y float64 // поля тоже экспортируются
|
||||
}
|
||||
|
||||
func Distance(p, q Point) float64 { ... }
|
||||
|
||||
// НЕ экспортируется (строчная буква):
|
||||
type circle struct { center Point; radius float64 }
|
||||
func area(c circle) float64 { ... }`)
|
||||
|
||||
utils.Note("Экспортируемость — единственный механизм инкапсуляции в Go. Нет private/protected/public ключевых слов.")
|
||||
utils.Note("Правило: экспортируй только то, что является частью публичного API. Всё остальное — строчными буквами.")
|
||||
|
||||
utils.Footer()
|
||||
}
|
||||
BIN
rubric
Executable file
BIN
rubric
Executable file
Binary file not shown.
76
utils/display.go
Normal file
76
utils/display.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Цвета для терминала
|
||||
const (
|
||||
ColorReset = "\033[0m"
|
||||
ColorBold = "\033[1m"
|
||||
ColorDim = "\033[2m"
|
||||
ColorCyan = "\033[36m"
|
||||
ColorGreen = "\033[32m"
|
||||
ColorYellow = "\033[33m"
|
||||
ColorBlue = "\033[34m"
|
||||
ColorMagenta = "\033[35m"
|
||||
ColorRed = "\033[31m"
|
||||
ColorWhite = "\033[97m"
|
||||
BgBlue = "\033[44m"
|
||||
BgGray = "\033[100m"
|
||||
)
|
||||
|
||||
func Header(title string) {
|
||||
width := 60
|
||||
line := strings.Repeat("─", width)
|
||||
padding := (width - len([]rune(title))) / 2
|
||||
padStr := strings.Repeat(" ", padding)
|
||||
fmt.Printf("\n%s%s┌%s┐%s\n", ColorBold, ColorCyan, line, ColorReset)
|
||||
fmt.Printf("%s%s│%s%s%s%s%s│%s\n",
|
||||
ColorBold, ColorCyan,
|
||||
ColorReset, ColorBold, ColorWhite, padStr+title, ColorReset+ColorBold+ColorCyan,
|
||||
ColorReset)
|
||||
fmt.Printf("%s%s└%s┘%s\n\n", ColorBold, ColorCyan, line, ColorReset)
|
||||
}
|
||||
|
||||
func Section(title string) {
|
||||
fmt.Printf("\n%s%s▶ %s%s\n", ColorBold, ColorYellow, title, ColorReset)
|
||||
fmt.Printf("%s%s%s\n", ColorDim, strings.Repeat("─", 50), ColorReset)
|
||||
}
|
||||
|
||||
func Code(code string) {
|
||||
lines := strings.Split(strings.TrimSpace(code), "\n")
|
||||
fmt.Printf("%s%s ┌─ код ────────────────────────────────────%s\n", ColorDim, ColorBold, ColorReset)
|
||||
for _, line := range lines {
|
||||
fmt.Printf("%s%s │%s %s%s%s\n", ColorDim, ColorBold, ColorReset, ColorGreen, line, ColorReset)
|
||||
}
|
||||
fmt.Printf("%s%s └───────────────────────────────────────────%s\n", ColorDim, ColorBold, ColorReset)
|
||||
}
|
||||
|
||||
func Command(cmd, description string) {
|
||||
fmt.Printf(" %s%-40s%s %s%s%s\n",
|
||||
ColorCyan+ColorBold, cmd, ColorReset,
|
||||
ColorDim, description, ColorReset)
|
||||
}
|
||||
|
||||
func Note(text string) {
|
||||
fmt.Printf("\n %s%s💡 %s%s\n", ColorBold, ColorYellow, text, ColorReset)
|
||||
}
|
||||
|
||||
func Info(label, value string) {
|
||||
fmt.Printf(" %s%s%-20s%s %s\n", ColorBold, ColorMagenta, label+":", ColorReset, value)
|
||||
}
|
||||
|
||||
func Println(text string) {
|
||||
fmt.Printf(" %s\n", text)
|
||||
}
|
||||
|
||||
func Separator() {
|
||||
fmt.Printf("\n%s%s%s\n\n", ColorDim, strings.Repeat("═", 60), ColorReset)
|
||||
}
|
||||
|
||||
func Footer() {
|
||||
fmt.Printf("\n%s%s%s%s\n\n", ColorDim, ColorCyan,
|
||||
strings.Repeat("─", 60), ColorReset)
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue