1use crate::{Comments, helpers::function_signature, solang_ext::SafeUnwrap};
2use solang_parser::pt::{
3 ContractDefinition, ContractTy, EnumDefinition, ErrorDefinition, EventDefinition,
4 FunctionDefinition, StructDefinition, TypeDefinition, VariableDefinition,
5};
6use std::ops::Range;
7
8#[derive(Debug, PartialEq)]
10pub struct ParseItem {
11 pub source: ParseSource,
13 pub comments: Comments,
15 pub children: Vec<Self>,
17 pub code: String,
19}
20
21macro_rules! filter_children_fn {
25 ($vis:vis fn $name:ident(&self, $variant:ident) -> $ret:ty) => {
26 $vis fn $name(&self) -> Option<Vec<(&$ret, &Comments, &String)>> {
28 let items = self.children.iter().filter_map(|item| match item.source {
29 ParseSource::$variant(ref inner) => Some((inner, &item.comments, &item.code)),
30 _ => None,
31 });
32 let items = items.collect::<Vec<_>>();
33 if !items.is_empty() {
34 Some(items)
35 } else {
36 None
37 }
38 }
39 };
40}
41
42macro_rules! as_inner_source {
45 ($vis:vis fn $name:ident(&self, $variant:ident) -> $ret:ty) => {
46 $vis fn $name(&self) -> Option<&$ret> {
49 match self.source {
50 ParseSource::$variant(ref inner) => Some(inner),
51 _ => None
52 }
53 }
54 };
55}
56
57impl ParseItem {
58 pub fn new(source: ParseSource) -> Self {
60 Self {
61 source,
62 comments: Default::default(),
63 children: Default::default(),
64 code: Default::default(),
65 }
66 }
67
68 pub fn with_comments(mut self, comments: Comments) -> Self {
70 self.comments = comments;
71 self
72 }
73
74 pub fn with_children(mut self, children: Vec<Self>) -> Self {
76 self.children = children;
77 self
78 }
79
80 pub fn with_code(mut self, source: &str, tab_width: usize) -> Self {
84 let mut code = source[self.source.range()].to_string();
85
86 if let ParseSource::Function(_) | ParseSource::Error(_) | ParseSource::Event(_) =
88 self.source
89 {
90 code.push(';');
91 }
92
93 let prefix = &" ".repeat(tab_width);
95 self.code = code
96 .lines()
97 .map(|line| line.strip_prefix(prefix).unwrap_or(line))
98 .collect::<Vec<_>>()
99 .join("\n");
100 self
101 }
102
103 pub fn filename(&self) -> String {
105 let prefix = match self.source {
106 ParseSource::Contract(ref c) => match c.ty {
107 ContractTy::Contract(_) => "contract",
108 ContractTy::Abstract(_) => "abstract",
109 ContractTy::Interface(_) => "interface",
110 ContractTy::Library(_) => "library",
111 },
112 ParseSource::Function(_) => "function",
113 ParseSource::Variable(_) => "variable",
114 ParseSource::Event(_) => "event",
115 ParseSource::Error(_) => "error",
116 ParseSource::Struct(_) => "struct",
117 ParseSource::Enum(_) => "enum",
118 ParseSource::Type(_) => "type",
119 };
120 let ident = self.source.ident();
121 format!("{prefix}.{ident}.md")
122 }
123
124 filter_children_fn!(pub fn variables(&self, Variable) -> VariableDefinition);
125 filter_children_fn!(pub fn functions(&self, Function) -> FunctionDefinition);
126 filter_children_fn!(pub fn events(&self, Event) -> EventDefinition);
127 filter_children_fn!(pub fn errors(&self, Error) -> ErrorDefinition);
128 filter_children_fn!(pub fn structs(&self, Struct) -> StructDefinition);
129 filter_children_fn!(pub fn enums(&self, Enum) -> EnumDefinition);
130
131 as_inner_source!(pub fn as_contract(&self, Contract) -> ContractDefinition);
132 as_inner_source!(pub fn as_variable(&self, Variable) -> VariableDefinition);
133 as_inner_source!(pub fn as_function(&self, Function) -> FunctionDefinition);
134}
135
136#[derive(Clone, Debug, PartialEq, Eq)]
138#[allow(clippy::large_enum_variant)]
139pub enum ParseSource {
140 Contract(Box<ContractDefinition>),
142 Function(FunctionDefinition),
144 Variable(VariableDefinition),
146 Event(EventDefinition),
148 Error(ErrorDefinition),
150 Struct(StructDefinition),
152 Enum(EnumDefinition),
154 Type(TypeDefinition),
156}
157
158impl ParseSource {
159 pub fn ident(&self) -> String {
161 match self {
162 Self::Contract(contract) => contract.name.safe_unwrap().name.to_owned(),
163 Self::Variable(var) => var.name.safe_unwrap().name.to_owned(),
164 Self::Event(event) => event.name.safe_unwrap().name.to_owned(),
165 Self::Error(error) => error.name.safe_unwrap().name.to_owned(),
166 Self::Struct(structure) => structure.name.safe_unwrap().name.to_owned(),
167 Self::Enum(enumerable) => enumerable.name.safe_unwrap().name.to_owned(),
168 Self::Function(func) => {
169 func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.to_owned())
170 }
171 Self::Type(ty) => ty.name.name.to_owned(),
172 }
173 }
174
175 pub fn signature(&self) -> String {
177 match self {
178 Self::Function(func) => function_signature(func),
179 _ => self.ident(),
180 }
181 }
182
183 pub fn range(&self) -> Range<usize> {
185 match self {
186 Self::Contract(contract) => contract.loc,
187 Self::Variable(var) => var.loc,
188 Self::Event(event) => event.loc,
189 Self::Error(error) => error.loc,
190 Self::Struct(structure) => structure.loc,
191 Self::Enum(enumerable) => enumerable.loc,
192 Self::Function(func) => func.loc_prototype,
193 Self::Type(ty) => ty.loc,
194 }
195 .range()
196 }
197}