Skip to main content

qualia_core_db/modalities/logic/shacl_extensions/
config.rs

1//! SHACL Extensions for Logging, System Tray, and Enhanced Settings
2//!
3//! This module extends the core SHACL compiler with constraints for the new
4//! client-side functionality including comprehensive logging, system tray
5//! integration, and enhanced configuration settings.
6
7use crate::webizen::SlgOpcode;
8
9// ── Logging System Constraints ─────────────────────────────────────────────
10
11/// `q42:LogConfiguration` — validates logging system configuration
12#[derive(Debug, Clone)]
13pub struct LogConfiguration {
14    pub max_memory_logs: u32,
15    pub max_disk_logs: u32,
16    pub flush_interval_ms: u32,
17}
18
19/// `q42:LogLevel` — validates log level enumeration
20#[derive(Debug, Clone)]
21pub struct LogLevel {
22    pub allowed_levels: Vec<String>, // ["DEBUG", "INFO", "WARN", "ERROR", "CRITICAL"]
23}
24
25/// `q42:LogEntry` — validates individual log entry structure
26#[derive(Debug, Clone)]
27pub struct LogEntry {
28    pub require_timestamp: bool,
29    pub require_context: bool,
30    pub max_message_length: u32,
31    pub allow_data_payload: bool,
32}
33
34/// `q42:LogRetention` — validates log retention policies
35#[derive(Debug, Clone)]
36pub struct LogRetention {
37    pub max_age_days: u32,
38    pub max_size_mb: u32,
39    pub backup_before_cleanup: bool,
40}
41
42/// `q42:LogExportFormat` — validates log export format constraints
43#[derive(Debug, Clone)]
44pub struct LogExportFormat {
45    pub allowed_formats: Vec<String>, // ["json", "txt", "csv"]
46    pub max_export_size_mb: u32,
47}
48
49// ── System Tray Constraints ─────────────────────────────────────────────────
50
51/// `q42:SystemTrayConfiguration` — validates system tray menu configuration
52#[derive(Debug, Clone)]
53pub struct SystemTrayConfiguration {
54    pub max_menu_items: u8,
55    pub require_separator_logic: bool,
56    pub allow_nested_menus: bool,
57}
58
59/// `q42:TrayMenuItem` — validates individual system tray menu items
60#[derive(Debug, Clone)]
61pub struct TrayMenuItem {
62    pub require_label: bool,
63    pub require_action: bool,
64    pub max_label_length: u32,
65    pub allow_icons: bool,
66}
67
68/// `q42:TrayStatusIndicator` — validates system tray status indicators
69#[derive(Debug, Clone)]
70pub struct TrayStatusIndicator {
71    pub allow_live_updates: bool,
72    pub update_interval_ms: u32,
73    pub max_status_length: u32,
74}
75
76/// `q42:TrayAction` — validates system tray action handlers
77#[derive(Debug, Clone)]
78pub struct TrayAction {
79    pub require_window_context: bool,
80    pub allow_async_actions: bool,
81    pub timeout_ms: u32,
82}
83
84// ── Enhanced Settings Constraints ─────────────────────────────────────────────
85
86/// `q42:StorageConfiguration` — validates storage path and quota settings
87#[derive(Debug, Clone)]
88pub struct StorageConfiguration {
89    pub min_quota_gb: u32,
90    pub max_quota_gb: u32,
91    pub require_absolute_path: bool,
92    pub allowed_path_patterns: Vec<String>,
93}
94
95/// `q42:NetworkConfiguration` — validates daemon network settings
96#[derive(Debug, Clone)]
97pub struct NetworkConfiguration {
98    pub allowed_ports: Vec<u16>,
99    pub require_port_conflict_check: bool,
100    pub default_host: String,
101}
102
103/// `q42:TaxRecipientConfiguration` — validates ILP tax recipient configuration
104#[derive(Debug, Clone)]
105pub struct TaxRecipientConfiguration {
106    pub require_ilp_address: bool,
107    pub min_share_percent: u8,
108    pub max_share_percent: u8,
109    pub allow_nym_routing: bool,
110    pub total_share_validation: bool,
111}
112
113/// `q42:SecurityConfiguration` — validates security-related settings
114#[derive(Debug, Clone)]
115pub struct SecurityConfiguration {
116    pub require_encryption: bool,
117    pub allowed_cipher_suites: Vec<String>,
118    pub min_key_length_bits: u16,
119}
120
121// ── Opcode Generation for New Constraints ─────────────────────────────────────
122
123impl LogConfiguration {
124    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
125        vec![
126            SlgOpcode::CheckMaxInclusive(self.max_memory_logs as f64),
127            SlgOpcode::CheckMaxInclusive(self.max_disk_logs as f64),
128            SlgOpcode::CheckMinInclusive(self.flush_interval_ms as f64),
129        ]
130    }
131}
132
133impl LogLevel {
134    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
135        // Validate that log level is in allowed set
136        self.allowed_levels
137            .iter()
138            .map(|level| SlgOpcode::CheckHasValue(crate::q_hash(level)))
139            .collect()
140    }
141}
142
143impl LogEntry {
144    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
145        let mut opcodes = Vec::new();
146        if self.require_timestamp {
147            opcodes.push(SlgOpcode::CheckMinCount(1));
148        }
149        if self.require_context {
150            opcodes.push(SlgOpcode::CheckMinCount(1));
151        }
152        if self.max_message_length > 0 {
153            opcodes.push(SlgOpcode::CheckMaxLength(self.max_message_length));
154        }
155        if !self.allow_data_payload {
156            opcodes.push(SlgOpcode::CheckMaxCount(3)); // timestamp, level, message only
157        }
158        opcodes
159    }
160}
161
162impl LogRetention {
163    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
164        vec![
165            SlgOpcode::CheckMaxInclusive(self.max_age_days as f64),
166            SlgOpcode::CheckMaxInclusive(self.max_size_mb as f64),
167        ]
168    }
169}
170
171impl LogExportFormat {
172    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
173        let mut opcodes = vec![SlgOpcode::CheckMaxInclusive(self.max_export_size_mb as f64)];
174        // Add format validation
175        for format in &self.allowed_formats {
176            opcodes.push(SlgOpcode::CheckHasValue(crate::q_hash(format)));
177        }
178        opcodes
179    }
180}
181
182impl SystemTrayConfiguration {
183    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
184        vec![SlgOpcode::CheckMaxInclusive(self.max_menu_items as f64)]
185    }
186}
187
188impl TrayMenuItem {
189    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
190        let mut opcodes = Vec::new();
191        if self.require_label {
192            opcodes.push(SlgOpcode::CheckMinCount(1));
193        }
194        if self.require_action {
195            opcodes.push(SlgOpcode::CheckMinCount(1));
196        }
197        if self.max_label_length > 0 {
198            opcodes.push(SlgOpcode::CheckMaxLength(self.max_label_length));
199        }
200        opcodes
201    }
202}
203
204impl TrayStatusIndicator {
205    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
206        vec![
207            SlgOpcode::CheckMaxLength(self.max_status_length),
208            SlgOpcode::CheckMinInclusive(self.update_interval_ms as f64),
209        ]
210    }
211}
212
213impl TrayAction {
214    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
215        vec![SlgOpcode::CheckMaxInclusive(self.timeout_ms as f64)]
216    }
217}
218
219impl StorageConfiguration {
220    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
221        let mut opcodes = vec![
222            SlgOpcode::CheckMinInclusive(self.min_quota_gb as f64),
223            SlgOpcode::CheckMaxInclusive(self.max_quota_gb as f64),
224        ];
225        if self.require_absolute_path {
226            opcodes.push(SlgOpcode::CheckMinCount(1));
227        }
228        opcodes
229    }
230}
231
232impl NetworkConfiguration {
233    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
234        let mut opcodes = Vec::new();
235        for port in &self.allowed_ports {
236            opcodes.push(SlgOpcode::CheckHasValue(*port as u64));
237        }
238        opcodes
239    }
240}
241
242impl TaxRecipientConfiguration {
243    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
244        vec![
245            SlgOpcode::CheckMinInclusive(self.min_share_percent as f64),
246            SlgOpcode::CheckMaxInclusive(self.max_share_percent as f64),
247        ]
248    }
249}
250
251impl SecurityConfiguration {
252    pub fn to_opcodes(&self) -> Vec<SlgOpcode> {
253        vec![SlgOpcode::CheckMinInclusive(
254            self.min_key_length_bits as f64,
255        )]
256    }
257}
258
259// ── SHACL TTL Vocabulary Extensions ─────────────────────────────────────────────
260
261/// Returns the SHACL TTL vocabulary extensions for the new constraints
262pub fn get_shacl_extensions_ttl() -> &'static str {
263    r#"
264@prefix q42: <https://webizen.org/q42#> .
265@prefix sh: <http://www.w3.org/ns/shacl#> .
266@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
267
268# ── Logging System Constraints ─────────────────────────────────────────────
269
270q42:LogConfiguration a sh:NodeShape ;
271    sh:property [
272        sh:path q42:maxMemoryLogs ;
273        sh:datatype xsd:integer ;
274        sh:minInclusive 1 ;
275        sh:maxInclusive 100000 ;
276        sh:message "Maximum memory logs must be between 1 and 100000" ;
277    ] ;
278    sh:property [
279        sh:path q42:maxDiskLogs ;
280        sh:datatype xsd:integer ;
281        sh:minInclusive 1 ;
282        sh:maxInclusive 1000000 ;
283        sh:message "Maximum disk logs must be between 1 and 1000000" ;
284    ] ;
285    sh:property [
286        sh:path q42:flushIntervalMs ;
287        sh:datatype xsd:integer ;
288        sh:minInclusive 100 ;
289        sh:maxInclusive 60000 ;
290        sh:message "Flush interval must be between 100ms and 60000ms" ;
291    ] .
292
293q42:LogLevel a sh:NodeShape ;
294    sh:property [
295        sh:path q42:allowedLevels ;
296        sh:in ("DEBUG" "INFO" "WARN" "ERROR" "CRITICAL") ;
297        sh:message "Log level must be one of: DEBUG, INFO, WARN, ERROR, CRITICAL" ;
298    ] .
299
300q42:LogEntry a sh:NodeShape ;
301    sh:property [
302        sh:path q42:timestamp ;
303        sh:datatype xsd:dateTime ;
304        sh:minCount 1 ;
305        sh:message "Log entry must have a timestamp" ;
306    ] ;
307    sh:property [
308        sh:path q42:level ;
309        sh:datatype xsd:string ;
310        sh:minCount 1 ;
311        sh:message "Log entry must have a level" ;
312    ] ;
313    sh:property [
314        sh:path q42:message ;
315        sh:datatype xsd:string ;
316        sh:maxLength 10000 ;
317        sh:message "Log message must not exceed 10000 characters" ;
318    ] .
319
320q42:LogRetention a sh:NodeShape ;
321    sh:property [
322        sh:path q42:maxAgeDays ;
323        sh:datatype xsd:integer ;
324        sh:minInclusive 1 ;
325        sh:maxInclusive 365 ;
326        sh:message "Log retention age must be between 1 and 365 days" ;
327    ] ;
328    sh:property [
329        sh:path q42:maxSizeMb ;
330        sh:datatype xsd:integer ;
331        sh:minInclusive 1 ;
332        sh:maxInclusive 10240 ;
333        sh:message "Log retention size must be between 1MB and 10GB" ;
334    ] .
335
336# ── System Tray Constraints ─────────────────────────────────────────────────
337
338q42:SystemTrayConfiguration a sh:NodeShape ;
339    sh:property [
340        sh:path q42:maxMenuItems ;
341        sh:datatype xsd:integer ;
342        sh:minInclusive 1 ;
343        sh:maxInclusive 20 ;
344        sh:message "System tray can have at most 20 menu items" ;
345    ] .
346
347q42:TrayMenuItem a sh:NodeShape ;
348    sh:property [
349        sh:path q42:label ;
350        sh:datatype xsd:string ;
351        sh:minCount 1 ;
352        sh:maxLength 50 ;
353        sh:message "Tray menu item must have a label (max 50 characters)" ;
354    ] ;
355    sh:property [
356        sh:path q42:action ;
357        sh:datatype xsd:string ;
358        sh:minCount 1 ;
359        sh:message "Tray menu item must have an action" ;
360    ] .
361
362q42:TrayStatusIndicator a sh:NodeShape ;
363    sh:property [
364        sh:path q42:statusText ;
365        sh:datatype xsd:string ;
366        sh:maxLength 100 ;
367        sh:message "Status text must not exceed 100 characters" ;
368    ] ;
369    sh:property [
370        sh:path q42:updateIntervalMs ;
371        sh:datatype xsd:integer ;
372        sh:minInclusive 100 ;
373        sh:maxInclusive 10000 ;
374        sh:message "Update interval must be between 100ms and 10000ms" ;
375    ] .
376
377# ── Enhanced Settings Constraints ─────────────────────────────────────────────
378
379q42:StorageConfiguration a sh:NodeShape ;
380    sh:property [
381        sh:path q42:storageQuotaGb ;
382        sh:datatype xsd:integer ;
383        sh:minInclusive 1 ;
384        sh:maxInclusive 1000 ;
385        sh:message "Storage quota must be between 1GB and 1000GB" ;
386    ] ;
387    sh:property [
388        sh:path q42:storagePath ;
389        sh:datatype xsd:string ;
390        sh:minCount 1 ;
391        sh:pattern "^/" ;
392        sh:message "Storage path must be an absolute path" ;
393    ] .
394
395q42:NetworkConfiguration a sh:NodeShape ;
396    sh:property [
397        sh:path q42:daemonPort ;
398        sh:datatype xsd:integer ;
399        sh:minInclusive 1024 ;
400        sh:maxInclusive 65535 ;
401        sh:message "Daemon port must be between 1024 and 65535" ;
402    ] ;
403    sh:property [
404        sh:path q42:daemonHost ;
405        sh:datatype xsd:string ;
406        sh:pattern "^(localhost|127\\.0\\.0\\.1|0\\.0\\.0\\.0)$" ;
407        sh:message "Daemon host must be localhost, 127.0.0.1, or 0.0.0.0" ;
408    ] .
409
410q42:TaxRecipientConfiguration a sh:NodeShape ;
411    sh:property [
412        sh:path q42:sharePercent ;
413        sh:datatype xsd:integer ;
414        sh:minInclusive 0 ;
415        sh:maxInclusive 100 ;
416        sh:message "Share percent must be between 0 and 100" ;
417    ] ;
418    sh:property [
419        sh:path q42:ilpAddress ;
420        sh:datatype xsd:string ;
421        sh:minCount 1 ;
422        sh:pattern "^\\$ilp\\." ;
423        sh:message "ILP address must start with $ilp." ;
424    ] .
425
426q42:SecurityConfiguration a sh:NodeShape ;
427    sh:property [
428        sh:path q42:minKeyLengthBits ;
429        sh:datatype xsd:integer ;
430        sh:minInclusive 128 ;
431        sh:maxInclusive 4096 ;
432        sh:message "Key length must be between 128 and 4096 bits" ;
433    ] ;
434    sh:property [
435        sh:path q42:requireEncryption ;
436        sh:datatype xsd:boolean ;
437        sh:message "Require encryption must be a boolean" ;
438    ] .
439"#
440}