Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
38bc189
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
1d29269
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
2b29108
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
2fde3c8
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
637e51b
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
866388e
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
f4abedd
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
f37eeee
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
5915617
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
198f105
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
c8dc432
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
ebc8e77
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
c6f86ea
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
ee5833e
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
45202ab
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
9e556e3
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
4bdc0f9
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
b54021d
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
bbe5173
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
fae739b
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
81a4db4
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
90103d5
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
466ffa6
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
d5fd90e
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
a319b43
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
f41dd23
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
d75200a
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
ca28fa2
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
19ef734
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
164554e
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
b71c38f
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
45500b4
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
fa4f8cb
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
b0f427c
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
e7d7412
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
6792802
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
b3af866
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
8add5b5
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
8d01210
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
215d722
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
6c5771f
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
90979ba
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
e19f919
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
ca00f65
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
de78289
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
8fe7fd0
add/new-mods [v0.1.2]
SUNsung Mar 28, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 6 additions & 5 deletions .github/actions/tidy-all-and-generate/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ description: 'Initialize all the modules and generate files'
runs:
using: 'composite'
steps:
- name: 📥 go mod tidy-all
shell: bash
run: ./_run/scripts/go_tidy_all.sh

- name: 🛠️ go generate
shell: bash
run: |
set -Eeuo pipefail
mkdir -p target tmp
GOWORK=off GOFLAGS=-mod=mod go generate ./...
go work sync
go generate .

- name: 📥 go mod tidy-all
shell: bash
run: ./_run/scripts/go_tidy_all.sh
5 changes: 3 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -156,9 +156,10 @@ jobs:
shell: bash
run: |
set -Eeuo pipefail
./_run/scripts/go_tidy_all.sh
mkdir -p target tmp
GOWORK=off GOFLAGS=-mod=mod go generate ./...
go work sync
go generate .
./_run/scripts/go_tidy_all.sh

- uses: ./.github/actions/generate-build-env

Expand Down
106 changes: 106 additions & 0 deletions README.RU.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Go-библиотека для встраивания узла Yggdrasil в пр
- **Resolver** (`mod/resolver`) — резолвер `.pk.ygg`-адресов
- **Forward** (`mod/forward`) — TCP/UDP-форвардинг
- **Traceroute** (`mod/traceroute`) — исследование топологии сети и трассировка маршрутов
- **Settings** (`mod/settings`) — кодогенерированные CLI-флаги и мультиформатные конфиг-файлы из YAML-схемы

### Примеры

Expand All @@ -62,6 +63,7 @@ Go-библиотека для встраивания узла Yggdrasil в пр
- [Структура модуля](#структура-модуля)
- [Пакеты](#пакеты)
- [traceroute](#traceroute)
- [settings](#settings)
- [Конфигурация](#конфигурация)
- [Примеры использования](#примеры-использования)
- [Snapshot](#snapshot)
Expand Down Expand Up @@ -297,6 +299,11 @@ CACHE[peerCacheObj — кеш с TTL]
POOL[workerPoolObj — пул BFS]
end

subgraph "settings"
SINIT[Init — bootstrap флагов и конфига]
SFILE[ParseFile / SaveFile — мультиформатный I/O]
end

A -->|встраивает|E
A -->|использует|J
A -->|создаёт|I
Expand Down Expand Up @@ -593,6 +600,105 @@ Cleanup-горутина запускается каждые `CacheTTL / 2`, у

Все ошибки проверяются через `errors.Is()`.

### `settings`

Конвейер настроек на основе кодогенерации. YAML-схема (`settings.yml`) — единственный источник
истины: генератор (`_generate/settings`) создаёт Go-структуры, типизированные enum'ы, значения
по умолчанию, регистрацию CLI-флагов, парсинг конфиг-файлов, логику сохранения и форматированный
help-текст. Рукописный пакет `mod/settings` связывает сгенерированный код с точкой входа приложения.

#### Как это работает

```mermaid
flowchart LR
YAML[settings.yml] -->|go generate| GEN[_generate/settings]
GEN -->|записывает| TARGET[target/settings/]
TARGET --> INIT["Init(): defaults → конфиг-файл → CLI-флаги"]
INIT --> APP[Приложение получает типизированный Obj]
```

**Трёхслойное разрешение** — каждый слой перекрывает предыдущий:

1. **Defaults** — зашиты в `NewDefault()` на этапе генерации.
2. **Конфиг-файл** — JSON, YAML или HJSON, загружается через `ParseFile()`. Формат определяется по расширению.
3. **CLI-флаги** — `flag.FlagSet`, зарегистрированный через `DefineFlags()`. Всегда побеждает.

#### Плюсы подхода

- Единый источник истины — одно изменение в `settings.yml` обновляет всё
- Типизированные enum'ы вместо строк
- Без рефлексии — прямой доступ к полям

#### Форматы конфиг-файлов

| Расширение | Формат |
|--------------------|--------|
| `.json` | JSON |
| `.yml` / `.yaml` | YAML |
| `.hjson` / `.conf` | HJSON |

`SaveFile()` сохраняет текущие настройки обратно в том же формате, определённом по расширению.

#### Триггеры

Поля с `trigger: true` в схеме становятся CLI-only булевыми флагами. Они никогда не читаются из
конфиг-файлов и не записываются в них — полезно для одноразовых действий вроде `--gen_private_key`.

#### Точка входа

```go
msettings.New(func(cfg msettings.Interface) error {
obj := msettings.Obj(cfg)
// obj — полностью разрешённый *target/settings.Obj
// готов к использованию с типизированными полями и enum'ами
return nil
})
```

`New()` вызывает сгенерированный `Init()`, обрабатывает `--help` / `--info` и передаёт
разрешённый объект настроек в колбек приложения. Возвращает `nil` при help/info (нормальный выход),
ошибку при сбое инициализации.

#### Цепочка конфигов

`ParseFile()` поддерживает редиректы между конфиг-файлами. Если файл содержит поле `config`,
указывающее на другой файл, парсер следует по цепочке до терминального файла
(без поля `config`). Поля промежуточных файлов игнорируются — парсится только терминальный.

- Относительные пути разрешаются от директории ссылающегося файла
- Обнаружение циклов через visited set
- Жёсткий лимит: 32 перехода

```mermaid
flowchart LR
A[a.yml<br/>config: b.yml] -->|редирект| B[b.yml<br/>config: c.json]
B -->|редирект| C[c.json<br/>без поля config]
C -->|парсинг| OBJ[Obj]
```

#### Нормализация duration

Поля с типом `duration` в схеме перечислены в сгенерированной карте `DurationKeys`.
Все три парсера (JSON, YAML, HJSON) нормализуют значения duration перед десериализацией в структуру:

1. Декодирование данных в `map[string]any`
2. Обход карты, конвертация известных duration-путей в наносекунды (`int64`)
3. Повторная сериализация в JSON и десериализация в типизированный `Obj`

Это позволяет использовать человекочитаемые строки (`"5s"`, `"100ms"`) в любом формате,
при этом структура всегда получает `time.Duration` (наносекунды).

#### Режимы сохранения

| Функция | Порядок полей | Комментарии | Типобезопасность |
|--------------------|---------------|-------------|----------------------|
| `SaveFile` | кодировщик | нет | типизированный `Obj` |
| `SaveFilePretty` | по схеме | да | типизированный `Obj` |
| `SaveUnsafePretty` | по схеме | да | `any` |

Все функции сохранения удаляют ключ `config` из вывода для предотвращения петель редиректов.
Комментарии и порядок полей берутся из сгенерированных карт `Comments` и `FieldOrder`.

## Конфигурация

### ConfigObj (ratatoskr)
Expand Down
106 changes: 106 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ a node is created with `ratatoskr.New()`, everything is controlled through the G
- **Resolver** (`mod/resolver`) — `.pk.ygg` address resolver
- **Forward** (`mod/forward`) — TCP/UDP forwarding
- **Traceroute** (`mod/traceroute`) — network topology exploration and path tracing
- **Settings** (`mod/settings`) — code-generated CLI flags and multi-format config files from a YAML schema

### Examples

Expand All @@ -62,6 +63,7 @@ Also [`cmd/yggstack/`](cmd/yggstack/) — yggstack implementation built on ratat
- [Module structure](#module-structure)
- [Packages](#packages)
- [traceroute](#traceroute)
- [settings](#settings)
- [Configuration](#configuration)
- [Usage examples](#usage-examples)
- [Snapshot](#snapshot)
Expand Down Expand Up @@ -297,6 +299,11 @@ CACHE[peerCacheObj — TTL cache]
POOL[workerPoolObj — BFS pool]
end

subgraph "settings"
SINIT[Init — flag & config bootstrap]
SFILE[ParseFile / SaveFile — multi-format I/O]
end

A -->|embeds|E
A -->|uses|J
A -->|creates|I
Expand Down Expand Up @@ -590,6 +597,105 @@ These are package-level `var` — change them before calling `New()`.

All errors can be checked with `errors.Is()`.

### `settings`

Code-generated settings pipeline. A YAML schema (`settings.yml`) is the single source of truth —
the generator (`_generate/settings`) produces Go structs, typed enums, default values, CLI flag
definitions, config file parsing, save logic, and formatted help text. The handwritten
`mod/settings` package wires the generated code into the application entry point.

#### How it works

```mermaid
flowchart LR
YAML[settings.yml] -->|go generate| GEN[_generate/settings]
GEN -->|writes| TARGET[target/settings/]
TARGET --> INIT["Init(): defaults → config file → CLI flags"]
INIT --> APP[Application receives typed Obj]
```

**Three-layer resolution** — each layer overrides the previous one:

1. **Defaults** — baked into `NewDefault()` at generation time.
2. **Config file** — JSON, YAML, or HJSON loaded by `ParseFile()`. Format is detected by extension.
3. **CLI flags** — `flag.FlagSet` registered by `DefineFlags()`. Always wins.

#### Advantages

- Single source of truth — one change in `settings.yml` updates everything
- Typed enums instead of raw strings
- No reflection — direct struct field access

#### Config file formats

| Extension | Format |
|--------------------|--------|
| `.json` | JSON |
| `.yml` / `.yaml` | YAML |
| `.hjson` / `.conf` | HJSON |

`SaveFile()` writes the current settings back in the same format, auto-detected by extension.

#### Triggers

Fields marked `trigger: true` in the schema become CLI-only boolean flags. They are never
read from or written to config files — useful for one-shot actions like `--gen_private_key`.

#### Entry point

```go
msettings.New(func (cfg msettings.Interface) error {
obj := msettings.Obj(cfg)
// obj is a fully resolved *target/settings.Obj
// ready to use with typed fields and enums
return nil
})
```

`New()` calls the generated `Init()`, handles `--help` / `--info`, and passes the resolved
settings object to the application callback. Returns `nil` on help/info (normal exit),
error on init failure.

#### Config chain resolution

`ParseFile()` supports config-to-config redirects. If a config file contains a `config` field
pointing to another file, the parser follows the chain until it reaches a terminal file
(one without `config`). Intermediate files' fields are ignored — only the terminal file is parsed.

- Relative paths resolve from the directory of the referring file
- Cycle detection via visited set
- Hard limit: 32 hops

```mermaid
flowchart LR
A[a.yml<br/>config: b.yml] -->|redirect| B[b.yml<br/>config: c.json]
B -->|redirect| C[c.json<br/>no config field]
C -->|parse| OBJ[Obj]
```

#### Duration normalization

Fields marked as `duration` in the schema are listed in the generated `DurationKeys` map.
All three parsers (JSON, YAML, HJSON) normalize duration values before unmarshalling into the struct:

1. Decode raw data into `map[string]any`
2. Walk the map, converting known duration paths to nanosecond `int64`
3. Re-encode as JSON and unmarshal into the typed `Obj`

This allows config files to use human-readable strings (`"5s"`, `"100ms"`) in any format,
while the struct always receives `time.Duration` (nanoseconds).

#### Save modes

| Function | Field order | Comments | Type safety |
|--------------------|-------------|----------|-------------|
| `SaveFile` | encoder | no | typed `Obj` |
| `SaveFilePretty` | schema | yes | typed `Obj` |
| `SaveUnsafePretty` | schema | yes | `any` |

All save functions strip the `config` key from output to prevent redirect loops.
Comments and field order come from the generated `Comments` and `FieldOrder` maps.

## Configuration

### ConfigObj (ratatoskr)
Expand Down
3 changes: 3 additions & 0 deletions _generate/dependencies/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
module gen_dependencies

go 1.24
Loading
Loading