Skip to main content

ryo_app/spec_dsl/
types.rs

1//! Type definitions for Spec DSL
2//!
3//! These types map directly to the YAML schema.
4
5use serde::{Deserialize, Serialize};
6
7/// Root specification for domain-driven construction
8#[derive(Debug, Clone, Serialize, Deserialize)]
9pub struct DomainSpec {
10    /// Project metadata
11    pub project: ProjectSpec,
12
13    /// Module structure definitions
14    #[serde(default)]
15    pub modules: Vec<ModuleSpec>,
16
17    /// Common types (newtypes, value objects)
18    #[serde(default)]
19    pub common_types: Option<CommonTypesSpec>,
20
21    /// Domain entities
22    #[serde(default)]
23    pub entities: Vec<EntitySpec>,
24
25    /// Error type definitions
26    #[serde(default)]
27    pub errors: Vec<ErrorSpec>,
28
29    /// Implementation blocks
30    #[serde(default)]
31    pub implementations: Vec<ImplSpec>,
32
33    /// Refactoring steps
34    #[serde(default)]
35    pub refactors: Vec<RefactorSpec>,
36
37    /// Verification points
38    #[serde(default)]
39    pub verification: Option<VerificationSpec>,
40}
41
42/// Project metadata
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct ProjectSpec {
45    /// Project name
46    pub name: String,
47
48    /// Crate name (defaults to project name with underscores)
49    #[serde(default)]
50    pub crate_name: Option<String>,
51}
52
53impl ProjectSpec {
54    pub fn crate_name(&self) -> String {
55        self.crate_name
56            .clone()
57            .unwrap_or_else(|| self.name.replace('-', "_"))
58    }
59}
60
61/// Module definition
62#[derive(Debug, Clone, Serialize, Deserialize)]
63pub struct ModuleSpec {
64    /// Module name
65    pub name: String,
66
67    /// Is public
68    #[serde(default)]
69    pub is_pub: bool,
70
71    /// Child modules
72    #[serde(default)]
73    pub children: Vec<ModuleSpec>,
74}
75
76impl ModuleSpec {
77    /// Flatten module tree into (path, is_pub) pairs
78    pub fn flatten(&self, parent_path: &str) -> Vec<(String, bool)> {
79        let mut result = Vec::new();
80        let path = if parent_path.is_empty() {
81            self.name.clone()
82        } else {
83            format!("{}::{}", parent_path, self.name)
84        };
85
86        result.push((path.clone(), self.is_pub));
87
88        for child in &self.children {
89            result.extend(child.flatten(&path));
90        }
91
92        result
93    }
94}
95
96/// Common types specification
97#[derive(Debug, Clone, Default, Serialize, Deserialize)]
98pub struct CommonTypesSpec {
99    /// Newtype definitions
100    #[serde(default)]
101    pub newtypes: Vec<NewtypeSpec>,
102
103    /// Value object definitions
104    #[serde(default)]
105    pub value_objects: Vec<EntitySpec>,
106}
107
108/// Newtype definition
109#[derive(Debug, Clone, Serialize, Deserialize)]
110pub struct NewtypeSpec {
111    /// Type name
112    pub name: String,
113
114    /// Inner type
115    pub inner: String,
116
117    /// Target module
118    pub module: String,
119
120    /// Derive macros
121    #[serde(default)]
122    pub derives: Vec<String>,
123}
124
125/// Entity (struct/enum) definition
126#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct EntitySpec {
128    /// Entity name
129    pub name: String,
130
131    /// Target module
132    #[serde(default)]
133    pub module: String,
134
135    /// Kind (struct or enum)
136    #[serde(default)]
137    pub kind: EntityKind,
138
139    /// Fields (for structs)
140    #[serde(default)]
141    pub fields: Vec<FieldSpec>,
142
143    /// Variants (for enums)
144    #[serde(default)]
145    pub variants: Vec<VariantSpec>,
146
147    /// Derive macros
148    #[serde(default)]
149    pub derives: Vec<String>,
150}
151
152/// Entity kind
153#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize)]
154#[serde(rename_all = "lowercase")]
155pub enum EntityKind {
156    #[default]
157    Struct,
158    Enum,
159    Newtype,
160}
161
162/// Field definition
163#[derive(Debug, Clone, Serialize, Deserialize)]
164pub struct FieldSpec {
165    /// Field name
166    pub name: String,
167
168    /// Field type
169    #[serde(rename = "type")]
170    pub ty: String,
171
172    /// Is public
173    #[serde(default)]
174    pub is_pub: bool,
175}
176
177/// Variant definition (for enums)
178#[derive(Debug, Clone, Serialize, Deserialize)]
179#[serde(untagged)]
180pub enum VariantSpec {
181    /// Simple unit variant: "Active"
182    Unit(String),
183
184    /// Complex variant with type: { name: "NotFound", type: "struct:entity:String" }
185    Complex {
186        name: String,
187        #[serde(rename = "type", default)]
188        variant_type: Option<String>,
189    },
190}
191
192impl VariantSpec {
193    pub fn name(&self) -> &str {
194        match self {
195            VariantSpec::Unit(name) => name,
196            VariantSpec::Complex { name, .. } => name,
197        }
198    }
199
200    pub fn variant_type(&self) -> Option<&str> {
201        match self {
202            VariantSpec::Unit(_) => None,
203            VariantSpec::Complex { variant_type, .. } => variant_type.as_deref(),
204        }
205    }
206}
207
208/// Error type definition
209#[derive(Debug, Clone, Serialize, Deserialize)]
210pub struct ErrorSpec {
211    /// Error type name
212    pub name: String,
213
214    /// Target module
215    pub module: String,
216
217    /// Kind (always enum for errors)
218    #[serde(default)]
219    pub kind: EntityKind,
220
221    /// Variants
222    #[serde(default)]
223    pub variants: Vec<VariantSpec>,
224
225    /// Derive macros
226    #[serde(default)]
227    pub derives: Vec<String>,
228}
229
230/// Implementation block definition
231#[derive(Debug, Clone, Serialize, Deserialize)]
232pub struct ImplSpec {
233    /// Target type
234    pub target: String,
235
236    /// Trait to implement (None for inherent impl)
237    #[serde(default)]
238    pub trait_name: Option<String>,
239
240    /// Methods
241    #[serde(default)]
242    pub methods: Vec<MethodSpec>,
243}
244
245/// Method definition
246#[derive(Debug, Clone, Serialize, Deserialize)]
247pub struct MethodSpec {
248    /// Method name
249    pub name: String,
250
251    /// Self parameter: "ref", "mut", "owned", or null
252    #[serde(default)]
253    pub self_param: Option<SelfParamSpec>,
254
255    /// Parameters: [[name, type], ...]
256    #[serde(default)]
257    pub params: Vec<(String, String)>,
258
259    /// Return type (None for unit)
260    #[serde(default)]
261    pub return_type: Option<String>,
262
263    /// Method body
264    #[serde(default = "default_body")]
265    pub body: String,
266
267    /// Is public
268    #[serde(default)]
269    pub is_pub: bool,
270}
271
272fn default_body() -> String {
273    "todo!()".to_string()
274}
275
276/// Self parameter kind
277#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
278#[serde(rename_all = "lowercase")]
279pub enum SelfParamSpec {
280    Ref,
281    Mut,
282    Owned,
283}
284
285/// Refactoring step definition
286#[derive(Debug, Clone, Serialize, Deserialize)]
287#[serde(tag = "kind")]
288pub enum RefactorSpec {
289    /// Add builder pattern
290    AddBuilderPattern { targets: Vec<String> },
291
292    /// Add From/Into implementations
293    AddFromInto { pairs: Vec<(String, String)> },
294
295    /// Add Default implementation
296    AddDefault { targets: Vec<String> },
297
298    /// Organize imports
299    OrganizeImports {
300        #[serde(default)]
301        target_modules: TargetModules,
302    },
303}
304
305/// Target modules specification
306#[derive(Debug, Clone, Default, Serialize)]
307pub enum TargetModules {
308    #[default]
309    All,
310    List(Vec<String>),
311}
312
313impl<'de> Deserialize<'de> for TargetModules {
314    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
315    where
316        D: serde::Deserializer<'de>,
317    {
318        use serde::de::{self, Visitor};
319
320        struct TargetModulesVisitor;
321
322        impl<'de> Visitor<'de> for TargetModulesVisitor {
323            type Value = TargetModules;
324
325            fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
326                formatter.write_str(r#""all" or a list of module names"#)
327            }
328
329            fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
330            where
331                E: de::Error,
332            {
333                if v == "all" {
334                    Ok(TargetModules::All)
335                } else {
336                    Err(de::Error::custom(format!(
337                        "expected 'all' or a list, got '{}'",
338                        v
339                    )))
340                }
341            }
342
343            fn visit_seq<A>(self, seq: A) -> Result<Self::Value, A::Error>
344            where
345                A: de::SeqAccess<'de>,
346            {
347                let modules = Vec::deserialize(de::value::SeqAccessDeserializer::new(seq))?;
348                Ok(TargetModules::List(modules))
349            }
350
351            fn visit_unit<E>(self) -> Result<Self::Value, E>
352            where
353                E: de::Error,
354            {
355                Ok(TargetModules::All)
356            }
357        }
358
359        deserializer.deserialize_any(TargetModulesVisitor)
360    }
361}
362
363impl TargetModules {
364    pub fn is_all(&self) -> bool {
365        matches!(self, TargetModules::All)
366    }
367}
368
369/// Verification specification
370#[derive(Debug, Clone, Default, Serialize, Deserialize)]
371pub struct VerificationSpec {
372    /// After Phase 1
373    #[serde(default)]
374    pub phase1: Vec<VerificationPointSpec>,
375
376    /// After Phase 2
377    #[serde(default)]
378    pub phase2: Vec<VerificationPointSpec>,
379
380    /// After Phase 3
381    #[serde(default)]
382    pub phase3: Vec<VerificationPointSpec>,
383
384    /// After Phase 4
385    #[serde(default)]
386    pub phase4: Vec<VerificationPointSpec>,
387
388    /// After Phase 5
389    #[serde(default)]
390    pub phase5: Vec<VerificationPointSpec>,
391
392    /// After Phase 6
393    #[serde(default)]
394    pub phase6: Vec<VerificationPointSpec>,
395
396    /// Final verification
397    #[serde(default, rename = "final")]
398    pub final_: Vec<VerificationPointSpec>,
399}
400
401/// Verification point definition
402#[derive(Debug, Clone, Serialize, Deserialize)]
403#[serde(tag = "kind")]
404pub enum VerificationPointSpec {
405    /// Check module exists
406    ModuleExists { paths: Vec<String> },
407
408    /// Check type exists
409    TypeExists { types: Vec<String> },
410
411    /// Check type has specific derive
412    HasDerive {
413        #[serde(rename = "type")]
414        ty: String,
415        derives: Vec<String>,
416    },
417
418    /// Check struct has specific field
419    HasField {
420        #[serde(rename = "type")]
421        ty: String,
422        field: String,
423        field_type: String,
424    },
425
426    /// Check type has specific method
427    MethodExists {
428        #[serde(rename = "type")]
429        ty: String,
430        method: String,
431    },
432
433    /// Check compilation
434    Compiles,
435
436    /// Check no warnings (except allowed)
437    NoWarnings {
438        #[serde(default)]
439        allowed: Vec<String>,
440    },
441}
442
443#[cfg(test)]
444mod tests {
445    use super::*;
446
447    #[test]
448    fn test_module_flatten() {
449        let module = ModuleSpec {
450            name: "lib".to_string(),
451            is_pub: true,
452            children: vec![
453                ModuleSpec {
454                    name: "user".to_string(),
455                    is_pub: true,
456                    children: vec![],
457                },
458                ModuleSpec {
459                    name: "common".to_string(),
460                    is_pub: true,
461                    children: vec![ModuleSpec {
462                        name: "types".to_string(),
463                        is_pub: true,
464                        children: vec![],
465                    }],
466                },
467            ],
468        };
469
470        let flattened = module.flatten("");
471        assert_eq!(flattened.len(), 4);
472        assert!(flattened.iter().any(|(p, _)| p == "lib"));
473        assert!(flattened.iter().any(|(p, _)| p == "lib::user"));
474        assert!(flattened.iter().any(|(p, _)| p == "lib::common"));
475        assert!(flattened.iter().any(|(p, _)| p == "lib::common::types"));
476    }
477
478    #[test]
479    fn test_variant_spec_parsing() {
480        // Unit variant
481        let yaml = r#""Active""#;
482        let variant: VariantSpec = serde_yaml::from_str(yaml).unwrap();
483        assert_eq!(variant.name(), "Active");
484        assert!(variant.variant_type().is_none());
485
486        // Complex variant
487        let yaml = r#"{ name: "NotFound", type: "struct:entity:String" }"#;
488        let variant: VariantSpec = serde_yaml::from_str(yaml).unwrap();
489        assert_eq!(variant.name(), "NotFound");
490        assert_eq!(variant.variant_type(), Some("struct:entity:String"));
491    }
492}