1use crate::{error::ParserResult, Comments};
2use forge_fmt::{
3 solang_ext::SafeUnwrap, Comments as FmtComments, Formatter, FormatterConfig, InlineConfig,
4 Visitor,
5};
6use solang_parser::pt::{
7 ContractDefinition, ContractTy, EnumDefinition, ErrorDefinition, EventDefinition,
8 FunctionDefinition, StructDefinition, TypeDefinition, VariableDefinition,
9};
10
11#[derive(Debug, PartialEq)]
13pub struct ParseItem {
14 pub source: ParseSource,
16 pub comments: Comments,
18 pub children: Vec<ParseItem>,
20 pub code: String,
22}
23
24macro_rules! filter_children_fn {
28 ($vis:vis fn $name:ident(&self, $variant:ident) -> $ret:ty) => {
29 $vis fn $name(&self) -> Option<Vec<(&$ret, &Comments, &String)>> {
31 let items = self.children.iter().filter_map(|item| match item.source {
32 ParseSource::$variant(ref inner) => Some((inner, &item.comments, &item.code)),
33 _ => None,
34 });
35 let items = items.collect::<Vec<_>>();
36 if !items.is_empty() {
37 Some(items)
38 } else {
39 None
40 }
41 }
42 };
43}
44
45macro_rules! as_inner_source {
48 ($vis:vis fn $name:ident(&self, $variant:ident) -> $ret:ty) => {
49 $vis fn $name(&self) -> Option<&$ret> {
52 match self.source {
53 ParseSource::$variant(ref inner) => Some(inner),
54 _ => None
55 }
56 }
57 };
58}
59
60impl ParseItem {
61 pub fn new(source: ParseSource) -> Self {
63 Self {
64 source,
65 comments: Default::default(),
66 children: Default::default(),
67 code: Default::default(),
68 }
69 }
70
71 pub fn with_comments(mut self, comments: Comments) -> Self {
73 self.comments = comments;
74 self
75 }
76
77 pub fn with_children(mut self, children: Vec<Self>) -> Self {
79 self.children = children;
80 self
81 }
82
83 pub fn with_code(mut self, source: &str, config: FormatterConfig) -> ParserResult<Self> {
85 let mut code = String::new();
86 let mut fmt = Formatter::new(
87 &mut code,
88 source,
89 FmtComments::default(),
90 InlineConfig::default(),
91 config,
92 );
93
94 match self.source.clone() {
95 ParseSource::Contract(mut contract) => {
96 contract.parts = vec![];
97 fmt.visit_contract(&mut contract)?
98 }
99 ParseSource::Function(mut func) => {
100 func.body = None;
101 fmt.visit_function(&mut func)?
102 }
103 ParseSource::Variable(mut var) => fmt.visit_var_definition(&mut var)?,
104 ParseSource::Event(mut event) => fmt.visit_event(&mut event)?,
105 ParseSource::Error(mut error) => fmt.visit_error(&mut error)?,
106 ParseSource::Struct(mut structure) => fmt.visit_struct(&mut structure)?,
107 ParseSource::Enum(mut enumeration) => fmt.visit_enum(&mut enumeration)?,
108 ParseSource::Type(mut ty) => fmt.visit_type_definition(&mut ty)?,
109 };
110
111 self.code = code;
112
113 Ok(self)
114 }
115
116 pub fn filename(&self) -> String {
118 let prefix = match self.source {
119 ParseSource::Contract(ref c) => match c.ty {
120 ContractTy::Contract(_) => "contract",
121 ContractTy::Abstract(_) => "abstract",
122 ContractTy::Interface(_) => "interface",
123 ContractTy::Library(_) => "library",
124 },
125 ParseSource::Function(_) => "function",
126 ParseSource::Variable(_) => "variable",
127 ParseSource::Event(_) => "event",
128 ParseSource::Error(_) => "error",
129 ParseSource::Struct(_) => "struct",
130 ParseSource::Enum(_) => "enum",
131 ParseSource::Type(_) => "type",
132 };
133 let ident = self.source.ident();
134 format!("{prefix}.{ident}.md")
135 }
136
137 filter_children_fn!(pub fn variables(&self, Variable) -> VariableDefinition);
138 filter_children_fn!(pub fn functions(&self, Function) -> FunctionDefinition);
139 filter_children_fn!(pub fn events(&self, Event) -> EventDefinition);
140 filter_children_fn!(pub fn errors(&self, Error) -> ErrorDefinition);
141 filter_children_fn!(pub fn structs(&self, Struct) -> StructDefinition);
142 filter_children_fn!(pub fn enums(&self, Enum) -> EnumDefinition);
143
144 as_inner_source!(pub fn as_contract(&self, Contract) -> ContractDefinition);
145 as_inner_source!(pub fn as_variable(&self, Variable) -> VariableDefinition);
146 as_inner_source!(pub fn as_function(&self, Function) -> FunctionDefinition);
147}
148
149#[derive(Clone, Debug, PartialEq, Eq)]
151#[allow(clippy::large_enum_variant)]
152pub enum ParseSource {
153 Contract(Box<ContractDefinition>),
155 Function(FunctionDefinition),
157 Variable(VariableDefinition),
159 Event(EventDefinition),
161 Error(ErrorDefinition),
163 Struct(StructDefinition),
165 Enum(EnumDefinition),
167 Type(TypeDefinition),
169}
170
171impl ParseSource {
172 pub fn ident(&self) -> String {
174 match self {
175 Self::Contract(contract) => contract.name.safe_unwrap().name.to_owned(),
176 Self::Variable(var) => var.name.safe_unwrap().name.to_owned(),
177 Self::Event(event) => event.name.safe_unwrap().name.to_owned(),
178 Self::Error(error) => error.name.safe_unwrap().name.to_owned(),
179 Self::Struct(structure) => structure.name.safe_unwrap().name.to_owned(),
180 Self::Enum(enumerable) => enumerable.name.safe_unwrap().name.to_owned(),
181 Self::Function(func) => {
182 func.name.as_ref().map_or(func.ty.to_string(), |n| n.name.to_owned())
183 }
184 Self::Type(ty) => ty.name.name.to_owned(),
185 }
186 }
187}