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
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
//! Rich, layered configuration for Rust applications. Define a struct, point
//! at your files, and go.
//!
//! Clapfig discovers, merges, and manages configuration from multiple sources
//! — config files, environment variables, and programmatic overrides — through
//! a builder API. Built on [confique](https://docs.rs/confique) for
//! struct-driven defaults and template generation.
//!
//! ```ignore
//! let config: AppConfig = Clapfig::builder()
//! .app_name("myapp")
//! .load()?;
//! ```
//!
//! That single call searches the platform config directory for `myapp.toml`,
//! merges `MYAPP__*` environment variables, fills in `#[config(default)]`
//! values, and hands you a typed struct.
//!
//! # Why clapfig
//!
//! Most applications need layered configuration: compiled defaults, a config
//! file, environment variables, maybe CLI flags. The typical approach is to
//! wire each source by hand — parse TOML, iterate env vars, map CLI args —
//! and the plumbing grows with every new source or setting.
//!
//! Clapfig replaces that plumbing with a single struct. The struct defines
//! which keys exist, what their defaults are, and what their documentation
//! says. Every operation — loading, template generation, `config get`,
//! `config set` — derives from that one definition. Add a field to the struct
//! and the config file, env vars, template, and CLI subcommands all pick it
//! up automatically.
//!
//! # Design: struct as source of truth
//!
//! Your config struct (via the `Config` derive, re-exported from confique) is
//! the schema for everything:
//!
//! - **`#[config(default = ...)]`** provides compiled defaults — the lowest
//! layer, always present. Works with scalars (`default = 8080`), strings
//! (`default = "localhost"`), and collections (`default = {}` for an empty
//! map, `default = []` for an empty vec).
//! - **`///` doc comments** become the comments in generated templates and the
//! output of `config get`.
//! - **`#[config(nested)]`** models hierarchical config. Nesting maps to TOML
//! sections, dotted keys, and double-underscore env var separators.
//! - **`Option<T>` fields** are truly optional — omitting them in every source
//! is valid. Fields without `Option` and without a default must be provided
//! by at least one layer or loading fails.
//!
//! This means there is no separate schema file, no key registry, and no
//! chance of the template drifting from the code.
//!
//! # Core library — no CLI framework required
//!
//! The core of clapfig has **no dependency on any CLI framework**. Config
//! discovery, multi-file merging, environment variable mapping, key lookup,
//! persistence, and template generation all work through [`ClapfigBuilder`]
//! and [`ConfigAction`]. You can use clapfig in GUI apps, servers, embedded
//! tools, or with any argument parser.
//!
//! For [clap](https://docs.rs/clap) users, an optional adapter (the `cli`
//! module, behind the `clap` Cargo feature, on by default) provides drop-in
//! derive types that give your app `config gen|list|get|set|unset`
//! subcommands with zero boilerplate. To use clapfig without clap:
//!
//! ```toml
//! clapfig = { version = "...", default-features = false }
//! ```
//!
//! # Layer precedence
//!
//! ```text
//! Compiled defaults #[config(default = ...)]
//! ↑ overridden by
//! Config files search paths in order, later paths win
//! ↑ overridden by
//! Environment vars PREFIX__KEY
//! ↑ overridden by
//! URL query params .url_query() (requires "url" feature)
//! ↑ overridden by
//! Overrides .cli_override()
//! ```
//!
//! This is the default order. You can customize it with
//! [`layer_order()`](ClapfigBuilder::layer_order) — for example, to make
//! files override env vars, or to exclude a layer entirely:
//!
//! ```ignore
//! Clapfig::builder::<MyConfig>()
//! .app_name("myapp")
//! .layer_order(vec![Layer::Env, Layer::Files, Layer::Cli])
//! .load()?;
//! ```
//!
//! See [`Layer`] for the available variants.
//!
//! Every layer is **sparse**. You only specify the keys you want to override
//! in that layer; unset keys fall through to the layer below. This is a
//! deliberate design choice: config files don't need to be complete, env vars
//! can target a single key, and CLI flags only override what the user
//! explicitly passes.
//!
//! # Three axes of file handling
//!
//! Config file behavior is controlled by three independent settings on the
//! builder. They compose freely — changing one doesn't affect the others.
//!
//! ## Discovery — where to look
//!
//! [`search_paths()`](ClapfigBuilder::search_paths) accepts a list of
//! [`SearchPath`] variants in **priority-ascending** order (last = highest):
//!
//! - **`Platform`** — the OS config directory (XDG on Linux, `~/Library/
//! Application Support` on macOS). Good for user-level settings.
//! - **`Home(".myapp")`** — a dotfile directory under `$HOME`. Common for
//! tools that predate XDG or target cross-platform consistency.
//! - **`Cwd`** — the working directory. Natural for project-local config.
//! - **`Path(path)`** — an explicit directory. Useful for system-wide
//! defaults (`/etc/myapp/`) or test fixtures.
//! - **`Ancestors(boundary)`** — walks up from CWD, expanding into multiple
//! directories (shallowest first, CWD last = highest priority). This is
//! how tools like `.editorconfig` or `.eslintrc` work. The [`Boundary`]
//! controls how far to walk: `Root` goes to the filesystem root;
//! `Marker(".git")` stops at the repo boundary.
//!
//! Missing files are silently skipped — listing a search path is a
//! suggestion, not a requirement.
//!
//! ## Resolution — what to do with found files
//!
//! [`search_mode()`](ClapfigBuilder::search_mode) controls what happens
//! when multiple config files are found:
//!
//! - **[`Merge`](SearchMode::Merge)** (default) — deep-merge all files. Each
//! file is a sparse overlay; later files override earlier ones key-by-key.
//! Use this when configs are additive: a global file sets defaults, a
//! project file overrides a few keys.
//!
//! - **[`FirstMatch`](SearchMode::FirstMatch)** — use only the single
//! highest-priority file found. Use this when configs are self-contained
//! and should not be layered: a code formatter whose project config
//! replaces the user config entirely.
//!
//! The priority ordering is the same in both modes — switching between them
//! never requires reordering your search paths.
//!
//! ## Persistence — where to write
//!
//! [`persist_scope()`](ClapfigBuilder::persist_scope) names a target for
//! `config set`/`unset`. You can have multiple scopes (e.g. "local" and
//! "global") and the `--scope` flag selects which one to write to. The first
//! scope added is the default.
//!
//! Scope paths are automatically added to the search path list, so persisted
//! values are always discoverable during load. This means you don't need to
//! duplicate paths between `search_paths()` and `persist_scope()`.
//!
//! See the [`types`] module for common patterns: layered global + local,
//! fallback chains, nearest project config, per-directory layering.
//!
//! # Environment variables
//!
//! With env prefix `MYAPP`, variables map via double-underscore nesting:
//!
//! | Env var | Config key |
//! |---------|------------|
//! | `MYAPP__HOST` | `host` |
//! | `MYAPP__DATABASE__URL` | `database.url` |
//!
//! `__` (double underscore) separates nesting levels. Single `_` within a
//! segment is literal (part of the field name). Segments are lowercased to
//! match Rust field names.
//!
//! Values are parsed heuristically: `true`/`false` → bool, then integer,
//! then float, then string. This works well for the common case (ports,
//! flags, URLs). If you need exact control over how a value is interpreted,
//! use confique's `#[config(deserialize_with = ...)]` on the field.
//!
//! Disable env loading entirely with [`.no_env()`](ClapfigBuilder::no_env)
//! when you don't want environment variables in the mix (e.g. in tests or
//! embedded contexts).
//!
//! # URL query parameters
//!
//! *(Requires the `url` Cargo feature.)*
//!
//! For Rust-backed web applications, URL query parameters can serve as a
//! per-request config layer — by default sitting between environment variables
//! and CLI overrides in precedence (customizable via
//! [`layer_order()`](ClapfigBuilder::layer_order)). This is useful for WASM
//! frontends (Leptos, Dioxus, Yew) or server-side apps that accept config
//! overrides via the URL.
//!
//! ```ignore
//! let config: AppConfig = Clapfig::builder()
//! .app_name("myapp")
//! .url_query("port=9090&database.url=pg%3A%2F%2Fprod&debug=true")
//! .load()?;
//! ```
//!
//! Keys use `.` for nesting (same as CLI overrides). Values are
//! percent-decoded and parsed with the same heuristic as env vars. A leading
//! `?` is stripped if present.
//!
//! | Query param | Config key | Parsed value |
//! |---|---|---|
//! | `port=9090` | `port` | `9090` (integer) |
//! | `database.url=pg%3A%2F%2Fprod` | `database.url` | `"pg://prod"` (string) |
//! | `debug=true` | `debug` | `true` (bool) |
//!
//! Clapfig is framework-agnostic — it takes a raw query string, not a
//! framework-specific request object. Your app extracts the query string
//! however it likes (`window.location.search`, request headers, etc.) and
//! passes it in.
//!
//! Enable the feature in your `Cargo.toml`:
//!
//! ```toml
//! clapfig = { version = "...", features = ["url"] }
//! ```
//!
//! # Programmatic overrides
//!
//! The [`cli_override()`](ClapfigBuilder::cli_override) and
//! [`cli_overrides_from()`](ClapfigBuilder::cli_overrides_from) methods
//! inject values at the `Cli` layer (highest priority by default). Despite
//! the name, they are not clap-specific — use them with any value source
//! (GUI inputs, HTTP headers, hardcoded test values). Their position in the
//! merge order can be changed with
//! [`layer_order()`](ClapfigBuilder::layer_order).
//!
//! `cli_overrides_from(source)` auto-matches: it serializes the source,
//! skips `None` values, and keeps only keys that exist in the config struct.
//! This means you can pass your entire clap struct and non-config fields
//! (`command`, `verbose`, `output`) are silently ignored. For fields where
//! the CLI name differs from the config key (e.g. `--db-url` vs
//! `database.url`), use `cli_override("database.url", cli.db_url)`.
//!
//! Both methods push to the same override list and compose freely. Later
//! calls take precedence.
//!
//! # Strict mode
//!
//! Strict mode is **on by default**. When a config file contains a key that
//! doesn't match any field in your struct, loading fails with the file path,
//! key name, and line number:
//!
//! ```text
//! Unknown key 'typo_key' in /home/user/.config/myapp/myapp.toml (line 5)
//! ```
//!
//! This catches typos and stale keys early. Turn it off with
//! [`.strict(false)`](ClapfigBuilder::strict) if you intentionally share
//! config files across tools or want forward-compatible configs.
//!
//! ## Cascading strictness
//!
//! Real apps rarely want one uniform answer. Typed fields want strict
//! ("catch my typos"); plugin-extension subtrees want lenient ("pass
//! through unknown keys"); some leaves inside that lenient subtree may
//! want to re-tighten. Three composable knobs cover all of that:
//!
//! 1. **`.strict(bool)`** — the whole-resolution default (see above).
//! 2. **`.strict_at(path, bool)`** — per-section override at a dotted path.
//! Runtime schemas additionally set per-node strictness via
//! [`Schema::strict(bool)`](crate::runtime::Schema::strict).
//! 3. **`.on_unknown_key(callback)`** — last-word per-key callback that
//! runs *only* on cascade-strict keys.
//!
//! ### The cascade rule
//!
//! For any unknown key at dotted path `a.b.c`, the effective strictness
//! is the value of the **nearest ancestor schema node** (including the
//! key's parent) whose `strict` is explicitly set. With no ancestor
//! override, the builder-level default applies.
//!
//! That single rule produces both expected behaviors:
//!
//! - A parent's `strict` cascades to every descendant that doesn't itself
//! set `strict`.
//! - The first descendant that sets its own `strict` becomes the new root
//! for its subtree, overriding the inherited value below it.
//!
//! ```ignore
//! Clapfig::builder::<AppConfig>()
//! .strict(true) // typo protection by default
//! .strict_at("plugins", false) // plugins.* subtree: lenient
//! .strict_at("plugins.audit", true) // …but plugins.audit re-tightens
//! .load()?;
//! ```
//!
//! Targeting a leaf or an unknown path errors at
//! [`build_resolver()`](ClapfigBuilder::build_resolver) time with
//! [`ClapfigError::InvalidStrictPath`] — typo protection on the override
//! itself. With [`.normalize_keys(true)`](ClapfigBuilder::normalize_keys)
//! set, `path` may be written in kebab-case.
//!
//! ### The `on_unknown_key` callback
//!
//! Some shapes the cascade alone can't express: a struct level where
//! typed fields and a `#[serde(flatten)] BTreeMap` catch-all are
//! siblings. The cascade sees them as the same node — accept everything
//! or reject everything. The callback splits the difference: it runs
//! only on cascade-strict keys and decides each one. Returning
//! [`UnknownKeyDecision::Accept`] drops the key silently; `Reject`
//! produces a `ClapfigError::UnknownKeys` entry (the default).
//!
//! ```ignore
//! Clapfig::builder::<AppConfig>()
//! .strict(true)
//! .on_unknown_key(|c: &UnknownKeyContext<'_>| {
//! // Extension-emitted dotted keys (`"acme.task-due-date"`) are
//! // a single TOML literal; bare typos aren't. Accept the
//! // former, reject the latter.
//! if c.leaf.contains('.') {
//! UnknownKeyDecision::Accept
//! } else {
//! UnknownKeyDecision::Reject
//! }
//! })
//! .load()?;
//! ```
//!
//! The [`UnknownKeyContext`] carries the dotted path, the raw TOML leaf
//! key (preserves quoted-key semantics — `"acme.task-due-date-missing"`
//! stays as a single literal even though it contains dots), the parsed
//! value as `Option<&toml::Value>` (`None` in the rare case lookup can't
//! resolve — out-of-bounds array index, path through a non-table
//! intermediate), the source file, and the 1-indexed line number.
//!
//! ### Behavior compatibility note
//!
//! Pre-Phase-3 `.strict(false)` skipped validation entirely. Combining a
//! lenient default with at least one strict override
//! (`.strict(false).strict_at("X", true)`) now activates the validation
//! step, which can surface type errors that an unconditionally lenient
//! resolution would have masked. Plain `.strict(false)` with no
//! `strict_at(_, true)` is byte-identical to the old behavior.
//!
//! # Runtime-defined schemas
//!
//! Some apps don't have a single compile-time `Config` struct: plugin
//! hosts assemble their schema from loaded plugins, scripting hosts read
//! it from a config descriptor file, generated apps build it programmatically.
//! [`Clapfig::runtime`] is the entry point for those cases.
//!
//! ```ignore
//! use clapfig::{Clapfig, runtime::{Field, Schema}};
//!
//! let schema = Schema::object("App")
//! .doc("Demo runtime schema.")
//! .field("host", Field::string().doc("App host.").default("localhost"))
//! .field("port", Field::integer().default(8080i64))
//! .field(
//! "level",
//! Field::enum_of(["debug", "info", "warn", "error"])
//! .doc("Log verbosity.")
//! .default("info"),
//! )
//! .nested(
//! "db",
//! Schema::object("Db")
//! .field("url", Field::string().optional())
//! .field("pool_size", Field::integer().default(5i64)),
//! )
//! .build();
//!
//! let table: toml::Table = Clapfig::runtime(schema)
//! .app_name("myapp")
//! .load()?;
//! ```
//!
//! Same surface as [`Clapfig::builder`] — `app_name`, `search_paths`,
//! `env_prefix`, `cli_override`, `post_validate`, `build_resolver`,
//! `handle` (drives `config gen|list|get|set|unset|schema` the same as
//! the static path) — but the result is a [`toml::Table`] rather than a
//! typed `C`. `post_validate` receives `&Table`. A
//! [`RuntimeResolver`] parallels [`Resolver<C>`](Resolver) for tree-walk
//! use cases.
//!
//! `LeafType` covers TOML primitives + array + map, plus
//! `Enum { values }` for constrained value sets (log levels, output
//! formats, modes). The schema is consumed by every existing surface
//! with no extra wiring: `config gen` emits a commented template with
//! allowed-value lines for enum leaves; the JSON Schema emitter emits
//! `enum: [...]` on the property; `meta::doc_for_runtime` reads
//! doc-comment lines from the runtime schema the same way
//! [`meta::doc_for`] reads them from `C::META`.
//!
//! Cascading strictness composes naturally: runtime schemas can set
//! per-node strictness inline via [`Schema::strict(bool)`](crate::runtime::Schema::strict),
//! and [`RuntimeBuilder::strict_at`](RuntimeBuilder::strict_at) /
//! [`RuntimeBuilder::on_unknown_key`](RuntimeBuilder::on_unknown_key)
//! overlay on top.
//!
//! Schema field names are validated at construction: empty names, names
//! containing `.` / `[` / `]`, and duplicate names within one schema
//! panic at `SchemaBuilder` time rather than producing silent
//! `KeyNotFound` errors at every consumer.
//!
//! # Kebab-case keys
//!
//! By default, keys in config files and overrides must match the Rust field
//! name exactly (`pool_size`, not `pool-size`). Opt into kebab acceptance
//! with [`.normalize_keys(true)`](ClapfigBuilder::normalize_keys):
//!
//! ```ignore
//! Clapfig::builder::<MyConfig>()
//! .app_name("myapp")
//! .normalize_keys(true)
//! .load()?;
//! ```
//!
//! Every key crossing the boundary into clapfig — TOML keys in files, dotted
//! keys in [`.cli_override()`](ClapfigBuilder::cli_override) /
//! [`.cli_overrides_from()`](ClapfigBuilder::cli_overrides_from), URL query
//! parameter keys — has its `-` characters rewritten to `_` before
//! validation, merging, and deserialization. So `pool-size`, `pool_size`,
//! and mixed forms all resolve to the same `pool_size` field.
//!
//! Strict-mode validation still flags genuine unknown keys; the error
//! message reports the normalized form, but the line-number snippet still
//! points at the user's original line.
//!
//! Generated templates ([`ConfigAction::Gen`]) also follow the normalized
//! presentation: with `.normalize_keys(true)` on, `config gen` emits keys
//! and section headers in kebab-case (`pool-size`, `[my-section]`) so the
//! template matches what users will type. Doc comments and values are
//! never touched.
//!
//! Environment variables are unaffected — shells dislike `-` in variable
//! names, and the env layer already lower-cases segments and treats `__` as
//! the nesting separator.
//!
//! # Semantic validation — the `post_validate` hook
//!
//! Strict mode and confique together cover **structural** validation: every
//! key is known, every required field is present, every value has the right
//! type. They do not cover **semantic** constraints — the rules that depend
//! on the merged value rather than a single field's type:
//!
//! - numeric ranges (`port >= 1024`, `quality <= 100`, `pool_size > 0`)
//! - cross-field invariants (`if tls_enabled then tls_cert_path must be set`)
//! - enum combinations (`mode == "fast" requires buffer_size < 64k`)
//! - filesystem preconditions (`output_dir must exist and be writable`)
//! - anything that needs the final, fully-merged `&C` to decide
//!
//! Write them once, in a closure, and register it on the builder:
//!
//! ```ignore
//! let config: AppConfig = Clapfig::builder()
//! .app_name("myapp")
//! .post_validate(|c| {
//! if c.port < 1024 {
//! return Err(format!("port {} is below 1024", c.port));
//! }
//! if c.tls_enabled && c.tls_cert_path.is_none() {
//! return Err("tls_enabled requires tls_cert_path".into());
//! }
//! Ok(())
//! })
//! .load()?;
//! ```
//!
//! The hook runs after all layers have been merged and confique has
//! type-validated the result, but before [`load()`](ClapfigBuilder::load)
//! returns. Rejections become
//! [`ClapfigError::PostValidationFailed`],
//! which renders with the same error pipeline as every other clapfig error.
//!
//! Design notes:
//!
//! - **Signature is `Fn(&C) -> Result<(), String>`.** String keeps the API
//! tiny. Callers with richer error types call `.map_err(|e| e.to_string())`.
//! - **Upstream failures short-circuit.** Parse errors, strict-mode
//! violations, and type errors all fire before the hook, so the hook only
//! ever sees a fully-valid `&C`.
//! - **One hook per builder.** Calling `.post_validate()` twice replaces the
//! previous hook — compose multiple checks inside one closure.
//! - **The hook is captured by value and fires on every resolution.** If you
//! build a [`Resolver<C>`](Resolver) for tree-walk use cases (see the next
//! section), the same hook runs on every [`resolve_at()`](Resolver::resolve_at)
//! call.
//!
//! # Tree-walk resolution — the `Resolver<C>` handle
//!
//! [`load()`](ClapfigBuilder::load) assumes one resolution per process,
//! anchored at `std::env::current_dir()`. For one-shot CLI tools that's
//! exactly right. But for tools that walk a **dynamic file tree** where every
//! directory can have an optional config — the `.htaccess` / `.gitignore` /
//! `.editorconfig` / `.eslintrc` pattern — you need N resolutions from N
//! different anchors, and you want to amortize the I/O cost across calls.
//!
//! [`ClapfigBuilder::build_resolver()`] gives you a reusable
//! [`Resolver<C>`](Resolver) handle for that case:
//!
//! ```ignore
//! let resolver = Clapfig::builder::<MyConfig>()
//! .app_name("myapp")
//! .search_paths(vec![SearchPath::Ancestors(Boundary::Marker(".git"))])
//! .post_validate(|c| validate_ranges(c))
//! .build_resolver()?;
//!
//! for leaf in walk_content_tree("./site") {
//! let cfg = resolver.resolve_at(&leaf)?;
//! render_page(&leaf, &cfg);
//! }
//! ```
//!
//! Key properties:
//!
//! - **`SearchPath::Cwd` and `SearchPath::Ancestors` are anchored at the
//! directory passed to [`resolve_at()`](Resolver::resolve_at)**, not at the
//! process CWD. Each call is a fully independent resolution with the same
//! builder state but a different starting point. Walking a site tree with
//! `Ancestors(Boundary::Marker(".git"))` gives you nearest-project-config
//! semantics on every leaf for free.
//! - **Files are cached by absolute path inside the resolver.** A tree walk
//! that visits 1000 leaves sharing 5 ancestor config files pays the disk +
//! parse cost once per unique file, not 1000×. The `Resolver` is the cache
//! scope — drop it to invalidate everything.
//! - **No mtime checking.** The cache is not invalidated when files change on
//! disk. Long-lived processes that need freshness should build a new
//! `Resolver`. This is a deliberate "keep v1 simple" choice; the contract
//! is documented and a regression test locks it in.
//! - **`load()` is the special case.** Internally `load()` is just
//! `self.build_resolver()?.resolve_at(std::env::current_dir()?)`, so
//! existing single-shot callers get zero behavior change and all resolution
//! flows through one code path.
//! - **The `post_validate` hook composes naturally.** Registered once on the
//! builder, captured into the resolver, fired on every `resolve_at` call —
//! so per-leaf invariants get the same enforcement as top-level `load()`.
//!
//! See [`Resolver`] for the full API.
//!
//! # Normalizing values
//!
//! Use confique's `#[config(deserialize_with = ...)]` to normalize values
//! during deserialization. The function runs automatically when a value is
//! loaded from any source — config files, env vars, or overrides. This is
//! useful for case-insensitive fields, path canonicalization, or unit
//! conversion.
//!
//! Note that `#[config(default)]` values are injected directly by confique
//! and **do not** pass through the deserializer — write defaults in their
//! already-normalized form.
//!
//! # Template generation
//!
//! `config gen` (or [`ConfigAction::Gen`]) produces a commented TOML file
//! derived from the struct's `///` doc comments and `#[config(default)]`
//! values. The template stays in sync with code — change a doc comment or a
//! default, the template reflects it. When `config set` creates a new file,
//! it seeds it from this template so the user gets a documented starting
//! point.
//!
//! # Metadata accessors
//!
//! Tools that build help text, tooltips, settings UIs, or `--describe`
//! flags can read a field's doc-comment lines directly from the static
//! `Config::META` tree — no need to spin up the full resolve pipeline. See
//! [`meta::doc_for`]:
//!
//! ```ignore
//! let lines = clapfig::meta::doc_for::<AppConfig>("database.pool-size")
//! .unwrap_or_default();
//! ```
//!
//! Key spelling is lenient (kebab and snake are interchangeable), and the
//! function returns `None` for keys that don't exist so callers can
//! distinguish "no such field" from "field exists, undocumented."
//!
//! # Clap adapter
//!
//! The `cli` module (behind the `clap` feature) offers two integration
//! paths:
//!
//! - **[`ConfigArgs`]** — a clap derive struct you embed in your
//! `#[derive(Subcommand)]` enum. Fastest path: one line to add, one call
//! to [`into_action()`](ConfigArgs::into_action) to bridge to the core.
//!
//! - **[`ConfigCommand`]** — a runtime builder for apps that need to rename
//! subcommands or flags (e.g. if your app already has a `--scope` flag).
//! Produces the same [`ConfigAction`], so all downstream logic is shared.
//!
//! Both paths give your users `config gen|list|get|set|unset` with `--scope`
//! support. Pick `ConfigArgs` for simplicity; reach for `ConfigCommand` only
//! when you hit naming conflicts.
//!
//! # Persistence
//!
//! `config set` and `config unset` write to config files through named
//! persist scopes. Key design decisions:
//!
//! - **Comment preservation**: edits use `toml_edit`, so existing comments
//! and formatting are preserved. Users won't lose their annotations.
//! - **Seeded files**: if the target file doesn't exist, a new one is created
//! from the generated template, so the user gets doc comments for every
//! field out of the box.
//! - **Validation before write**: `config set` validates that the key exists
//! and the value is type-compatible before touching the file. A typo in the
//! key name or a string where an integer is expected fails fast.
//! - **Scoped reads**: `config list --scope global` and `config get key
//! --scope local` read from a single scope's file rather than the merged
//! view, letting users inspect where values come from.
//!
//! # Error handling
//!
//! All fallible operations return [`ClapfigError`]. Errors are designed to
//! be user-facing: unknown keys include file paths and line numbers, unknown
//! scopes list the available ones, and missing prerequisites reference the
//! builder method to call. See the [`error`] module for the full set.
pub
pub use ;
pub use Schema;
pub use ;
pub use Config;
pub use ;
pub use ConfigResult;
pub use Resolver;
pub use ;
pub use SchemaConfigBuilder;
pub use Schema;
pub use ;
pub use ;