rust-config-tree 0.1.9

Recursive include tree utilities for layered configuration files.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
# rust-config-tree

[English]README.md | [中文]README.zh.md | [日本語]README.ja.md | [한국어]README.ko.md | [Français]README.fr.md | [Deutsch]README.de.md | [Español]README.es.md | [Português]README.pt.md | [Svenska]README.sv.md | [Suomi]README.fi.md | [Nederlands]README.nl.md

`rust-config-tree` tillhandahaller laddning av konfigurationstrad och
CLI-hjalpare for Rust-program som anvander lagerindelade konfigurationsfiler.

Projektmanual: <https://developerworks.github.io/rust-config-tree/>. Manualer
publiceras som fristaende mdBook-webbplatser med spraklankar.

Det hanterar:

- laddning av ett `confique`-schema till ett direkt anvandbart konfigurationsobjekt via Figment-runtime providers
- kommandohanterare for `config-template`, `config-schema`,
  `config-validate`, `completions`, `install-completions` och
  `uninstall-completions`
- generering av Draft 7 JSON Schema for rot och sektioner for editor-komplettering och grundlaggande schemakontroller
- generering av konfigurationsmallar for YAML, TOML, JSON och JSON5
- schemabindningar for TOML-, YAML-, JSON- och JSON5-mallar
- rekursiv include-traversering
- laddning av `.env` innan miljo-varden slas samman
- kallspArning via Figment-metadata
- kallspArningsloggar pa TRACE-niva via `tracing`
- relativa include-sokvagar upplosta fran filen som deklarerar dem
- lexikal sokvagsnormalisering
- detektering av include-cykler
- deterministisk traverseringsordning
- speglad insamling av mallmal
- opt-in YAML-malluppdelning for sektioner markerade med `x-tree-split`

Program tillhandahaller sitt schema genom att derivera `confique::Config` och
implementera `ConfigSchema` for att exponera schemats include-falt.

## Installation

```toml
[dependencies]
rust-config-tree = "0.1"
confique = { version = "0.4", features = ["yaml", "toml", "json5"] }
figment = { version = "0.10", features = ["yaml", "toml", "json", "env"] }
schemars = { version = "1", features = ["derive"] }
serde = { version = "1", features = ["derive"] }
clap = { version = "4", features = ["derive"] }
```

## Konfigurationsschema

Ditt programschema ager include-faltet. `rust-config-tree` behover bara en
liten adapter som hamtar includes fran det mellanliggande `confique`-lagret.

```rust
use std::path::PathBuf;

use confique::Config;
use schemars::JsonSchema;
use rust_config_tree::ConfigSchema;

#[derive(Debug, Config, JsonSchema)]
struct AppConfig {
    #[config(default = [])]
    include: Vec<PathBuf>,

    #[config(default = "paper")]
    mode: String,

    #[config(nested)]
    #[schemars(extend("x-tree-split" = true))]
    server: ServerConfig,
}

#[derive(Debug, Config, JsonSchema)]
struct ServerConfig {
    #[config(default = 8080)]
    port: u16,
}

impl ConfigSchema for AppConfig {
    fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
        layer.include.clone().unwrap_or_default()
    }
}
```

Relativa include-sokvagar losas fran filen som deklarerar dem:

```yaml
# config.yaml
include:
  - config/server.yaml

mode: shadow
```

```yaml
# config/server.yaml
server:
  port: 7777
```

Ladda det slutliga schemat med `load_config`:

```rust
use rust_config_tree::load_config;

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let config = load_config::<AppConfig>("config.yaml")?;
    println!("{config:#?}");

    Ok(())
}
```

`load_config` laddar den forsta `.env`-filen som hittas genom att ga uppat fran
rotkonfigurationsfilens katalog innan Figment far lasa schemadeklarerade
miljovariabler. Varden som redan finns i processmiljon bevaras och har hogre
prioritet an `.env`-varden.

Runtime-laddning av konfiguration sker via Figment. `confique` ansvarar fortsatt
for schemametadata, standardvarden, validering och mallgenerering.
Miljovariabelnamn lases fran `#[config(env = "...")]`; laddaren anvander inte
`Env::split("_")` eller `Env::split("__")`, sa en variabel som
`APP_DATABASE_POOL_SIZE` kan mappa till ett falt med namnet
`database.pool_size`.

`load_config` laser inte kommandoradsargument eftersom CLI-flaggor ar
programspecifika. Lagg till CLI-override genom att sla samman en provider efter
`build_config_figment`, och validera sedan med `load_config_from_figment`:

CLI-flaggornas namn harleds inte fran konfigurationssokvagar. Anvand normala
programflaggor som `--server-port` eller `--database-url`; forlita dig inte pa
`--server.port` eller `a.b.c` om inte programmet avsiktligt implementerar en
sadan parser. Den nastlade serialiserade override-formen avgor vilken
konfigurationsnyckel som skrivs over.

Endast varden som representeras i programmets `CliOverrides`-provider kan
skriva over konfiguration. Detta ar avsett for runtime-parametrar som justeras
ofta, dar en flagga for en enskild korning ar battre an att redigera en
konfigurationsfil. Hall stabil konfiguration i filer och exponera bara
avsiktliga tillfalliga overrides som CLI-flaggor.

```rust
use figment::providers::Serialized;
use serde::Serialize;
use rust_config_tree::{build_config_figment, load_config_from_figment};

#[derive(Debug, Serialize)]
struct CliOverrides {
    #[serde(skip_serializing_if = "Option::is_none")]
    mode: Option<String>,
}

fn load_with_cli_overrides(cli_mode: Option<String>) -> Result<AppConfig, Box<dyn std::error::Error + Send + Sync>> {
    let cli_overrides = CliOverrides {
        mode: cli_mode,
    };

    let figment = build_config_figment::<AppConfig>("config.yaml")?
        .merge(Serialized::defaults(cli_overrides));

    let config = load_config_from_figment::<AppConfig>(&figment)?;
    Ok(config)
}
```

Med CLI-overrides sammanslagna pa detta satt ar runtime-prioriteten:

```text
command-line overrides
  > environment variables
    > config files
      > confique code defaults
```

Anvand `load_config_with_figment` nar anroparen behover kallmetadata:

```rust
use rust_config_tree::load_config_with_figment;

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let (config, figment) = load_config_with_figment::<AppConfig>("config.yaml")?;

    if let Some(metadata) = figment.find_metadata("mode") {
        let source = metadata.interpolate(&figment::Profile::Default, &["mode"]);
        println!("mode came from {source}");
    }

    println!("{config:#?}");

    Ok(())
}
```

Laddaren skickar ocksa kallspArning for konfiguration med `tracing::trace!`.
Dessa handelser produceras bara nar TRACE ar aktiverat i programmets
tracing-subscriber. Om tracing initieras efter konfigurationsladdning, anropa
`trace_config_sources::<AppConfig>(&figment)` efter att subscribern installerats.

## Mallgenerering

Mallar renderas med samma schema och include-traverseringsregler. Utdataformatet
harleds fran utdatasokvagen:

- `.yaml` och `.yml` genererar YAML
- `.toml` genererar TOML
- `.json` och `.json5` genererar JSON5-kompatibla mallar
- okanda eller saknade filandelse genererar YAML

Anvand `write_config_schemas` for att skapa Draft 7 JSON Schemas for
rotkonfigurationen och `x-tree-split`-markerade nastlade sektioner. De genererade schemana utelamnar
`required`-begransningar sa IDE:er kan erbjuda komplettering for partiella
konfigurationsfiler utan att rapportera saknade falt. Genererade
`*.schema.json`-filer ar bara for IDE-komplettering och grundlaggande
editor-kontroller; de avgor inte om ett konkret faltvarde ar giltigt for
programmet. Faltvardevalidering ska implementeras i kod med
`#[config(validate = Self::validate)]` och koras via `load_config` eller
`config-validate`:

```rust
use rust_config_tree::write_config_schemas;

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    write_config_schemas::<AppConfig>("schemas/myapp.schema.json")?;

    Ok(())
}
```

Mark a nested field with `#[schemars(extend("x-tree-split" = true))]` when it
should be generated as its own `*.yaml` template and
`<section>.schema.json` schema. Unmarked nested fields stay in the parent
template and parent schema.

Markera ett bladfalt med `#[schemars(extend("x-env-only" = true))]` nar vardet bara ska komma fran miljovariabler. Genererade mallar och JSON Schemas utelamnar env-only-falt, och foralderobjekt som blir tomma tas bort.

For ett schema med sektionerna `server` och `log` markerade med `x-tree-split` skriver detta
`schemas/myapp.schema.json`, `schemas/server.schema.json` och
`schemas/log.schema.json`. Rotschemat innehaller bara falt som hor hemma i
rotkonfigurationsfilen, som `include` och rotens skalara falt. Det utelamnar
avsiktligt delade sektionsproperties, sa `server` och `log` kompletteras bara
nar deras egna YAML-sektionsfiler redigeras.

Anvand `write_config_templates` for att skapa en rotmall och varje mallfil som
nas fran dess include-trad:

```rust
use rust_config_tree::write_config_templates;

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    write_config_templates::<AppConfig>("config.yaml", "config.example.yaml")?;

    Ok(())
}
```

Anvand `write_config_templates_with_schema` nar genererade TOML-, YAML-, JSON-
och JSON5-mallar ska binda dessa scheman for IDE-komplettering och
grundlaggande schemakontroller:

```rust
use rust_config_tree::write_config_templates_with_schema;

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    write_config_templates_with_schema::<AppConfig>(
        "config.toml",
        "config.example.toml",
        "schemas/myapp.schema.json",
    )?;

    Ok(())
}
```

Rotmal for TOML och YAML binder rotschemat och kompletterar inte delade
barnsektioners falt. Delade sektionsmal for YAML binder sitt motsvarande
sektionsschema, till exempel far `log.yaml`
`# yaml-language-server: $schema=./schemas/log.schema.json`. JSON- och
JSON5-mal far ett rotfalt `$schema` som VS Code kan kanna igen. VS Code
`json.schemas` ar fortfarande en alternativ bindningsvag.

Mallgenerering valjer kalltrad i denna ordning:

- en befintlig konfigurationssokvag
- en befintlig utdatakatalog eller mallutdatasokvag
- utdatasokvagen, behandlad som ett nytt tomt malltrad

Om en kallnod saknar include-lista harleder `rust-config-tree`
barnmallfiler fran nastlade `confique`-sektioner markerade med `x-tree-split`. Med schemat ovan producerar en
tom `config.example.yaml`-kalla:

```text
config.example.yaml
server.yaml
```

Rotmallen far ett include-block for `server.yaml`. YAML-mal som mappar
till en nastlad sektion, till exempel `server.yaml`, innehaller bara den
sektionen. Ytterligare nastlade sektioner delas rekursivt bara nar de falten ocksa bar `x-tree-split`.

Overstyr `template_path_for_section` nar en sektion ska genereras pa en annan
sokvag:

```rust
use std::path::PathBuf;

use confique::Config;
use rust_config_tree::ConfigSchema;

impl ConfigSchema for AppConfig {
    fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
        layer.include.clone().unwrap_or_default()
    }

    fn template_path_for_section(section_path: &[&str]) -> Option<PathBuf> {
        match section_path {
            ["server"] => Some(PathBuf::from("examples/server.yaml")),
            _ => None,
        }
    }
}
```

Standardvagen for sektioner ar `<section>.yaml` for nastlade sektioner
pa toppniva. Nastlade barn placeras under foraldrafilens stam, till exempel
`trading/risk.yaml`.

## CLI-integrering

Platta ut `ConfigCommand` i programmets befintliga clap-kommandoenum for att
lagga till:

- `config-template`
- `config-schema`
- `config-validate`
- `completions`
- `install-completions`
- `uninstall-completions`

Det konsumerande programmet behaller sin egen `Parser`-typ och sin egen
kommandoenum. `rust-config-tree` bidrar bara med ateranvandbara underkommandon:

1. Lagg till `#[command(subcommand)] command: Command` i programmets parser.
2. Lagg till `#[command(flatten)] Config(ConfigCommand)` i programmets
   `Subcommand`-enum.
3. Clap expanderar de utplattade varianterna till samma underkommandoniva som
   programmets egna kommandon.
4. Matcha den varianten och anropa `handle_config_command::<Cli, AppConfig>`.

Programspecifika override-flaggor for konfiguration stannar pa programmets egen
parser. Till exempel kan `--server-port` mappa till `server.port` genom att
bygga ett nastlat `CliOverrides { server: Some(CliServerOverrides { port }) }`
-varde och sla samman det med `Serialized::defaults`.

```rust
use std::path::PathBuf;

use clap::{Parser, Subcommand};
use confique::Config;
use schemars::JsonSchema;
use rust_config_tree::{ConfigCommand, ConfigSchema, handle_config_command, load_config};

#[derive(Debug, Config, JsonSchema)]
struct AppConfig {
    #[config(default = [])]
    include: Vec<PathBuf>,
    #[config(default = "paper")]
    mode: String,
}

impl ConfigSchema for AppConfig {
    fn include_paths(layer: &<Self as Config>::Layer) -> Vec<PathBuf> {
        layer.include.clone().unwrap_or_default()
    }
}

#[derive(Debug, Parser)]
#[command(name = "demo")]
struct Cli {
    #[arg(long, default_value = "config.yaml")]
    config: PathBuf,
    #[arg(long)]
    server_port: Option<u16>,
    #[command(subcommand)]
    command: Command,
}

#[derive(Debug, Subcommand)]
enum Command {
    Run,
    #[command(flatten)]
    Config(ConfigCommand),
}

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let cli = Cli::parse();
    match cli.command {
        Command::Run => {
            let config = load_config::<AppConfig>(&cli.config)?;
            println!("{config:#?}");
        }
        Command::Config(command) => {
            handle_config_command::<Cli, AppConfig>(command, &cli.config)?;
        }
    }

    Ok(())
}
```

`config-template --output <file-name>` skriver mallar under
`config/<root_config_name>/` med det valda filnamnet. Om en sokvag anges anvands
bara filnamnet. Om inget utdatafilnamn anges skriver det
`config/<root_config_name>/<root_config_name>.example.yaml`. Lagg till
`--schema <path>` for att binda TOML-, YAML-, JSON- och JSON5-mallar till en
genererad JSON Schema-uppsattning. JSON- och JSON5-mallar far ett `$schema`-falt
som VS Code kanner igen. Detta skriver ocksa rotschemat och sektionsscheman till
den valda schemasokvagen.

`config-schema --output <path>` skriver rotens Draft 7 JSON Schema och
sektionsscheman. Om ingen utdatasokvag anges skrivs rotschemat till
`config/<root_config_name>/<root_config_name>.schema.json`.

`config-validate` laddar hela runtime-konfigurationstradet och kor `confique`
standardvarden och validering, inklusive validatorer deklarerade med
`#[config(validate = Self::validate)]`. Anvand editorscheman for tyst
komplettering medan delade filer redigeras; anvand detta kommando for
obligatoriska falt och slutlig konfigurationsvalidering. Det skriver
`Configuration is ok` nar valideringen lyckas.

`completions <shell>` skriver kompletteringar till stdout.

`install-completions <shell>` skriver kompletteringar under anvandarens
hemkatalog och uppdaterar skalets startfil nar skalet kraver det. Bash, Elvish,
Fish, PowerShell och Zsh stods.

`uninstall-completions <shell>` tar bort den aktuella binarens completion-fil
och tar bort det hanterade shell-startblocket nar skalet anvander ett.

## Lagre niva: trad-API

Anvand `load_config_tree` nar du inte anvander `confique` eller nar du behover
direkt atkomst till traverseringsresultat:

```rust
use std::{fs, io, path::{Path, PathBuf}};

use rust_config_tree::{ConfigSource, load_config_tree};

fn load_source(path: &Path) -> io::Result<ConfigSource<String>> {
    let content = fs::read_to_string(path)?;
    let includes = content
        .lines()
        .filter_map(|line| line.strip_prefix("include: "))
        .map(PathBuf::from)
        .collect();

    Ok(ConfigSource::new(content, includes))
}

fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let tree = load_config_tree("config.yaml", load_source)?;

    for node in tree.nodes() {
        println!("{}", node.path().display());
    }

    Ok(())
}
```

Trad-API:t normaliserar sokvagar lexikalt, avvisar tomma include-sokvagar,
detekterar rekursiva include-cykler och hoppar over filer som redan laddats via
en annan include-gren.

## Licens

Licensierad enligt valfritt av:

- Apache License, Version 2.0
- MIT license

enligt ditt val.