teaql_tool_core/audit.rs
1use std::collections::HashMap;
2
3/// Identifies a specific tool module in the TeaQL ecosystem.
4/// Used as the key for per-module audit configuration.
5/// Application layer can only reference these predefined modules.
6#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7#[non_exhaustive]
8pub enum Module {
9 // IO modules (default: Full audit)
10 Http,
11 File,
12 Cmd,
13 Email,
14 Kv,
15
16 // Security modules (default: Summary audit)
17 Crypto,
18 Jwt,
19
20 // Computation modules (default: Silent)
21 Time,
22 Id,
23 Text,
24 Decimal,
25 Money,
26 Json,
27 Regex,
28 Codec,
29 List,
30 Map,
31 Diff,
32 Url,
33 Validate,
34 Color,
35 Unit,
36 DateRange,
37 Desensitize,
38 Filter,
39 Tree,
40 System,
41}
42
43/// Controls how much detail is captured for a module's operations.
44/// Application layer picks from this closed set — no custom implementations allowed.
45#[derive(Debug, Clone, Copy, PartialEq, Eq)]
46#[non_exhaustive]
47pub enum AuditLevel {
48 /// No output at all. Maximum performance.
49 Silent,
50
51 /// Operation type + elapsed time only.
52 /// Example: `[HTTP GET] 127ms OK`
53 Summary,
54
55 /// Operation type + comment + target + elapsed + trace ID.
56 /// Example: `[HTTP GET] Intent: [Sync tasks] URL: https://... 127ms OK Trace: abc-123`
57 Full,
58
59 /// Full detail + captured request/response payload (truncated).
60 /// For debugging and compliance-critical environments.
61 FullWithPayload {
62 max_bytes: usize,
63 },
64}
65
66/// Per-module audit configuration.
67/// Application layer can only select from predefined presets or
68/// set individual module levels. No raw logging API is exposed.
69#[derive(Debug, Clone)]
70pub struct AuditConfig {
71 levels: HashMap<Module, AuditLevel>,
72 default_io_level: AuditLevel,
73 default_compute_level: AuditLevel,
74}
75
76impl AuditConfig {
77 /// Create a new AuditConfig with explicit default levels.
78 pub fn new(io_level: AuditLevel, compute_level: AuditLevel) -> Self {
79 Self {
80 levels: HashMap::new(),
81 default_io_level: io_level,
82 default_compute_level: compute_level,
83 }
84 }
85
86 // ─── Presets ───────────────────────────────────────────────
87
88 /// Everything silent. Use in CI/test environments or when
89 /// you want zero audit overhead.
90 pub fn silent_all() -> Self {
91 Self {
92 levels: HashMap::new(),
93 default_io_level: AuditLevel::Silent,
94 default_compute_level: AuditLevel::Silent,
95 }
96 }
97
98 /// Production defaults: IO = Full, Security = Summary, Compute = Silent.
99 pub fn production() -> Self {
100 let mut cfg = Self {
101 levels: HashMap::new(),
102 default_io_level: AuditLevel::Full,
103 default_compute_level: AuditLevel::Silent,
104 };
105 cfg.levels.insert(Module::Crypto, AuditLevel::Summary);
106 cfg.levels.insert(Module::Jwt, AuditLevel::Summary);
107 cfg
108 }
109
110 /// Focus on a single module with Full audit, silence everything else.
111 /// Ideal for development: only see what you care about.
112 pub fn focus_on(module: Module) -> Self {
113 let mut cfg = Self::silent_all();
114 cfg.levels.insert(module, AuditLevel::Full);
115 cfg
116 }
117
118 /// Focus on multiple modules, silence everything else.
119 pub fn focus_on_many(modules: &[Module]) -> Self {
120 let mut cfg = Self::silent_all();
121 for m in modules {
122 cfg.levels.insert(*m, AuditLevel::Full);
123 }
124 cfg
125 }
126
127 /// All IO modules at Full, all compute modules Silent.
128 pub fn io_only() -> Self {
129 Self {
130 levels: HashMap::new(),
131 default_io_level: AuditLevel::Full,
132 default_compute_level: AuditLevel::Silent,
133 }
134 }
135
136 /// Everything at Full. Use for deep debugging sessions.
137 pub fn verbose_all() -> Self {
138 Self {
139 levels: HashMap::new(),
140 default_io_level: AuditLevel::Full,
141 default_compute_level: AuditLevel::Full,
142 }
143 }
144
145 // ─── Per-module overrides ──────────────────────────────────
146
147 /// Set a specific module's audit level. Returns self for chaining.
148 pub fn enable(mut self, module: Module, level: AuditLevel) -> Self {
149 self.levels.insert(module, level);
150 self
151 }
152
153 /// Silence a specific module. Returns self for chaining.
154 pub fn silence(mut self, module: Module) -> Self {
155 self.levels.insert(module, AuditLevel::Silent);
156 self
157 }
158
159 // ─── Runtime query (used by framework infra layer only) ───
160
161 /// Returns the effective audit level for a given module.
162 /// Framework infrastructure calls this internally to decide
163 /// whether to log. Application code never calls this.
164 pub fn level_for(&self, module: Module) -> AuditLevel {
165 if let Some(&level) = self.levels.get(&module) {
166 return level;
167 }
168 if Self::is_io_module(module) {
169 self.default_io_level
170 } else {
171 self.default_compute_level
172 }
173 }
174
175 /// Returns true if the given module should produce any output.
176 pub fn is_active(&self, module: Module) -> bool {
177 self.level_for(module) != AuditLevel::Silent
178 }
179
180 fn is_io_module(module: Module) -> bool {
181 matches!(
182 module,
183 Module::Http
184 | Module::File
185 | Module::Cmd
186 | Module::Email
187 | Module::Kv
188 | Module::Crypto
189 | Module::Jwt
190 )
191 }
192}
193
194impl Default for AuditConfig {
195 /// Sensible defaults: IO at Full, Compute at Silent.
196 fn default() -> Self {
197 Self::production()
198 }
199}