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
//! Reading `config.toml` and rejecting what it must not contain.
//!
//! Split out of `cli.rs` on 2026-09-04 under GAP-2026-074. The parent
//! module declares the command line; this one answers the separate
//! question of what a file on disk is allowed to say, and the two were
//! only in the same file because the file grew rather than because the
//! responsibilities are one.
use *;
/// Load CLI defaults from a TOML config file. The file is a flat
/// table whose keys mirror the long-form CLI flag names (without the
/// leading `--`). Unknown keys are rejected with a clear error
/// message so typos surface immediately. CLI flags always override
/// config values, so the precedence is CLI > config > built-in
/// default.
///
/// Supported keys: `url`, `lang`, `ui_lang`, `format`, `timeout`, `cache_ttl`,
/// `user_agent`. Booleans `verbose`, `quiet`, `json`, `batch`,
/// `no_cache`, `dry_run`, `no_progress`, `yes`. Optional `log_level`,
/// `log_format`, `color`.
///
/// # Errors
///
/// - [`crate::error::AppError::Config`] when the file is missing,
/// unreadable, or contains malformed TOML or an unknown key. This
/// maps to sysexits `EX_CONFIG = 78`, distinct from
/// `AppError::InvalidUsage` (exit 64) which is reserved for
/// post-parse CLI argument validation failures.
/// Reject every leaf under a tuning namespace that the registry does not
/// declare.
///
/// A tuning namespace is read lazily by [`crate::config`], so a
/// misspelled key such as `net.foo` used to be dropped in silence: no
/// error, no warning, and no reader. Checking the flattened tree here
/// turns that silence into a failure at load time, and it covers the
/// operator who edits `config.toml` by hand — a path `config set` never
/// sees.
///
/// The tree is flattened by [`crate::config::ConfigStore::flattened`],
/// the same routine that backs `config show`, so the dotted form
/// compared here is the one the registry stores. Membership is decided
/// by [`crate::config::spec`], never by a second copy of the registry.
///
/// # Errors
///
/// Returns [`crate::error::AppError::Config`] — sysexits
/// `EX_CONFIG = 78` — for the first leaf absent from
/// [`crate::config::KEYS`].
/// Re-file [`crate::config::unknown_key`] under
/// [`crate::error::AppError::Config`].
///
/// The wording, and its twelve translations, belongs to
/// [`crate::config`], which raises the condition as an
/// [`crate::error::AppError::InvalidUsage`] because its own callers are
/// command-line arguments. Reaching the same condition through the
/// configuration *file* is a configuration fault, so the variant changes
/// and the exit becomes 78 while the sentence stays the single one.
pub