1use ahash::HashMap;
4use ron2::schema::{Schema, TypeKind, Variant, VariantKind};
5
6use crate::discovery::DiscoveredSchema;
7
8pub fn generate_example(
10 schema: &Schema,
11 max_depth: usize,
12 schemas: &HashMap<&str, &Schema>,
13) -> String {
14 generate_type_example(&schema.kind, max_depth, schemas, 0, 0)
15}
16
17fn generate_type_example(
18 kind: &TypeKind,
19 max_depth: usize,
20 schemas: &HashMap<&str, &Schema>,
21 current_depth: usize,
22 indent_level: usize,
23) -> String {
24 let indent = " ".repeat(indent_level);
25 let inner_indent = " ".repeat(indent_level + 1);
26
27 match kind {
28 TypeKind::Bool => "true".to_string(),
30 TypeKind::String => "\"...\"".to_string(),
31 TypeKind::Char => "'?'".to_string(),
32 TypeKind::I8 | TypeKind::I16 | TypeKind::I32 | TypeKind::I64 | TypeKind::I128 => {
33 "0".to_string()
34 }
35 TypeKind::U8 | TypeKind::U16 | TypeKind::U32 | TypeKind::U64 | TypeKind::U128 => {
36 "0".to_string()
37 }
38 TypeKind::F32 | TypeKind::F64 => "0.0".to_string(),
39 TypeKind::Unit => "()".to_string(),
40
41 TypeKind::Option(inner) => {
43 let inner_example =
44 generate_type_example(inner, max_depth, schemas, current_depth, indent_level);
45 format!("Some({})", inner_example)
46 }
47 TypeKind::List(inner) => {
48 let inner_example =
49 generate_type_example(inner, max_depth, schemas, current_depth, indent_level);
50 format!("[{}]", inner_example)
51 }
52 TypeKind::Map { key, value } => {
53 let key_example =
54 generate_type_example(key, max_depth, schemas, current_depth, indent_level);
55 let value_example =
56 generate_type_example(value, max_depth, schemas, current_depth, indent_level);
57 format!("{{ {}: {} }}", key_example, value_example)
58 }
59 TypeKind::Tuple(types) => {
60 let items: Vec<_> = types
61 .iter()
62 .map(|t| generate_type_example(t, max_depth, schemas, current_depth, indent_level))
63 .collect();
64 format!("({})", items.join(", "))
65 }
66
67 TypeKind::Struct { fields } => {
69 let required_fields: Vec<_> = fields.iter().filter(|f| !f.optional).collect();
70
71 if required_fields.is_empty() {
72 return "()".to_string();
73 }
74
75 let field_examples: Vec<_> = required_fields
76 .iter()
77 .map(|f| {
78 let value = generate_type_example(
79 &f.ty,
80 max_depth,
81 schemas,
82 current_depth + 1,
83 indent_level + 1,
84 );
85 format!("{}{}: {}", inner_indent, f.name, value)
86 })
87 .collect();
88
89 format!("(\n{},\n{})", field_examples.join(",\n"), indent)
90 }
91
92 TypeKind::Enum { variants } => {
94 if let Some(variant) = variants.first() {
95 generate_variant_example(variant, max_depth, schemas, current_depth, indent_level)
96 } else {
97 "/* no variants */".to_string()
98 }
99 }
100
101 TypeKind::TypeRef(path) => {
103 if current_depth < max_depth
104 && let Some(resolved) = schemas.get(path.as_str()) {
105 return generate_type_example(
106 &resolved.kind,
107 max_depth,
108 schemas,
109 current_depth + 1,
110 indent_level,
111 );
112 }
113 let short_name = path.split("::").last().unwrap_or(path);
115 format!("/* {} */", short_name)
116 }
117 }
118}
119
120fn generate_variant_example(
121 variant: &Variant,
122 max_depth: usize,
123 schemas: &HashMap<&str, &Schema>,
124 current_depth: usize,
125 indent_level: usize,
126) -> String {
127 match &variant.kind {
128 VariantKind::Unit => variant.name.clone(),
129 VariantKind::Tuple(types) => {
130 let items: Vec<_> = types
131 .iter()
132 .map(|t| generate_type_example(t, max_depth, schemas, current_depth, indent_level))
133 .collect();
134 format!("{}({})", variant.name, items.join(", "))
135 }
136 VariantKind::Struct(fields) => {
137 let inner_indent = " ".repeat(indent_level + 1);
138 let field_examples: Vec<_> = fields
139 .iter()
140 .filter(|f| !f.optional)
141 .map(|f| {
142 let value = generate_type_example(
143 &f.ty,
144 max_depth,
145 schemas,
146 current_depth + 1,
147 indent_level + 1,
148 );
149 format!("{}{}: {}", inner_indent, f.name, value)
150 })
151 .collect();
152
153 if field_examples.is_empty() {
154 format!("{} {{}}", variant.name)
155 } else {
156 let indent = " ".repeat(indent_level);
157 format!(
158 "{}(\n{},\n{})",
159 variant.name,
160 field_examples.join(",\n"),
161 indent
162 )
163 }
164 }
165 }
166}
167
168pub fn build_schema_map(schemas: &[DiscoveredSchema]) -> HashMap<&str, &Schema> {
170 schemas
171 .iter()
172 .map(|s| (s.type_path.as_str(), &s.schema))
173 .collect()
174}
175
176#[cfg(test)]
177mod tests {
178 use ahash::HashMapExt;
179 use ron2::schema::Field;
180
181 use super::*;
182
183 #[test]
184 fn test_generate_primitive_examples() {
185 let schemas = HashMap::new();
186 assert_eq!(
187 generate_type_example(&TypeKind::Bool, 2, &schemas, 0, 0),
188 "true"
189 );
190 assert_eq!(
191 generate_type_example(&TypeKind::String, 2, &schemas, 0, 0),
192 "\"...\""
193 );
194 assert_eq!(
195 generate_type_example(&TypeKind::I32, 2, &schemas, 0, 0),
196 "0"
197 );
198 }
199
200 #[test]
201 fn test_generate_compound_examples() {
202 let schemas = HashMap::new();
203
204 let option_string = TypeKind::Option(Box::new(TypeKind::String));
205 assert_eq!(
206 generate_type_example(&option_string, 2, &schemas, 0, 0),
207 "Some(\"...\")"
208 );
209
210 let list_i32 = TypeKind::List(Box::new(TypeKind::I32));
211 assert_eq!(generate_type_example(&list_i32, 2, &schemas, 0, 0), "[0]");
212 }
213
214 #[test]
215 fn test_generate_struct_example() {
216 let schemas = HashMap::new();
217
218 let struct_kind = TypeKind::Struct {
219 fields: vec![
220 Field::new("name", TypeKind::String),
221 Field::new("age", TypeKind::I32),
222 ],
223 };
224
225 let example = generate_type_example(&struct_kind, 2, &schemas, 0, 0);
226 assert!(example.contains("name: \"...\""));
227 assert!(example.contains("age: 0"));
228 }
229
230 #[test]
231 fn test_generate_enum_example() {
232 let schemas = HashMap::new();
233
234 let enum_kind = TypeKind::Enum {
235 variants: vec![
236 Variant::unit("Low"),
237 Variant::unit("Medium"),
238 Variant::unit("High"),
239 ],
240 };
241
242 let example = generate_type_example(&enum_kind, 2, &schemas, 0, 0);
243 assert_eq!(example, "Low");
244 }
245}