zamm_yang/codegen/template/basic/
item_declaration.rs1use super::{AtomicFragment, CodeFragment, NestedFragment};
2use crate::codegen::docstring::into_docstring;
3use itertools::Itertools;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7pub trait ItemDeclarationAPI {
9 fn mark_as_public(&mut self);
11
12 fn is_public(&self) -> bool;
14
15 fn document(&mut self, documentation: String);
17
18 fn add_attribute(&mut self, attribute: String);
20
21 fn set_body(&mut self, body: Rc<RefCell<dyn CodeFragment>>);
23
24 fn mark_as_declare_only(&mut self);
26
27 fn mark_for_full_implementation(&mut self);
30}
31
32#[derive(Clone)]
34pub struct ItemDeclaration {
35 pub doc: Option<String>,
37 pub public: bool,
39 pub attributes: Vec<String>,
41 pub definition: Rc<RefCell<dyn CodeFragment>>,
43 pub body: Option<Rc<RefCell<dyn CodeFragment>>>,
46}
47
48impl ItemDeclaration {
49 pub fn new(definition: Rc<RefCell<dyn CodeFragment>>) -> Self {
51 Self {
52 definition,
53 ..Self::default()
54 }
55 }
56
57 pub fn set_definition(&mut self, definition: Rc<RefCell<dyn CodeFragment>>) {
59 self.definition = definition;
60 }
61}
62
63impl Default for ItemDeclaration {
64 fn default() -> Self {
65 Self {
66 doc: None,
67 public: false,
68 attributes: vec![],
69 definition: Rc::new(RefCell::new(AtomicFragment::default())),
70 body: None,
71 }
72 }
73}
74
75impl ItemDeclarationAPI for ItemDeclaration {
76 fn mark_as_public(&mut self) {
77 self.public = true;
78 }
79
80 fn is_public(&self) -> bool {
81 self.public
82 }
83
84 fn add_attribute(&mut self, attribute: String) {
85 self.attributes.push(attribute);
86 }
87
88 fn document(&mut self, documentation: String) {
89 self.doc = Some(documentation);
90 }
91
92 fn set_body(&mut self, body: Rc<RefCell<dyn CodeFragment>>) {
93 self.body = Some(body);
94 }
95
96 fn mark_as_declare_only(&mut self) {
97 self.body = None;
98 }
99
100 fn mark_for_full_implementation(&mut self) {
101 if self.body.is_none() {
102 self.body = Some(Rc::new(RefCell::new(AtomicFragment::default())));
103 }
104 }
105}
106
107impl CodeFragment for ItemDeclaration {
108 fn body(&self, line_width: usize) -> String {
109 let doc = match &self.doc {
110 Some(d) => into_docstring(&d, line_width) + "\n",
111 None => String::new(),
112 };
113 let public = if self.public { "pub " } else { "" };
114 let mut attrs = self
115 .attributes
116 .iter()
117 .map(|a| format!("#[{}]", a))
118 .format("\n")
119 .to_string();
120 if !attrs.is_empty() {
121 attrs.push('\n');
122 }
123 let preamble = format!(
124 "{doc}{attrs}{public}{definition}",
125 doc = doc,
126 attrs = attrs,
127 public = public,
128 definition = self.definition.borrow().body(line_width),
129 )
130 .trim()
131 .to_owned();
132 match &self.body {
133 Some(actual_implementation) => {
134 let mut nested =
135 NestedFragment::new(AtomicFragment::new(format!("{} {{", preamble)), "}");
136 nested.append(actual_implementation.clone());
137 nested.body(line_width) }
139 None => format!("{};", preamble),
140 }
141 }
142
143 fn imports(&self) -> Vec<String> {
144 self.definition.borrow().imports()
145 }
146}
147
148#[cfg(test)]
149mod tests {
150 use super::*;
151 use indoc::indoc;
152
153 fn simple_declaration() -> ItemDeclaration {
154 ItemDeclaration::new(Rc::new(RefCell::new(AtomicFragment::new(
155 "fn foo() -> bool".to_owned(),
156 ))))
157 }
158
159 #[test]
160 fn test_simple_declaration() {
161 let i = simple_declaration();
162
163 assert_eq!(i.imports(), Vec::<String>::new());
164 assert_eq!(i.body(80), "fn foo() -> bool;");
165 }
166
167 #[test]
168 fn test_public_declaration() {
169 let mut i = simple_declaration();
170 i.mark_as_public();
171
172 assert_eq!(i.imports(), Vec::<String>::new());
173 assert_eq!(i.body(80), "pub fn foo() -> bool;");
174 }
175
176 #[test]
177 fn test_documented_declaration() {
178 let mut i = simple_declaration();
179 i.document("Some bloody documentation for ya.".to_owned());
180
181 assert_eq!(i.imports(), Vec::<String>::new());
182 assert_eq!(
183 i.body(80),
184 indoc! {"
185 /// Some bloody documentation for ya.
186 fn foo() -> bool;"}
187 );
188 }
189
190 #[test]
191 fn test_documentation_line_width() {
192 let mut i = simple_declaration();
193 i.document("Some bloody documentation for ya.".to_owned());
194
195 assert_eq!(i.imports(), Vec::<String>::new());
196 assert_eq!(
197 i.body(30),
198 indoc! {"
199 /// Some bloody documentation
200 /// for ya.
201 fn foo() -> bool;"}
202 );
203 }
204
205 #[test]
206 fn test_attributed_declaration() {
207 let mut i = simple_declaration();
208 i.add_attribute("allow(deprecated)".to_owned());
209
210 assert_eq!(i.imports(), Vec::<String>::new());
211 assert_eq!(
212 i.body(80),
213 indoc! {"
214 #[allow(deprecated)]
215 fn foo() -> bool;"}
216 );
217 }
218
219 #[test]
220 fn test_full_but_empty_declaration() {
221 let mut i = simple_declaration();
222 i.mark_for_full_implementation();
223
224 assert_eq!(i.imports(), Vec::<String>::new());
225 assert_eq!(i.body(80), "fn foo() -> bool {}");
226 }
227
228 #[test]
229 fn test_nonempty_declaration() {
230 let mut i = simple_declaration();
231 i.set_body(Rc::new(RefCell::new(AtomicFragment::new(
232 "!bar()".to_owned(),
233 ))));
234
235 assert_eq!(i.imports(), Vec::<String>::new());
236 assert_eq!(
237 i.body(80),
238 indoc! {"
239 fn foo() -> bool {
240 !bar()
241 }"}
242 );
243 }
244
245 #[test]
246 fn test_combined_declaration() {
247 let mut i = simple_declaration();
248 i.mark_as_public();
249 i.document("Some bloody documentation for ya.".to_owned());
250 i.add_attribute("allow(deprecated)".to_owned());
251
252 assert_eq!(i.imports(), Vec::<String>::new());
253 assert_eq!(
254 i.body(80),
255 indoc! {"
256 /// Some bloody documentation for ya.
257 #[allow(deprecated)]
258 pub fn foo() -> bool;"}
259 );
260 }
261}