hemmer_provider_generator_parser/
rustdoc_loader.rs1use hemmer_provider_generator_common::{FieldDefinition, GeneratorError, Result};
6use rustdoc_types::{Crate, Id, ItemEnum, Type};
7use std::path::Path;
8
9pub struct RustdocLoader;
16
17impl RustdocLoader {
18 pub fn load_from_file(path: &Path) -> Result<Crate> {
20 let content = std::fs::read_to_string(path).map_err(|e| {
21 GeneratorError::Parse(format!("Failed to read rustdoc JSON file: {}", e))
22 })?;
23
24 let crate_data: Crate = serde_json::from_str(&content)
25 .map_err(|e| GeneratorError::Parse(format!("Failed to parse rustdoc JSON: {}", e)))?;
26
27 Ok(crate_data)
28 }
29
30 pub fn find_operation_modules(crate_data: &Crate) -> Vec<String> {
34 let mut operations = Vec::new();
35
36 let root_id = &crate_data.root;
38 if let Some(root_item) = crate_data.index.get(root_id) {
39 if let rustdoc_types::ItemEnum::Module(module) = &root_item.inner {
41 for item_id in &module.items {
42 if let Some(item) = crate_data.index.get(item_id) {
43 if let Some(name) = &item.name {
44 if name == "operation" {
45 if let rustdoc_types::ItemEnum::Module(op_module) = &item.inner {
47 for op_id in &op_module.items {
48 if let Some(op_item) = crate_data.index.get(op_id) {
49 if let Some(op_name) = &op_item.name {
50 operations.push(op_name.clone());
51 }
52 }
53 }
54 }
55 }
56 }
57 }
58 }
59 }
60 }
61
62 operations
63 }
64
65 pub fn find_type_modules(crate_data: &Crate) -> Vec<String> {
67 let mut types = Vec::new();
68
69 let root_id = &crate_data.root;
70 if let Some(root_item) = crate_data.index.get(root_id) {
71 if let rustdoc_types::ItemEnum::Module(module) = &root_item.inner {
72 for item_id in &module.items {
73 if let Some(item) = crate_data.index.get(item_id) {
74 if let Some(name) = &item.name {
75 if name == "types" {
76 if let rustdoc_types::ItemEnum::Module(types_module) = &item.inner {
77 for type_id in &types_module.items {
78 if let Some(type_item) = crate_data.index.get(type_id) {
79 if let Some(type_name) = &type_item.name {
80 types.push(type_name.clone());
81 }
82 }
83 }
84 }
85 }
86 }
87 }
88 }
89 }
90 }
91
92 types
93 }
94
95 pub fn extract_struct_fields(crate_data: &Crate, struct_name: &str) -> Vec<FieldDefinition> {
100 let struct_item = crate_data
102 .index
103 .values()
104 .find(|item| item.name.as_deref() == Some(struct_name));
105
106 let struct_item = match struct_item {
107 Some(item) => item,
108 None => return vec![],
109 };
110
111 if let ItemEnum::Struct(struct_data) = &struct_item.inner {
113 match &struct_data.kind {
114 rustdoc_types::StructKind::Plain { fields, .. } => {
115 return fields
116 .iter()
117 .filter_map(|field_id| Self::extract_field_definition(crate_data, field_id))
118 .collect();
119 },
120 _ => return vec![],
121 }
122 }
123
124 vec![]
125 }
126
127 fn extract_field_definition(crate_data: &Crate, field_id: &Id) -> Option<FieldDefinition> {
129 let field_item = crate_data.index.get(field_id)?;
130 let field_name = field_item.name.as_ref()?.clone();
131
132 if let ItemEnum::StructField(field_type) = &field_item.inner {
134 let (field_type_mapped, required) =
135 Self::map_rustdoc_type_to_field_type(crate_data, field_type);
136
137 Some(FieldDefinition {
138 name: field_name.clone(),
139 field_type: field_type_mapped,
140 required,
141 sensitive: crate::TypeMapper::is_sensitive(&field_name),
142 immutable: crate::TypeMapper::is_immutable(&field_name),
143 description: field_item.docs.clone(),
144 response_accessor: Some(field_name),
146 })
147 } else {
148 None
149 }
150 }
151
152 #[allow(clippy::only_used_in_recursion)]
156 fn map_rustdoc_type_to_field_type(
157 crate_data: &Crate,
158 rustdoc_type: &Type,
159 ) -> (hemmer_provider_generator_common::FieldType, bool) {
160 use hemmer_provider_generator_common::FieldType;
161
162 match rustdoc_type {
163 Type::ResolvedPath(path) => {
164 let type_name = path.path.rsplit("::").next().unwrap_or(&path.path);
166
167 if type_name == "Option" {
169 if let Some(generic_args) = &path.args {
170 if let rustdoc_types::GenericArgs::AngleBracketed { args, .. } =
171 generic_args.as_ref()
172 {
173 if let Some(rustdoc_types::GenericArg::Type(inner_type)) = args.first()
174 {
175 let (inner_field_type, _) =
176 Self::map_rustdoc_type_to_field_type(crate_data, inner_type);
177 return (inner_field_type, false); }
179 }
180 }
181 return (FieldType::String, false);
182 }
183
184 if type_name == "Vec" {
186 if let Some(generic_args) = &path.args {
187 if let rustdoc_types::GenericArgs::AngleBracketed { args, .. } =
188 generic_args.as_ref()
189 {
190 if let Some(rustdoc_types::GenericArg::Type(inner_type)) = args.first()
191 {
192 let (inner_field_type, _) =
193 Self::map_rustdoc_type_to_field_type(crate_data, inner_type);
194 return (FieldType::List(Box::new(inner_field_type)), true);
195 }
196 }
197 }
198 return (FieldType::List(Box::new(FieldType::String)), true);
199 }
200
201 if type_name == "HashMap" {
203 if let Some(generic_args) = &path.args {
204 if let rustdoc_types::GenericArgs::AngleBracketed { args, .. } =
205 generic_args.as_ref()
206 {
207 if args.len() >= 2 {
208 let key_type = if let Some(rustdoc_types::GenericArg::Type(k)) =
209 args.first()
210 {
211 let (kt, _) =
212 Self::map_rustdoc_type_to_field_type(crate_data, k);
213 kt
214 } else {
215 FieldType::String
216 };
217
218 let value_type =
219 if let Some(rustdoc_types::GenericArg::Type(v)) = args.get(1) {
220 let (vt, _) =
221 Self::map_rustdoc_type_to_field_type(crate_data, v);
222 vt
223 } else {
224 FieldType::String
225 };
226
227 return (
228 FieldType::Map(Box::new(key_type), Box::new(value_type)),
229 true,
230 );
231 }
232 }
233 }
234 return (
235 FieldType::Map(Box::new(FieldType::String), Box::new(FieldType::String)),
236 true,
237 );
238 }
239
240 (crate::TypeMapper::map_type(type_name), true)
242 },
243 Type::Primitive(prim) => (crate::TypeMapper::map_type(prim), true),
244 _ => (FieldType::String, true), }
246 }
247}
248
249#[cfg(test)]
250mod tests {
251 #[test]
252 fn test_rustdoc_loader_api() {
253 }
256}