1use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub struct GgenConfig {
12 pub project: ProjectConfig,
14
15 #[serde(skip_serializing_if = "Option::is_none")]
17 pub ai: Option<AiConfig>,
18
19 #[serde(skip_serializing_if = "Option::is_none")]
21 pub templates: Option<TemplatesConfig>,
22
23 #[serde(skip_serializing_if = "Option::is_none")]
25 pub rdf: Option<RdfConfig>,
26
27 #[serde(skip_serializing_if = "Option::is_none")]
29 pub sparql: Option<SparqlConfig>,
30
31 #[serde(skip_serializing_if = "Option::is_none")]
33 pub lifecycle: Option<LifecycleConfig>,
34
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub security: Option<SecurityConfig>,
38
39 #[serde(skip_serializing_if = "Option::is_none")]
41 pub performance: Option<PerformanceConfig>,
42
43 #[serde(skip_serializing_if = "Option::is_none")]
45 pub logging: Option<LoggingConfig>,
46
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub diataxis: Option<DiataxisConfig>,
50
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub features: Option<HashMap<String, bool>>,
54
55 #[serde(skip_serializing_if = "Option::is_none")]
57 pub env: Option<HashMap<String, serde_json::Value>>,
58
59 #[serde(skip_serializing_if = "Option::is_none")]
61 pub build: Option<BuildConfig>,
62
63 #[serde(skip_serializing_if = "Option::is_none")]
65 pub test: Option<TestConfig>,
66
67 #[serde(skip_serializing_if = "Option::is_none")]
69 pub package: Option<PackageMetadata>,
70
71 #[serde(skip_serializing_if = "Option::is_none")]
73 pub marketplace: Option<MarketplaceConfig>,
74
75 #[serde(skip_serializing_if = "Option::is_none")]
77 pub generation: Option<GenerationSafetyConfig>,
78
79 #[serde(skip_serializing_if = "Option::is_none")]
81 pub codeowners: Option<CodeownersConfig>,
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86pub struct ProjectConfig {
87 pub name: String,
89
90 pub version: String,
92
93 #[serde(skip_serializing_if = "Option::is_none")]
95 pub description: Option<String>,
96
97 #[serde(skip_serializing_if = "Option::is_none")]
99 pub authors: Option<Vec<String>>,
100
101 #[serde(skip_serializing_if = "Option::is_none")]
103 pub license: Option<String>,
104
105 #[serde(skip_serializing_if = "Option::is_none")]
107 pub repository: Option<String>,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
112pub struct AiConfig {
113 pub provider: String,
115
116 pub model: String,
118
119 #[serde(default = "default_temperature")]
121 pub temperature: f32,
122
123 #[serde(default = "default_max_tokens")]
125 pub max_tokens: u32,
126
127 #[serde(default = "default_timeout")]
129 pub timeout: u32,
130
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub prompts: Option<AiPrompts>,
134
135 #[serde(skip_serializing_if = "Option::is_none")]
137 pub validation: Option<AiValidation>,
138}
139
140#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142pub struct AiPrompts {
143 #[serde(skip_serializing_if = "Option::is_none")]
145 pub system: Option<String>,
146
147 #[serde(skip_serializing_if = "Option::is_none")]
149 pub user_prefix: Option<String>,
150}
151
152#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
154pub struct AiValidation {
155 #[serde(default)]
157 pub enabled: bool,
158
159 #[serde(default = "default_quality_threshold")]
161 pub quality_threshold: f32,
162
163 #[serde(default = "default_max_iterations")]
165 pub max_iterations: u32,
166}
167
168#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
170pub struct TemplatesConfig {
171 #[serde(skip_serializing_if = "Option::is_none")]
173 pub directory: Option<String>,
174
175 #[serde(skip_serializing_if = "Option::is_none")]
177 pub output_directory: Option<String>,
178
179 #[serde(default)]
181 pub backup_enabled: bool,
182
183 #[serde(default)]
185 pub idempotent: bool,
186}
187
188#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
190pub struct DiataxisConfig {
191 #[serde(default = "default_diataxis_root")]
193 pub root: String,
194
195 #[serde(default = "default_diataxis_index")]
197 pub index: String,
198
199 #[serde(skip_serializing_if = "Option::is_none")]
201 pub quadrants: Option<DiataxisQuadrants>,
202}
203
204#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
206pub struct DiataxisQuadrants {
207 #[serde(skip_serializing_if = "Option::is_none")]
209 pub tutorials: Option<DiataxisSection>,
210
211 #[serde(rename = "how-to", skip_serializing_if = "Option::is_none")]
213 pub how_to: Option<DiataxisSection>,
214
215 #[serde(skip_serializing_if = "Option::is_none")]
217 pub reference: Option<DiataxisSection>,
218
219 #[serde(skip_serializing_if = "Option::is_none")]
221 pub explanations: Option<DiataxisSection>,
222}
223
224#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
226pub struct DiataxisSection {
227 #[serde(skip_serializing_if = "Option::is_none")]
229 pub source: Option<String>,
230
231 #[serde(skip_serializing_if = "Option::is_none")]
233 pub output: Option<String>,
234
235 #[serde(default, skip_serializing_if = "Vec::is_empty")]
237 pub navigation: Vec<DiataxisNavItem>,
238}
239
240#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
242pub struct DiataxisNavItem {
243 pub title: String,
245
246 pub path: String,
248
249 #[serde(skip_serializing_if = "Option::is_none")]
251 pub description: Option<String>,
252}
253
254#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
256pub struct RdfConfig {
257 #[serde(skip_serializing_if = "Option::is_none")]
259 pub base_uri: Option<String>,
260
261 #[serde(skip_serializing_if = "Option::is_none")]
263 pub base_iri: Option<String>,
264
265 #[serde(skip_serializing_if = "Option::is_none")]
267 pub prefixes: Option<HashMap<String, String>>,
268
269 #[serde(skip_serializing_if = "Option::is_none")]
271 pub default_format: Option<String>,
272
273 #[serde(default)]
275 pub cache_queries: bool,
276}
277
278#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
280pub struct SparqlConfig {
281 #[serde(default = "default_sparql_timeout")]
283 pub timeout: u32,
284
285 #[serde(default = "default_max_results")]
287 pub max_results: u32,
288
289 #[serde(default)]
291 pub cache_enabled: bool,
292}
293
294#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
296pub struct LifecycleConfig {
297 #[serde(default)]
299 pub enabled: bool,
300
301 #[serde(skip_serializing_if = "Option::is_none")]
303 pub config_file: Option<String>,
304
305 #[serde(skip_serializing_if = "Option::is_none")]
307 pub cache_directory: Option<String>,
308
309 #[serde(skip_serializing_if = "Option::is_none")]
311 pub state_file: Option<String>,
312
313 #[serde(skip_serializing_if = "Option::is_none")]
315 pub phases: Option<HashMap<String, Vec<String>>>,
316}
317
318#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
320pub struct SecurityConfig {
321 #[serde(default = "default_true")]
323 pub path_traversal_protection: bool,
324
325 #[serde(default = "default_true")]
327 pub shell_injection_protection: bool,
328
329 #[serde(default = "default_true")]
331 pub template_sandboxing: bool,
332
333 #[serde(default = "default_true")]
335 pub validate_paths: bool,
336
337 #[serde(default)]
339 pub require_confirmation: bool,
340
341 #[serde(default)]
343 pub audit_operations: bool,
344
345 #[serde(default)]
347 pub backup_before_write: bool,
348}
349
350#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
352pub struct PerformanceConfig {
353 #[serde(default)]
355 pub parallel_execution: bool,
356
357 #[serde(default = "default_max_workers")]
359 pub max_workers: u32,
360
361 #[serde(skip_serializing_if = "Option::is_none")]
363 pub cache_size: Option<String>,
364
365 #[serde(default)]
367 pub enable_profiling: bool,
368
369 #[serde(skip_serializing_if = "Option::is_none")]
371 pub memory_limit_mb: Option<u32>,
372}
373
374#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
376pub struct LoggingConfig {
377 #[serde(default = "default_log_level")]
379 pub level: String,
380
381 #[serde(default = "default_log_format")]
383 pub format: String,
384
385 #[serde(skip_serializing_if = "Option::is_none")]
387 pub file: Option<String>,
388
389 #[serde(skip_serializing_if = "Option::is_none")]
391 pub rotation: Option<String>,
392}
393
394#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
396pub struct BuildConfig {
397 #[serde(skip_serializing_if = "Option::is_none")]
399 pub target: Option<String>,
400
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub features: Option<Vec<String>>,
404
405 #[serde(skip_serializing_if = "Option::is_none")]
407 pub profile: Option<String>,
408
409 #[serde(skip_serializing_if = "Option::is_none")]
411 pub parallel_jobs: Option<u32>,
412}
413
414#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
416pub struct TestConfig {
417 #[serde(skip_serializing_if = "Option::is_none")]
419 pub framework: Option<String>,
420
421 #[serde(default)]
423 pub parallel: bool,
424
425 #[serde(skip_serializing_if = "Option::is_none")]
427 pub timeout_seconds: Option<u32>,
428
429 #[serde(default)]
431 pub coverage_enabled: bool,
432
433 #[serde(skip_serializing_if = "Option::is_none")]
435 pub coverage_threshold: Option<u32>,
436}
437
438#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
440pub struct PackageMetadata {
441 pub name: String,
443
444 pub version: String,
446
447 #[serde(skip_serializing_if = "Option::is_none")]
449 pub description: Option<String>,
450
451 #[serde(skip_serializing_if = "Option::is_none")]
453 pub authors: Option<Vec<String>>,
454
455 #[serde(skip_serializing_if = "Option::is_none")]
457 pub license: Option<String>,
458
459 #[serde(skip_serializing_if = "Option::is_none")]
461 pub repository: Option<String>,
462
463 #[serde(skip_serializing_if = "Option::is_none")]
465 pub keywords: Option<Vec<String>>,
466
467 #[serde(skip_serializing_if = "Option::is_none")]
469 pub categories: Option<Vec<String>>,
470
471 #[serde(skip_serializing_if = "Option::is_none")]
473 pub metadata: Option<HashMap<String, serde_json::Value>>,
474}
475
476#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
487pub struct MarketplaceConfig {
488 #[serde(default)]
490 pub fmea_validation: bool,
491
492 #[serde(default)]
494 pub require_fmea: bool,
495
496 #[serde(default = "default_critical_threshold")]
498 pub critical_threshold: u16,
499}
500
501impl Default for MarketplaceConfig {
502 fn default() -> Self {
503 Self {
504 fmea_validation: false,
505 require_fmea: false,
506 critical_threshold: 200,
507 }
508 }
509}
510
511#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
521pub struct FmeaControl {
522 pub id: String,
524
525 pub mode: String,
527
528 #[serde(default = "default_fmea_score")]
530 pub severity: u8,
531
532 #[serde(default = "default_fmea_score")]
534 pub occurrence: u8,
535
536 #[serde(default = "default_fmea_score")]
538 pub detection: u8,
539
540 #[serde(skip_serializing_if = "Option::is_none")]
542 pub control: Option<String>,
543
544 #[serde(skip_serializing_if = "Option::is_none")]
546 pub evidence: Option<String>,
547}
548
549impl FmeaControl {
550 #[must_use]
552 pub fn rpn(&self) -> u16 {
553 u16::from(self.severity) * u16::from(self.occurrence) * u16::from(self.detection)
554 }
555
556 #[must_use]
558 pub fn is_mitigated(&self) -> bool {
559 self.control.is_some()
560 }
561
562 #[must_use]
564 pub fn is_critical_unmitigated(&self, threshold: u16) -> bool {
565 self.rpn() > threshold && !self.is_mitigated()
566 }
567}
568
569#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
573pub struct PackageFmeaSection {
574 #[serde(default, skip_serializing_if = "Vec::is_empty")]
576 pub controls: Vec<FmeaControl>,
577}
578
579#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
589pub struct GenerationSafetyConfig {
590 #[serde(default = "default_true")]
592 pub enabled: bool,
593
594 #[serde(default, skip_serializing_if = "Vec::is_empty")]
596 pub protected_paths: Vec<String>,
597
598 #[serde(default, skip_serializing_if = "Vec::is_empty")]
600 pub regenerate_paths: Vec<String>,
601
602 #[serde(skip_serializing_if = "Option::is_none")]
604 pub generated_header: Option<String>,
605
606 #[serde(default)]
608 pub require_confirmation: bool,
609
610 #[serde(default = "default_true")]
612 pub backup_before_write: bool,
613
614 #[serde(skip_serializing_if = "Option::is_none")]
616 pub poka_yoke: Option<PokaYokeSettings>,
617}
618
619#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
621pub struct PokaYokeSettings {
622 #[serde(default = "default_true")]
624 pub warning_headers: bool,
625
626 #[serde(default)]
628 pub gitignore_generated: bool,
629
630 #[serde(default)]
632 pub gitattributes_generated: bool,
633
634 #[serde(default)]
636 pub validate_imports: bool,
637}
638
639impl Default for GenerationSafetyConfig {
640 fn default() -> Self {
641 Self {
642 enabled: true,
643 protected_paths: vec!["src/domain/**".to_string()],
644 regenerate_paths: vec!["src/generated/**".to_string()],
645 generated_header: Some(
646 "// DO NOT EDIT - Generated by ggen. Changes will be overwritten.".to_string(),
647 ),
648 require_confirmation: false,
649 backup_before_write: true,
650 poka_yoke: Some(PokaYokeSettings::default()),
651 }
652 }
653}
654
655impl Default for PokaYokeSettings {
656 fn default() -> Self {
657 Self {
658 warning_headers: true,
659 gitignore_generated: false,
660 gitattributes_generated: false,
661 validate_imports: false,
662 }
663 }
664}
665
666#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
676pub struct CodeownersConfig {
677 #[serde(default)]
679 pub enabled: bool,
680
681 #[serde(default, skip_serializing_if = "Vec::is_empty")]
683 pub source_dirs: Vec<String>,
684
685 #[serde(default, skip_serializing_if = "Vec::is_empty")]
687 pub base_dirs: Vec<String>,
688
689 #[serde(skip_serializing_if = "Option::is_none")]
691 pub output_path: Option<String>,
692
693 #[serde(default)]
695 pub auto_regenerate: bool,
696}
697
698fn default_critical_threshold() -> u16 {
700 200
701}
702
703fn default_fmea_score() -> u8 {
704 5
705}
706
707fn default_temperature() -> f32 {
709 0.7
710}
711
712fn default_max_tokens() -> u32 {
713 2000
714}
715
716fn default_timeout() -> u32 {
717 30
718}
719
720fn default_quality_threshold() -> f32 {
721 0.8
722}
723
724fn default_max_iterations() -> u32 {
725 3
726}
727
728fn default_sparql_timeout() -> u32 {
729 10
730}
731
732fn default_max_results() -> u32 {
733 1000
734}
735
736fn default_max_workers() -> u32 {
737 num_cpus()
738}
739
740fn default_log_level() -> String {
741 "info".to_string()
742}
743
744fn default_log_format() -> String {
745 "text".to_string()
746}
747
748fn default_diataxis_root() -> String {
749 "docs".to_string()
750}
751
752fn default_diataxis_index() -> String {
753 "docs/diataxis-index.md".to_string()
754}
755
756fn default_true() -> bool {
757 true
758}
759
760fn num_cpus() -> u32 {
761 std::thread::available_parallelism()
762 .map(|n| n.get() as u32)
763 .unwrap_or(4)
764}
765
766impl Default for GgenConfig {
767 fn default() -> Self {
768 Self {
769 project: ProjectConfig {
770 name: "unnamed".to_string(),
771 version: "0.1.0".to_string(),
772 description: None,
773 authors: None,
774 license: None,
775 repository: None,
776 },
777 ai: None,
778 templates: None,
779 rdf: None,
780 sparql: None,
781 lifecycle: None,
782 security: None,
783 performance: None,
784 logging: None,
785 diataxis: None,
786 features: None,
787 env: None,
788 build: None,
789 test: None,
790 package: None,
791 marketplace: None,
792 generation: None,
793 codeowners: None,
794 }
795 }
796}
797
798impl Default for CodeownersConfig {
799 fn default() -> Self {
800 Self {
801 enabled: false,
802 source_dirs: vec!["ontology".to_string()],
803 base_dirs: vec![
804 "ontology".to_string(),
805 "src/generated".to_string(),
806 "src/domain".to_string(),
807 ],
808 output_path: None,
809 auto_regenerate: false,
810 }
811 }
812}