1use std::path::Path;
2
3use anyhow::{ Context as _, Result};
4
5use crate::{Context, FieldUnit, FileUnit, StructUnit};
6
7pub fn process_path<S: AsRef<Path>>(context: &mut Context, p: S) -> Result<()> {
8 let path = p.as_ref();
9 if path.is_file() && path.extension().map_or(false, |ext| ext == "rs") {
10
11 let code = std::fs::read_to_string(path)?;
12 let unit = process_code(code).with_context(|| format!("failed to parse file: {:?}", path))?;
13 context.files.push(unit);
14 } else if path.is_dir() {
15 for entry in std::fs::read_dir(path)? {
16 let entry = entry?;
17 let entry_path = entry.path();
18 if entry_path.is_file() {
19 process_path(context, entry.path())?;
20 } else if entry_path.is_dir() {
21 process_path(context, entry.path())?;
22 }
23 }
24 }
25 Ok(())
26}
27
28fn process_code<S: AsRef<str>>(code: S) -> Result<FileUnit> {
29 let tree: syn::File = syn::parse_str(code.as_ref()).context("failed to parse code")?;
30 let mut unit = FileUnit::new();
31 for item in &tree.items {
32 if let syn::Item::Struct(item_struct) = item {
33 unit.structs.push(process_struct(item_struct).context("failed to parse struct")?);
34 }
35 }
36 Ok(unit)
37}
38
39fn process_struct(item_struct: &syn::ItemStruct) -> Result<StructUnit> {
40 let mut struct_unit = StructUnit::new(
41 item_struct.ident.to_string(),
42 );
43
44 if let Some(comment) = item_struct.attrs.iter().find(|attr| attr.path().is_ident("doc")) {
45 if let Ok(name) = comment.meta.require_name_value() {
46 if let syn::Expr::Lit(expr_list) = &name.value {
47 if let syn::Lit::Str(lit) = &expr_list.lit {
48 struct_unit.doc = Some(lit.value());
49 }
50 }
51 }
52 }
53
54 if let syn::Fields::Named(fields) = &item_struct.fields {
55 for field in &fields.named {
56 let name = field.ident.as_ref().unwrap().to_string();
57 let ty = match &field.ty {
58 syn::Type::Path(type_path) => type_path.path.segments.last().unwrap().ident.to_string(),
59 _ => "Unknown".to_string(),
60 };
61 let doc:Option<String> = field.attrs.iter()
62 .find(|attr| attr.path().is_ident("doc"))
63 .and_then(|attr|{
64 if let Ok(name) = attr.meta.require_name_value() {
65 if let syn::Expr::Lit(expr_list) = &name.value {
66 if let syn::Lit::Str(lit) = &expr_list.lit {
67 return Some(lit.value());
68 }
69 }
70
71 }
72 None
73 });
74
75
76 struct_unit.fields.push(FieldUnit::new (
77 name,
78 ty,
79 doc
80 ));
81 }
82 }
83
84 for attr in &item_struct.attrs {
85 if attr.path().is_ident("derive") {
86 attr.parse_nested_meta(|meta| {
87 if let Some(ident) = meta.path.get_ident() {
88 struct_unit.derive.push(ident.to_string());
89 }
90
91 Ok(())
92 })?;
93 }
94 }
95
96 Ok(struct_unit)
97}
98
99
100
101#[cfg(test)]
102mod test {
103
104 use super::*;
105
106 #[test]
107 fn test_parse_struct() -> Result<()> {
108 let code = r#"
109#[derive(Serialize, Deserialize, Debug)]
110struct Point {
111 x: i32,
112 y: i32,
113}
114"#;
115 let unit = process_code(code)?;
116
117 assert_eq!(unit.structs.len(), 1);
118 let struct_unit = &unit.structs[0];
119 assert_eq!(struct_unit.name, "Point");
120 assert_eq!(struct_unit.fields.len(), 2);
121 assert_eq!(struct_unit.fields[0].name, "x");
122 assert_eq!(struct_unit.fields[0].ty, "i32");
123 assert_eq!(struct_unit.fields[1].name, "y");
124 assert_eq!(struct_unit.fields[1].ty, "i32");
125 assert_eq!(struct_unit.derive.len(), 3);
126 assert_eq!(struct_unit.derive[0], "Serialize");
127 Ok(())
128 }
129
130 #[test]
131 fn test_parse_struct_with_comment() -> Result<()> {
132 let code = r#"
133#[derive(Serialize, Deserialize, Debug)]
134/// This is a point struct
135struct Point {
136 /// The x coordinate
137 x: i32,
138 /// The y coordinate
139 y: i32,
140}
141"#;
142 let unit = process_code(code)?;
143
144 assert_eq!(unit.structs.len(), 1);
145 let struct_unit = &unit.structs[0];
146 assert_eq!(struct_unit.name, "Point");
147 assert_eq!(struct_unit.fields.len(), 2);
148 assert_eq!(struct_unit.fields[0].name, "x");
149 assert_eq!(struct_unit.fields[0].ty, "i32");
150 assert_eq!(struct_unit.fields[1].name, "y");
151 assert_eq!(struct_unit.fields[1].ty, "i32");
152 assert_eq!(struct_unit.derive.len(), 3);
153 assert_eq!(struct_unit.derive[0], "Serialize");
154 assert_eq!(struct_unit.doc, Some(" This is a point struct".to_string()));
155 assert_eq!(struct_unit.fields[0].doc, Some(" The x coordinate".to_string()));
156 assert_eq!(struct_unit.fields[1].doc, Some(" The y coordinate".to_string()));
157
158 Ok(())
159 }
160}