1use crate::ast::*;
4
5pub fn generate_rust(node: &SchemaNode, module_name: &str, fn_name: &str) -> Result<String, String> {
7 let mut b = String::new();
8
9 b.push_str(&format!("//! Generated schema for {}.\n\n", node.name));
10 b.push_str("use pflow_tokenmodel::schema::*;\n\n");
11
12 b.push_str(&format!(
13 "/// {} creates a schema from DSL definition.\n",
14 fn_name
15 ));
16 b.push_str(&format!("pub fn {}() -> Schema {{\n", fn_name));
17
18 b.push_str(&format!(
19 " let mut schema = Schema::new({:?});\n",
20 node.name
21 ));
22 if !node.version.is_empty() {
23 b.push_str(&format!(
24 " schema.version = {:?}.into();\n",
25 node.version
26 ));
27 }
28 b.push('\n');
29
30 if !node.states.is_empty() {
32 b.push_str(" // States\n");
33 for s in &node.states {
34 b.push_str(" schema.add_state(State {\n");
35 b.push_str(&format!(" id: {:?}.into(),\n", s.id));
36 if s.kind == "token" {
37 b.push_str(" kind: Kind::Token,\n");
38 } else {
39 b.push_str(" kind: Kind::Data,\n");
40 }
41 if !s.typ.is_empty() {
42 b.push_str(&format!(" typ: {:?}.into(),\n", s.typ));
43 } else {
44 b.push_str(" typ: String::new(),\n");
45 }
46 match &s.initial {
47 Some(InitialValue::Int(n)) => {
48 b.push_str(&format!(
49 " initial: Some(serde_json::Value::Number({}.into())),\n",
50 n
51 ));
52 }
53 Some(InitialValue::Str(v)) => {
54 b.push_str(&format!(
55 " initial: Some(serde_json::Value::String({:?}.into())),\n",
56 v
57 ));
58 }
59 _ => {
60 b.push_str(" initial: None,\n");
61 }
62 }
63 b.push_str(&format!(" exported: {},\n", s.exported));
64 b.push_str(" });\n");
65 }
66 b.push('\n');
67 }
68
69 if !node.actions.is_empty() {
71 b.push_str(" // Actions\n");
72 for a in &node.actions {
73 b.push_str(" schema.add_action(Action {\n");
74 b.push_str(&format!(" id: {:?}.into(),\n", a.id));
75 if !a.guard.is_empty() {
76 b.push_str(&format!(" guard: {:?}.into(),\n", a.guard));
77 } else {
78 b.push_str(" guard: String::new(),\n");
79 }
80 b.push_str(" event_id: String::new(),\n");
81 b.push_str(" event_bindings: None,\n");
82 b.push_str(" });\n");
83 }
84 b.push('\n');
85 }
86
87 if !node.arcs.is_empty() {
89 b.push_str(" // Arcs\n");
90 for a in &node.arcs {
91 b.push_str(" schema.add_arc(Arc {\n");
92 b.push_str(&format!(" source: {:?}.into(),\n", a.source));
93 b.push_str(&format!(" target: {:?}.into(),\n", a.target));
94 if !a.keys.is_empty() {
95 let keys: Vec<String> = a.keys.iter().map(|k| format!("{:?}.into()", k)).collect();
96 b.push_str(&format!(" keys: vec![{}],\n", keys.join(", ")));
97 } else {
98 b.push_str(" keys: vec![],\n");
99 }
100 if !a.value.is_empty() {
101 b.push_str(&format!(" value: {:?}.into(),\n", a.value));
102 } else {
103 b.push_str(" value: String::new(),\n");
104 }
105 b.push_str(" });\n");
106 }
107 b.push('\n');
108 }
109
110 if !node.constraints.is_empty() {
112 b.push_str(" // Constraints\n");
113 for c in &node.constraints {
114 b.push_str(" schema.add_constraint(Constraint {\n");
115 b.push_str(&format!(" id: {:?}.into(),\n", c.id));
116 b.push_str(&format!(" expr: {:?}.into(),\n", c.expr));
117 b.push_str(" });\n");
118 }
119 b.push('\n');
120 }
121
122 b.push_str(" schema\n");
123 b.push_str("}\n");
124
125 let _ = module_name; Ok(b)
127}
128
129pub fn generate_rust_from_dsl(
131 input: &str,
132 module_name: &str,
133 fn_name: &str,
134) -> Result<String, String> {
135 let node = crate::parser::parse(input)?;
136 generate_rust(&node, module_name, fn_name)
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142
143 #[test]
144 fn test_codegen_basic() {
145 let input = r#"(schema test
146 (version v1.0.0)
147 (states
148 (state count :kind token :initial 5)
149 )
150 (actions
151 (action inc)
152 )
153 (arcs
154 (arc inc -> count)
155 )
156)"#;
157
158 let code = generate_rust_from_dsl(input, "test", "make_schema").unwrap();
159 assert!(code.contains("pub fn make_schema()"));
160 assert!(code.contains("Schema::new"));
161 assert!(code.contains("Kind::Token"));
162 assert!(code.contains("\"count\""));
163 }
164}