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 {
106 return generate_type_example(
107 &resolved.kind,
108 max_depth,
109 schemas,
110 current_depth + 1,
111 indent_level,
112 );
113 }
114 let short_name = path.split("::").last().unwrap_or(path);
116 format!("/* {} */", short_name)
117 }
118 }
119}
120
121fn generate_variant_example(
122 variant: &Variant,
123 max_depth: usize,
124 schemas: &HashMap<&str, &Schema>,
125 current_depth: usize,
126 indent_level: usize,
127) -> String {
128 match &variant.kind {
129 VariantKind::Unit => variant.name.clone(),
130 VariantKind::Tuple(types) => {
131 let items: Vec<_> = types
132 .iter()
133 .map(|t| generate_type_example(t, max_depth, schemas, current_depth, indent_level))
134 .collect();
135 format!("{}({})", variant.name, items.join(", "))
136 }
137 VariantKind::Struct(fields) => {
138 let inner_indent = " ".repeat(indent_level + 1);
139 let field_examples: Vec<_> = fields
140 .iter()
141 .filter(|f| !f.optional)
142 .map(|f| {
143 let value = generate_type_example(
144 &f.ty,
145 max_depth,
146 schemas,
147 current_depth + 1,
148 indent_level + 1,
149 );
150 format!("{}{}: {}", inner_indent, f.name, value)
151 })
152 .collect();
153
154 if field_examples.is_empty() {
155 format!("{} {{}}", variant.name)
156 } else {
157 let indent = " ".repeat(indent_level);
158 format!(
159 "{}(\n{},\n{})",
160 variant.name,
161 field_examples.join(",\n"),
162 indent
163 )
164 }
165 }
166 }
167}
168
169pub fn build_schema_map(schemas: &[DiscoveredSchema]) -> HashMap<&str, &Schema> {
171 schemas
172 .iter()
173 .map(|s| (s.type_path.as_str(), &s.schema))
174 .collect()
175}
176
177#[cfg(test)]
178mod tests {
179 use ahash::HashMapExt;
180 use ron2::schema::Field;
181
182 use super::*;
183
184 #[test]
185 fn test_generate_primitive_examples() {
186 let schemas = HashMap::new();
187 assert_eq!(
188 generate_type_example(&TypeKind::Bool, 2, &schemas, 0, 0),
189 "true"
190 );
191 assert_eq!(
192 generate_type_example(&TypeKind::String, 2, &schemas, 0, 0),
193 "\"...\""
194 );
195 assert_eq!(
196 generate_type_example(&TypeKind::I32, 2, &schemas, 0, 0),
197 "0"
198 );
199 }
200
201 #[test]
202 fn test_generate_compound_examples() {
203 let schemas = HashMap::new();
204
205 let option_string = TypeKind::Option(Box::new(TypeKind::String));
206 assert_eq!(
207 generate_type_example(&option_string, 2, &schemas, 0, 0),
208 "Some(\"...\")"
209 );
210
211 let list_i32 = TypeKind::List(Box::new(TypeKind::I32));
212 assert_eq!(generate_type_example(&list_i32, 2, &schemas, 0, 0), "[0]");
213 }
214
215 #[test]
216 fn test_generate_struct_example() {
217 let schemas = HashMap::new();
218
219 let struct_kind = TypeKind::Struct {
220 fields: vec![
221 Field::new("name", TypeKind::String),
222 Field::new("age", TypeKind::I32),
223 ],
224 };
225
226 let example = generate_type_example(&struct_kind, 2, &schemas, 0, 0);
227 assert!(example.contains("name: \"...\""));
228 assert!(example.contains("age: 0"));
229 }
230
231 #[test]
232 fn test_generate_enum_example() {
233 let schemas = HashMap::new();
234
235 let enum_kind = TypeKind::Enum {
236 variants: vec![
237 Variant::unit("Low"),
238 Variant::unit("Medium"),
239 Variant::unit("High"),
240 ],
241 };
242
243 let example = generate_type_example(&enum_kind, 2, &schemas, 0, 0);
244 assert_eq!(example, "Low");
245 }
246}