Skip to main content

zamm_yang/codegen/template/basic/
item_declaration.rs

1use super::{AtomicFragment, CodeFragment, NestedFragment};
2use crate::codegen::docstring::into_docstring;
3use itertools::Itertools;
4use std::cell::RefCell;
5use std::rc::Rc;
6
7/// API for all items to be modified in the same way.
8pub trait ItemDeclarationAPI {
9    /// Mark this as a public declaration.
10    fn mark_as_public(&mut self);
11
12    /// Whether this has been marked as a public declaration or not.
13    fn is_public(&self) -> bool;
14
15    /// Set the documentation for the fragment.
16    fn document(&mut self, documentation: String);
17
18    /// Add an attribute to the declaration.
19    fn add_attribute(&mut self, attribute: String);
20
21    /// Set an implementation to go along with the declaration.
22    fn set_body(&mut self, body: Rc<RefCell<dyn CodeFragment>>);
23
24    /// Declares the item without implementing it.
25    fn mark_as_declare_only(&mut self);
26
27    /// Mark the module as requiring full implementation, even if that implementation is an empty
28    /// one.
29    fn mark_for_full_implementation(&mut self);
30}
31
32/// Fragment containing all universal modifiers for an item declaration.
33#[derive(Clone)]
34pub struct ItemDeclaration {
35    /// Documentation string for the item.
36    pub doc: Option<String>,
37    /// Whether or not this item should be publicly exported out of the module.
38    pub public: bool,
39    /// Attributes to be added to the item.
40    pub attributes: Vec<String>,
41    /// Actual definition of the item, whether it be a variable, function, or module.
42    pub definition: Rc<RefCell<dyn CodeFragment>>,
43    /// Some items, such as functions and submodules, may have actual implementations that go along
44    /// with their declaration.
45    pub body: Option<Rc<RefCell<dyn CodeFragment>>>,
46}
47
48impl ItemDeclaration {
49    /// Create a new item declaration.
50    pub fn new(definition: Rc<RefCell<dyn CodeFragment>>) -> Self {
51        Self {
52            definition,
53            ..Self::default()
54        }
55    }
56
57    /// Set the fragment that defines this item.
58    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) // nested fragment will take care of indent size
138            }
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}