ggen_config/
schema.rs

1//! Schema definitions for ggen.toml configuration
2//!
3//! This module defines the complete structure of ggen.toml files
4//! using serde-compatible Rust structs.
5
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9/// Root configuration structure for ggen.toml
10#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
11pub struct GgenConfig {
12    /// Project metadata
13    pub project: ProjectConfig,
14
15    /// AI configuration (optional)
16    #[serde(skip_serializing_if = "Option::is_none")]
17    pub ai: Option<AiConfig>,
18
19    /// Templates configuration (optional)
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub templates: Option<TemplatesConfig>,
22
23    /// RDF configuration (optional)
24    #[serde(skip_serializing_if = "Option::is_none")]
25    pub rdf: Option<RdfConfig>,
26
27    /// SPARQL configuration (optional)
28    #[serde(skip_serializing_if = "Option::is_none")]
29    pub sparql: Option<SparqlConfig>,
30
31    /// Lifecycle configuration (optional)
32    #[serde(skip_serializing_if = "Option::is_none")]
33    pub lifecycle: Option<LifecycleConfig>,
34
35    /// Security settings (optional)
36    #[serde(skip_serializing_if = "Option::is_none")]
37    pub security: Option<SecurityConfig>,
38
39    /// Performance settings (optional)
40    #[serde(skip_serializing_if = "Option::is_none")]
41    pub performance: Option<PerformanceConfig>,
42
43    /// Logging configuration (optional)
44    #[serde(skip_serializing_if = "Option::is_none")]
45    pub logging: Option<LoggingConfig>,
46
47    /// Diataxis documentation configuration (optional)
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub diataxis: Option<DiataxisConfig>,
50
51    /// Feature flags (optional)
52    #[serde(skip_serializing_if = "Option::is_none")]
53    pub features: Option<HashMap<String, bool>>,
54
55    /// Environment-specific overrides (optional)
56    #[serde(skip_serializing_if = "Option::is_none")]
57    pub env: Option<HashMap<String, serde_json::Value>>,
58
59    /// Build configuration (optional)
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub build: Option<BuildConfig>,
62
63    /// Test configuration (optional)
64    #[serde(skip_serializing_if = "Option::is_none")]
65    pub test: Option<TestConfig>,
66
67    /// Package metadata (for marketplace packages)
68    #[serde(skip_serializing_if = "Option::is_none")]
69    pub package: Option<PackageMetadata>,
70
71    /// Marketplace configuration (FMEA validation settings for package operations)
72    #[serde(skip_serializing_if = "Option::is_none")]
73    pub marketplace: Option<MarketplaceConfig>,
74
75    /// Generation configuration (structural Poka-Yoke - directory separation, headers)
76    #[serde(skip_serializing_if = "Option::is_none")]
77    pub generation: Option<GenerationSafetyConfig>,
78
79    /// CODEOWNERS generation configuration
80    #[serde(skip_serializing_if = "Option::is_none")]
81    pub codeowners: Option<CodeownersConfig>,
82}
83
84/// Project metadata configuration
85#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
86pub struct ProjectConfig {
87    /// Project name
88    pub name: String,
89
90    /// Project version
91    pub version: String,
92
93    /// Project description (optional)
94    #[serde(skip_serializing_if = "Option::is_none")]
95    pub description: Option<String>,
96
97    /// Project authors (optional)
98    #[serde(skip_serializing_if = "Option::is_none")]
99    pub authors: Option<Vec<String>>,
100
101    /// Project license (optional)
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub license: Option<String>,
104
105    /// Project repository URL (optional)
106    #[serde(skip_serializing_if = "Option::is_none")]
107    pub repository: Option<String>,
108}
109
110/// AI provider configuration
111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
112pub struct AiConfig {
113    /// AI provider (openai, ollama, anthropic, etc.)
114    pub provider: String,
115
116    /// Model name
117    pub model: String,
118
119    /// Temperature for generation (0.0 - 1.0)
120    #[serde(default = "default_temperature")]
121    pub temperature: f32,
122
123    /// Maximum tokens for generation
124    #[serde(default = "default_max_tokens")]
125    pub max_tokens: u32,
126
127    /// Request timeout in seconds
128    #[serde(default = "default_timeout")]
129    pub timeout: u32,
130
131    /// System and user prompts (optional)
132    #[serde(skip_serializing_if = "Option::is_none")]
133    pub prompts: Option<AiPrompts>,
134
135    /// Validation settings (optional)
136    #[serde(skip_serializing_if = "Option::is_none")]
137    pub validation: Option<AiValidation>,
138}
139
140/// AI prompt configuration
141#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
142pub struct AiPrompts {
143    /// System prompt
144    #[serde(skip_serializing_if = "Option::is_none")]
145    pub system: Option<String>,
146
147    /// User prompt prefix
148    #[serde(skip_serializing_if = "Option::is_none")]
149    pub user_prefix: Option<String>,
150}
151
152/// AI validation configuration
153#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
154pub struct AiValidation {
155    /// Whether validation is enabled
156    #[serde(default)]
157    pub enabled: bool,
158
159    /// Quality threshold (0.0 - 1.0)
160    #[serde(default = "default_quality_threshold")]
161    pub quality_threshold: f32,
162
163    /// Maximum validation iterations
164    #[serde(default = "default_max_iterations")]
165    pub max_iterations: u32,
166}
167
168/// Templates configuration
169#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
170pub struct TemplatesConfig {
171    /// Template source directory
172    #[serde(skip_serializing_if = "Option::is_none")]
173    pub directory: Option<String>,
174
175    /// Output directory for generated files
176    #[serde(skip_serializing_if = "Option::is_none")]
177    pub output_directory: Option<String>,
178
179    /// Enable backup before overwriting
180    #[serde(default)]
181    pub backup_enabled: bool,
182
183    /// Idempotent generation (only update if changed)
184    #[serde(default)]
185    pub idempotent: bool,
186}
187
188/// Diataxis documentation configuration
189#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
190pub struct DiataxisConfig {
191    /// Root directory for diataxis docs (defaults to docs)
192    #[serde(default = "default_diataxis_root")]
193    pub root: String,
194
195    /// Index file path
196    #[serde(default = "default_diataxis_index")]
197    pub index: String,
198
199    /// Quadrant-specific configuration
200    #[serde(skip_serializing_if = "Option::is_none")]
201    pub quadrants: Option<DiataxisQuadrants>,
202}
203
204/// Quadrant configuration (tutorials, how-to, reference, explanations)
205#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
206pub struct DiataxisQuadrants {
207    /// Tutorials quadrant configuration
208    #[serde(skip_serializing_if = "Option::is_none")]
209    pub tutorials: Option<DiataxisSection>,
210
211    /// How-to guides quadrant configuration
212    #[serde(rename = "how-to", skip_serializing_if = "Option::is_none")]
213    pub how_to: Option<DiataxisSection>,
214
215    /// Reference quadrant configuration
216    #[serde(skip_serializing_if = "Option::is_none")]
217    pub reference: Option<DiataxisSection>,
218
219    /// Explanations quadrant configuration
220    #[serde(skip_serializing_if = "Option::is_none")]
221    pub explanations: Option<DiataxisSection>,
222}
223
224/// Per-quadrant configuration
225#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
226pub struct DiataxisSection {
227    /// Source directory for existing docs
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub source: Option<String>,
230
231    /// Output directory for generated docs
232    #[serde(skip_serializing_if = "Option::is_none")]
233    pub output: Option<String>,
234
235    /// Navigation entries for this quadrant
236    #[serde(default, skip_serializing_if = "Vec::is_empty")]
237    pub navigation: Vec<DiataxisNavItem>,
238}
239
240/// Navigation entry within a quadrant
241#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
242pub struct DiataxisNavItem {
243    /// Display title for the navigation entry
244    pub title: String,
245
246    /// Relative or absolute path to the target content
247    pub path: String,
248
249    /// Optional description shown alongside the entry
250    #[serde(skip_serializing_if = "Option::is_none")]
251    pub description: Option<String>,
252}
253
254/// RDF configuration
255#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
256pub struct RdfConfig {
257    /// Base IRI/URI for RDF entities
258    #[serde(skip_serializing_if = "Option::is_none")]
259    pub base_uri: Option<String>,
260
261    /// Base IRI (alternative name)
262    #[serde(skip_serializing_if = "Option::is_none")]
263    pub base_iri: Option<String>,
264
265    /// RDF namespace prefixes
266    #[serde(skip_serializing_if = "Option::is_none")]
267    pub prefixes: Option<HashMap<String, String>>,
268
269    /// Default RDF format (turtle, rdfxml, etc.)
270    #[serde(skip_serializing_if = "Option::is_none")]
271    pub default_format: Option<String>,
272
273    /// Enable query caching
274    #[serde(default)]
275    pub cache_queries: bool,
276}
277
278/// SPARQL configuration
279#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
280pub struct SparqlConfig {
281    /// Query timeout in seconds
282    #[serde(default = "default_sparql_timeout")]
283    pub timeout: u32,
284
285    /// Maximum results per query
286    #[serde(default = "default_max_results")]
287    pub max_results: u32,
288
289    /// Enable query caching
290    #[serde(default)]
291    pub cache_enabled: bool,
292}
293
294/// Lifecycle configuration
295#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
296pub struct LifecycleConfig {
297    /// Enable lifecycle management
298    #[serde(default)]
299    pub enabled: bool,
300
301    /// Lifecycle config file (e.g., make.toml)
302    #[serde(skip_serializing_if = "Option::is_none")]
303    pub config_file: Option<String>,
304
305    /// Cache directory
306    #[serde(skip_serializing_if = "Option::is_none")]
307    pub cache_directory: Option<String>,
308
309    /// State file path
310    #[serde(skip_serializing_if = "Option::is_none")]
311    pub state_file: Option<String>,
312
313    /// Lifecycle phases
314    #[serde(skip_serializing_if = "Option::is_none")]
315    pub phases: Option<HashMap<String, Vec<String>>>,
316}
317
318/// Security configuration
319#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
320pub struct SecurityConfig {
321    /// Enable path traversal protection
322    #[serde(default = "default_true")]
323    pub path_traversal_protection: bool,
324
325    /// Enable shell injection protection
326    #[serde(default = "default_true")]
327    pub shell_injection_protection: bool,
328
329    /// Enable template sandboxing
330    #[serde(default = "default_true")]
331    pub template_sandboxing: bool,
332
333    /// Validate file paths
334    #[serde(default = "default_true")]
335    pub validate_paths: bool,
336
337    /// Require user confirmation for destructive operations
338    #[serde(default)]
339    pub require_confirmation: bool,
340
341    /// Audit all operations
342    #[serde(default)]
343    pub audit_operations: bool,
344
345    /// Backup before write operations
346    #[serde(default)]
347    pub backup_before_write: bool,
348}
349
350/// Performance configuration
351#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
352pub struct PerformanceConfig {
353    /// Enable parallel execution
354    #[serde(default)]
355    pub parallel_execution: bool,
356
357    /// Maximum parallel workers
358    #[serde(default = "default_max_workers")]
359    pub max_workers: u32,
360
361    /// Cache size (as string, e.g., "1GB")
362    #[serde(skip_serializing_if = "Option::is_none")]
363    pub cache_size: Option<String>,
364
365    /// Enable profiling
366    #[serde(default)]
367    pub enable_profiling: bool,
368
369    /// Memory limit in MB
370    #[serde(skip_serializing_if = "Option::is_none")]
371    pub memory_limit_mb: Option<u32>,
372}
373
374/// Logging configuration
375#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
376pub struct LoggingConfig {
377    /// Log level (debug, info, warn, error)
378    #[serde(default = "default_log_level")]
379    pub level: String,
380
381    /// Log format (json, text)
382    #[serde(default = "default_log_format")]
383    pub format: String,
384
385    /// Log file path (optional)
386    #[serde(skip_serializing_if = "Option::is_none")]
387    pub file: Option<String>,
388
389    /// Log rotation (daily, size-based, etc.)
390    #[serde(skip_serializing_if = "Option::is_none")]
391    pub rotation: Option<String>,
392}
393
394/// Build configuration
395#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
396pub struct BuildConfig {
397    /// Build target (release, debug)
398    #[serde(skip_serializing_if = "Option::is_none")]
399    pub target: Option<String>,
400
401    /// Features to enable
402    #[serde(skip_serializing_if = "Option::is_none")]
403    pub features: Option<Vec<String>>,
404
405    /// Build profile
406    #[serde(skip_serializing_if = "Option::is_none")]
407    pub profile: Option<String>,
408
409    /// Number of parallel build jobs
410    #[serde(skip_serializing_if = "Option::is_none")]
411    pub parallel_jobs: Option<u32>,
412}
413
414/// Test configuration
415#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
416pub struct TestConfig {
417    /// Test framework
418    #[serde(skip_serializing_if = "Option::is_none")]
419    pub framework: Option<String>,
420
421    /// Enable parallel test execution
422    #[serde(default)]
423    pub parallel: bool,
424
425    /// Test timeout in seconds
426    #[serde(skip_serializing_if = "Option::is_none")]
427    pub timeout_seconds: Option<u32>,
428
429    /// Enable code coverage
430    #[serde(default)]
431    pub coverage_enabled: bool,
432
433    /// Coverage threshold percentage
434    #[serde(skip_serializing_if = "Option::is_none")]
435    pub coverage_threshold: Option<u32>,
436}
437
438/// Package metadata (for marketplace packages)
439#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
440pub struct PackageMetadata {
441    /// Package name
442    pub name: String,
443
444    /// Package version
445    pub version: String,
446
447    /// Description
448    #[serde(skip_serializing_if = "Option::is_none")]
449    pub description: Option<String>,
450
451    /// Authors
452    #[serde(skip_serializing_if = "Option::is_none")]
453    pub authors: Option<Vec<String>>,
454
455    /// License
456    #[serde(skip_serializing_if = "Option::is_none")]
457    pub license: Option<String>,
458
459    /// Repository URL
460    #[serde(skip_serializing_if = "Option::is_none")]
461    pub repository: Option<String>,
462
463    /// Keywords
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub keywords: Option<Vec<String>>,
466
467    /// Categories
468    #[serde(skip_serializing_if = "Option::is_none")]
469    pub categories: Option<Vec<String>>,
470
471    /// Package-specific metadata
472    #[serde(skip_serializing_if = "Option::is_none")]
473    pub metadata: Option<HashMap<String, serde_json::Value>>,
474}
475
476// ============================================================================
477// Marketplace Configuration
478// Settings for marketplace package operations (install, publish)
479// ============================================================================
480
481/// Marketplace configuration
482///
483/// Controls behavior when interacting with the ggen marketplace:
484/// - FMEA validation during package installation
485/// - Quality gates for package publishing
486#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
487pub struct MarketplaceConfig {
488    /// Validate package FMEA during installation
489    #[serde(default)]
490    pub fmea_validation: bool,
491
492    /// Require packages to have [fmea] section in package.toml
493    #[serde(default)]
494    pub require_fmea: bool,
495
496    /// RPN threshold for critical failures (reject packages with unmitigated modes above this)
497    #[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// ============================================================================
512// FMEA Types (for package.toml, NOT ggen.toml)
513// These types are used when parsing marketplace package metadata
514// ============================================================================
515
516/// FMEA control entry (used in package.toml [fmea] section)
517///
518/// This struct is used for parsing marketplace packages, NOT for ggen.toml.
519/// FMEA documentation belongs in package metadata, not project configuration.
520#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
521pub struct FmeaControl {
522    /// Control identifier (e.g., "F1", "F2")
523    pub id: String,
524
525    /// Failure mode description
526    pub mode: String,
527
528    /// Severity score (1-10, where 10 = catastrophic)
529    #[serde(default = "default_fmea_score")]
530    pub severity: u8,
531
532    /// Occurrence likelihood (1-10, where 10 = very frequent)
533    #[serde(default = "default_fmea_score")]
534    pub occurrence: u8,
535
536    /// Detection capability (1-10, where 10 = undetectable)
537    #[serde(default = "default_fmea_score")]
538    pub detection: u8,
539
540    /// Control measure implemented (required for modes with RPN > threshold)
541    #[serde(skip_serializing_if = "Option::is_none")]
542    pub control: Option<String>,
543
544    /// Evidence of control effectiveness
545    #[serde(skip_serializing_if = "Option::is_none")]
546    pub evidence: Option<String>,
547}
548
549impl FmeaControl {
550    /// Calculate Risk Priority Number (Severity × Occurrence × Detection)
551    #[must_use]
552    pub fn rpn(&self) -> u16 {
553        u16::from(self.severity) * u16::from(self.occurrence) * u16::from(self.detection)
554    }
555
556    /// Check if this failure mode is mitigated (has control defined)
557    #[must_use]
558    pub fn is_mitigated(&self) -> bool {
559        self.control.is_some()
560    }
561
562    /// Check if this failure mode is critical and unmitigated
563    #[must_use]
564    pub fn is_critical_unmitigated(&self, threshold: u16) -> bool {
565        self.rpn() > threshold && !self.is_mitigated()
566    }
567}
568
569/// Package FMEA section (used in package.toml)
570///
571/// This is parsed from marketplace package.toml files, NOT ggen.toml.
572#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
573pub struct PackageFmeaSection {
574    /// Failure mode controls documented for this package
575    #[serde(default, skip_serializing_if = "Vec::is_empty")]
576    pub controls: Vec<FmeaControl>,
577}
578
579// ============================================================================
580// Generation Configuration (Path Protection & Poka-Yoke)
581// Prevents accidental overwrites of domain logic
582// ============================================================================
583
584/// Generation configuration with path protection
585///
586/// Implements Poka-Yoke (error-proofing) patterns to prevent
587/// accidental overwrites of domain logic during code generation.
588#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
589pub struct GenerationSafetyConfig {
590    /// Enable generation safety checks
591    #[serde(default = "default_true")]
592    pub enabled: bool,
593
594    /// Protected paths (glob patterns) - NEVER overwrite
595    #[serde(default, skip_serializing_if = "Vec::is_empty")]
596    pub protected_paths: Vec<String>,
597
598    /// Regeneratable paths (glob patterns) - safe to overwrite
599    #[serde(default, skip_serializing_if = "Vec::is_empty")]
600    pub regenerate_paths: Vec<String>,
601
602    /// Header to inject into generated files
603    #[serde(skip_serializing_if = "Option::is_none")]
604    pub generated_header: Option<String>,
605
606    /// Require confirmation before overwriting any file
607    #[serde(default)]
608    pub require_confirmation: bool,
609
610    /// Create backup before overwriting
611    #[serde(default = "default_true")]
612    pub backup_before_write: bool,
613
614    /// Poka-yoke controls
615    #[serde(skip_serializing_if = "Option::is_none")]
616    pub poka_yoke: Option<PokaYokeSettings>,
617}
618
619/// Poka-yoke (error-proofing) settings
620#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
621pub struct PokaYokeSettings {
622    /// Add warning headers to generated files
623    #[serde(default = "default_true")]
624    pub warning_headers: bool,
625
626    /// Add generated files to .gitignore
627    #[serde(default)]
628    pub gitignore_generated: bool,
629
630    /// Add generated files to .gitattributes (linguist-generated)
631    #[serde(default)]
632    pub gitattributes_generated: bool,
633
634    /// Validate imports don't cross boundaries
635    #[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// ============================================================================
667// CODEOWNERS Configuration
668// Aggregates team ownership from distributed OWNERS files
669// ============================================================================
670
671/// CODEOWNERS generation configuration
672///
673/// Aggregates team ownership from noun-level OWNERS files
674/// into a single .github/CODEOWNERS file for GitHub PR approval enforcement.
675#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
676pub struct CodeownersConfig {
677    /// Enable CODEOWNERS generation
678    #[serde(default)]
679    pub enabled: bool,
680
681    /// Source directories to scan for OWNERS files
682    #[serde(default, skip_serializing_if = "Vec::is_empty")]
683    pub source_dirs: Vec<String>,
684
685    /// Base directories to generate entries for
686    #[serde(default, skip_serializing_if = "Vec::is_empty")]
687    pub base_dirs: Vec<String>,
688
689    /// Output path (defaults to .github/CODEOWNERS)
690    #[serde(skip_serializing_if = "Option::is_none")]
691    pub output_path: Option<String>,
692
693    /// Auto-regenerate on noun changes
694    #[serde(default)]
695    pub auto_regenerate: bool,
696}
697
698// Default value functions for serde - Marketplace/FMEA
699fn default_critical_threshold() -> u16 {
700    200
701}
702
703fn default_fmea_score() -> u8 {
704    5
705}
706
707// Default value functions for serde
708fn 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}