1use std::fmt;
4use std::path::PathBuf;
5
6#[derive(Debug, Clone, Default)]
8pub struct SourceLocation {
9 pub file: Option<PathBuf>,
10 pub line: Option<usize>,
11 pub column: Option<usize>,
12}
13
14impl SourceLocation {
15 pub fn new(file: PathBuf, line: usize) -> Self {
16 Self {
17 file: Some(file),
18 line: Some(line),
19 column: None,
20 }
21 }
22}
23
24impl fmt::Display for SourceLocation {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 match (&self.file, self.line) {
27 (Some(file), Some(line)) => write!(f, "{}:{}", file.display(), line),
28 (Some(file), None) => write!(f, "{}", file.display()),
29 _ => write!(f, "unknown"),
30 }
31 }
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
36pub enum Visibility {
37 Public,
38 Crate,
39 Super,
40 SelfOnly,
41 #[default]
42 Private,
43}
44
45impl fmt::Display for Visibility {
46 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
47 match self {
48 Visibility::Public => write!(f, "pub"),
49 Visibility::Crate => write!(f, "pub(crate)"),
50 Visibility::Super => write!(f, "pub(super)"),
51 Visibility::SelfOnly => write!(f, "pub(self)"),
52 Visibility::Private => write!(f, ""),
53 }
54 }
55}
56
57#[derive(Debug, Clone)]
59pub enum AnalyzedItem {
60 Function(FunctionInfo),
61 Struct(StructInfo),
62 Enum(EnumInfo),
63 Trait(TraitInfo),
64 Impl(ImplInfo),
65 Module(ModuleInfo),
66 TypeAlias(TypeAliasInfo),
67 Const(ConstInfo),
68 Static(StaticInfo),
69}
70
71impl AnalyzedItem {
72 pub fn name(&self) -> &str {
73 match self {
74 AnalyzedItem::Function(f) => &f.name,
75 AnalyzedItem::Struct(s) => &s.name,
76 AnalyzedItem::Enum(e) => &e.name,
77 AnalyzedItem::Trait(t) => &t.name,
78 AnalyzedItem::Impl(i) => &i.self_ty,
79 AnalyzedItem::Module(m) => &m.name,
80 AnalyzedItem::TypeAlias(t) => &t.name,
81 AnalyzedItem::Const(c) => &c.name,
82 AnalyzedItem::Static(s) => &s.name,
83 }
84 }
85
86 pub fn kind(&self) -> &'static str {
87 match self {
88 AnalyzedItem::Function(_) => "fn",
89 AnalyzedItem::Struct(_) => "struct",
90 AnalyzedItem::Enum(_) => "enum",
91 AnalyzedItem::Trait(_) => "trait",
92 AnalyzedItem::Impl(_) => "impl",
93 AnalyzedItem::Module(_) => "mod",
94 AnalyzedItem::TypeAlias(_) => "type",
95 AnalyzedItem::Const(_) => "const",
96 AnalyzedItem::Static(_) => "static",
97 }
98 }
99
100 pub fn visibility(&self) -> Option<Visibility> {
101 match self {
102 AnalyzedItem::Function(f) => Some(f.visibility),
103 AnalyzedItem::Struct(s) => Some(s.visibility),
104 AnalyzedItem::Enum(e) => Some(e.visibility),
105 AnalyzedItem::Trait(t) => Some(t.visibility),
106 AnalyzedItem::Impl(_) => None,
107 AnalyzedItem::Module(m) => Some(m.visibility),
108 AnalyzedItem::TypeAlias(t) => Some(t.visibility),
109 AnalyzedItem::Const(c) => Some(c.visibility),
110 AnalyzedItem::Static(s) => Some(s.visibility),
111 }
112 }
113
114 pub fn documentation(&self) -> Option<&str> {
115 match self {
116 AnalyzedItem::Function(f) => f.documentation.as_deref(),
117 AnalyzedItem::Struct(s) => s.documentation.as_deref(),
118 AnalyzedItem::Enum(e) => e.documentation.as_deref(),
119 AnalyzedItem::Trait(t) => t.documentation.as_deref(),
120 AnalyzedItem::Impl(_) => None,
121 AnalyzedItem::Module(m) => m.documentation.as_deref(),
122 AnalyzedItem::TypeAlias(t) => t.documentation.as_deref(),
123 AnalyzedItem::Const(c) => c.documentation.as_deref(),
124 AnalyzedItem::Static(s) => s.documentation.as_deref(),
125 }
126 }
127
128 pub fn source_location(&self) -> Option<&SourceLocation> {
129 match self {
130 AnalyzedItem::Function(f) => Some(&f.source_location),
131 AnalyzedItem::Struct(s) => Some(&s.source_location),
132 AnalyzedItem::Enum(e) => Some(&e.source_location),
133 AnalyzedItem::Trait(t) => Some(&t.source_location),
134 AnalyzedItem::Impl(i) => Some(&i.source_location),
135 AnalyzedItem::Module(m) => Some(&m.source_location),
136 AnalyzedItem::TypeAlias(t) => Some(&t.source_location),
137 AnalyzedItem::Const(c) => Some(&c.source_location),
138 AnalyzedItem::Static(s) => Some(&s.source_location),
139 }
140 }
141
142 pub fn module_path(&self) -> &[String] {
144 match self {
145 AnalyzedItem::Function(f) => &f.module_path,
146 AnalyzedItem::Struct(s) => &s.module_path,
147 AnalyzedItem::Enum(e) => &e.module_path,
148 AnalyzedItem::Trait(t) => &t.module_path,
149 AnalyzedItem::Impl(i) => &i.module_path,
150 AnalyzedItem::Module(m) => &m.module_path,
151 AnalyzedItem::TypeAlias(t) => &t.module_path,
152 AnalyzedItem::Const(c) => &c.module_path,
153 AnalyzedItem::Static(s) => &s.module_path,
154 }
155 }
156
157 pub fn qualified_name(&self) -> String {
159 let path = self.module_path();
160 if path.is_empty() {
161 self.name().to_string()
162 } else {
163 format!("{}::{}", path.join("::"), self.name())
164 }
165 }
166
167 pub fn definition(&self) -> String {
169 match self {
170 AnalyzedItem::Function(f) => f.signature.clone(),
171 AnalyzedItem::Struct(s) => s.full_definition(),
172 AnalyzedItem::Enum(e) => e.full_definition(),
173 AnalyzedItem::Trait(t) => t.full_definition(),
174 AnalyzedItem::Impl(i) => i.full_definition(),
175 AnalyzedItem::Module(m) => format!("mod {}", m.name),
176 AnalyzedItem::TypeAlias(t) => format!("type {} = {}", t.name, t.ty),
177 AnalyzedItem::Const(c) => format!("const {}: {}", c.name, c.ty),
178 AnalyzedItem::Static(s) => {
179 let mut_str = if s.is_mut { "mut " } else { "" };
180 format!("static {}{}: {}", mut_str, s.name, s.ty)
181 }
182 }
183 }
184}
185
186#[derive(Debug, Clone)]
188pub struct FunctionInfo {
189 pub name: String,
190 pub signature: String,
191 pub visibility: Visibility,
192 pub is_async: bool,
193 pub is_const: bool,
194 pub is_unsafe: bool,
195 pub generics: Vec<String>,
196 pub parameters: Vec<Parameter>,
197 pub return_type: Option<String>,
198 pub documentation: Option<String>,
199 pub attributes: Vec<String>,
200 pub where_clause: Option<String>,
201 pub source_location: SourceLocation,
202 pub module_path: Vec<String>,
204}
205
206#[derive(Debug, Clone)]
208pub struct Parameter {
209 pub name: String,
210 pub ty: String,
211 pub is_self: bool,
212 pub is_mut: bool,
213 pub is_ref: bool,
214}
215
216impl fmt::Display for Parameter {
217 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
218 if self.is_self {
219 if self.is_ref && self.is_mut {
220 write!(f, "&mut self")
221 } else if self.is_ref {
222 write!(f, "&self")
223 } else if self.is_mut {
224 write!(f, "mut self")
225 } else {
226 write!(f, "self")
227 }
228 } else {
229 write!(f, "{}: {}", self.name, self.ty)
230 }
231 }
232}
233
234#[derive(Debug, Clone)]
236pub struct StructInfo {
237 pub name: String,
238 pub visibility: Visibility,
239 pub generics: Vec<String>,
240 pub fields: Vec<Field>,
241 pub kind: StructKind,
242 pub documentation: Option<String>,
243 pub derives: Vec<String>,
244 pub attributes: Vec<String>,
245 pub where_clause: Option<String>,
246 pub source_location: SourceLocation,
247 pub module_path: Vec<String>,
249}
250
251impl StructInfo {
252 pub fn full_definition(&self) -> String {
253 let vis = if self.visibility == Visibility::Public {
254 "pub "
255 } else {
256 ""
257 };
258 let generics = if self.generics.is_empty() {
259 String::new()
260 } else {
261 format!("<{}>", self.generics.join(", "))
262 };
263
264 match self.kind {
265 StructKind::Named => {
266 let fields: Vec<String> = self
267 .fields
268 .iter()
269 .map(|f| {
270 let fvis = if f.visibility == Visibility::Public {
271 "pub "
272 } else {
273 ""
274 };
275 format!(" {}{}: {}", fvis, f.name, f.ty)
276 })
277 .collect();
278 format!(
279 "{}struct {}{} {{\n{}\n}}",
280 vis,
281 self.name,
282 generics,
283 fields.join(",\n")
284 )
285 }
286 StructKind::Tuple => {
287 let fields: Vec<String> = self
288 .fields
289 .iter()
290 .map(|f| {
291 let fvis = if f.visibility == Visibility::Public {
292 "pub "
293 } else {
294 ""
295 };
296 format!("{}{}", fvis, f.ty)
297 })
298 .collect();
299 format!(
300 "{}struct {}{}({});",
301 vis,
302 self.name,
303 generics,
304 fields.join(", ")
305 )
306 }
307 StructKind::Unit => format!("{}struct {}{};", vis, self.name, generics),
308 }
309 }
310}
311
312#[derive(Debug, Clone, Copy, PartialEq, Eq)]
313pub enum StructKind {
314 Named,
315 Tuple,
316 Unit,
317}
318
319#[derive(Debug, Clone)]
321pub struct Field {
322 pub name: String,
323 pub ty: String,
324 pub visibility: Visibility,
325 pub documentation: Option<String>,
326}
327
328#[derive(Debug, Clone)]
330pub struct EnumInfo {
331 pub name: String,
332 pub visibility: Visibility,
333 pub generics: Vec<String>,
334 pub variants: Vec<Variant>,
335 pub documentation: Option<String>,
336 pub derives: Vec<String>,
337 pub attributes: Vec<String>,
338 pub where_clause: Option<String>,
339 pub source_location: SourceLocation,
340 pub module_path: Vec<String>,
342}
343
344impl EnumInfo {
345 pub fn full_definition(&self) -> String {
346 let vis = if self.visibility == Visibility::Public {
347 "pub "
348 } else {
349 ""
350 };
351 let generics = if self.generics.is_empty() {
352 String::new()
353 } else {
354 format!("<{}>", self.generics.join(", "))
355 };
356
357 let variants: Vec<String> = self
358 .variants
359 .iter()
360 .map(|v| {
361 let fields = match &v.fields {
362 VariantFields::Named(fields) => {
363 let f: Vec<_> = fields
364 .iter()
365 .map(|f| format!("{}: {}", f.name, f.ty))
366 .collect();
367 format!(" {{ {} }}", f.join(", "))
368 }
369 VariantFields::Unnamed(types) => format!("({})", types.join(", ")),
370 VariantFields::Unit => String::new(),
371 };
372 format!(" {}{}", v.name, fields)
373 })
374 .collect();
375
376 format!(
377 "{}enum {}{} {{\n{}\n}}",
378 vis,
379 self.name,
380 generics,
381 variants.join(",\n")
382 )
383 }
384}
385
386#[derive(Debug, Clone)]
388pub struct Variant {
389 pub name: String,
390 pub fields: VariantFields,
391 pub discriminant: Option<String>,
392 pub documentation: Option<String>,
393}
394
395#[derive(Debug, Clone)]
396pub enum VariantFields {
397 Named(Vec<Field>),
398 Unnamed(Vec<String>),
399 Unit,
400}
401
402#[derive(Debug, Clone)]
404pub struct TraitInfo {
405 pub name: String,
406 pub visibility: Visibility,
407 pub generics: Vec<String>,
408 pub supertraits: Vec<String>,
409 pub methods: Vec<TraitMethod>,
410 pub associated_types: Vec<AssociatedType>,
411 pub associated_consts: Vec<AssociatedConst>,
412 pub documentation: Option<String>,
413 pub is_unsafe: bool,
414 pub is_auto: bool,
415 pub where_clause: Option<String>,
416 pub source_location: SourceLocation,
417 pub module_path: Vec<String>,
419}
420
421impl TraitInfo {
422 pub fn full_definition(&self) -> String {
423 let vis = if self.visibility == Visibility::Public {
424 "pub "
425 } else {
426 ""
427 };
428 let unsafe_str = if self.is_unsafe { "unsafe " } else { "" };
429 let auto_str = if self.is_auto { "auto " } else { "" };
430 let generics = if self.generics.is_empty() {
431 String::new()
432 } else {
433 format!("<{}>", self.generics.join(", "))
434 };
435 let bounds = if self.supertraits.is_empty() {
436 String::new()
437 } else {
438 format!(": {}", self.supertraits.join(" + "))
439 };
440
441 let mut items = Vec::new();
442 for at in &self.associated_types {
443 items.push(format!(" type {};", at.name));
444 }
445 for method in &self.methods {
446 items.push(format!(" {};", method.signature));
447 }
448
449 format!(
450 "{}{}{}trait {}{}{} {{\n{}\n}}",
451 vis,
452 unsafe_str,
453 auto_str,
454 self.name,
455 generics,
456 bounds,
457 items.join("\n")
458 )
459 }
460}
461
462#[derive(Debug, Clone)]
464pub struct TraitMethod {
465 pub name: String,
466 pub signature: String,
467 pub has_default: bool,
468 pub is_async: bool,
469 pub documentation: Option<String>,
470}
471
472#[derive(Debug, Clone)]
474pub struct AssociatedType {
475 pub name: String,
476 pub bounds: Vec<String>,
477 pub default: Option<String>,
478}
479
480#[derive(Debug, Clone)]
482pub struct AssociatedConst {
483 pub name: String,
484 pub ty: String,
485 pub default: Option<String>,
486}
487
488#[derive(Debug, Clone)]
490pub struct ImplInfo {
491 pub self_ty: String,
492 pub trait_name: Option<String>,
493 pub generics: Vec<String>,
494 pub methods: Vec<FunctionInfo>,
495 pub is_unsafe: bool,
496 pub is_negative: bool,
497 pub where_clause: Option<String>,
498 pub source_location: SourceLocation,
499 pub module_path: Vec<String>,
501}
502
503impl ImplInfo {
504 pub fn full_definition(&self) -> String {
505 let unsafe_str = if self.is_unsafe { "unsafe " } else { "" };
506 let negative_str = if self.is_negative { "!" } else { "" };
507 let generics = if self.generics.is_empty() {
508 String::new()
509 } else {
510 format!("<{}>", self.generics.join(", "))
511 };
512
513 match &self.trait_name {
514 Some(trait_name) => format!(
515 "{}impl{} {}{} for {}",
516 unsafe_str, generics, negative_str, trait_name, self.self_ty
517 ),
518 None => format!("impl{} {}", generics, self.self_ty),
519 }
520 }
521}
522
523#[derive(Debug, Clone)]
525pub struct ModuleInfo {
526 pub name: String,
527 pub path: String,
528 pub visibility: Visibility,
529 pub items: Vec<String>,
530 pub submodules: Vec<String>,
531 pub documentation: Option<String>,
532 pub is_inline: bool,
533 pub source_location: SourceLocation,
534 pub module_path: Vec<String>,
536}
537
538#[derive(Debug, Clone)]
540pub struct TypeAliasInfo {
541 pub name: String,
542 pub visibility: Visibility,
543 pub generics: Vec<String>,
544 pub ty: String,
545 pub documentation: Option<String>,
546 pub where_clause: Option<String>,
547 pub source_location: SourceLocation,
548 pub module_path: Vec<String>,
550}
551
552#[derive(Debug, Clone)]
554pub struct ConstInfo {
555 pub name: String,
556 pub visibility: Visibility,
557 pub ty: String,
558 pub value: Option<String>,
559 pub documentation: Option<String>,
560 pub source_location: SourceLocation,
561 pub module_path: Vec<String>,
563}
564
565#[derive(Debug, Clone)]
567pub struct StaticInfo {
568 pub name: String,
569 pub visibility: Visibility,
570 pub ty: String,
571 pub is_mut: bool,
572 pub documentation: Option<String>,
573 pub source_location: SourceLocation,
574 pub module_path: Vec<String>,
576}