crustal/
method.rs

1// C/C++ Code Generator For Rust
2//
3//
4// MIT License
5//
6// Copyright (c) 2022 Reto Achermann
7//
8// Permission is hereby granted, free of charge, to any person obtaining a copy
9// of this software and associated documentation files (the "Software"), to deal
10// in the Software without restriction, including without limitation the rights
11// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12// copies of the Software, and to permit persons to whom the Software is
13// furnished to do so, subject to the following conditions:
14//
15// The above copyright notice and this permission notice shall be included in all
16// copies or substantial portions of the Software.
17//
18// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24// SOFTWARE.
25
26//! # Method
27//!
28//! This module contains definitions for C++ class methods. Note that this is
29//! for ordinary methods only, not constructors or destructors.
30
31use std::fmt::{self, Write};
32
33use crate::{Block, Doc, Formatter, MethodParam, Type, Visibility};
34
35/// holds a method definition
36#[derive(Debug, Clone)]
37pub struct Method {
38    /// Name of the method
39    name: String,
40
41    /// the visibility of the method
42    visibility: Visibility,
43
44    /// the method documentation
45    doc: Option<Doc>,
46
47    /// the method parameters
48    params: Vec<MethodParam>,
49
50    /// the return type of the method
51    ret: Type,
52
53    /// whether the method is static
54    is_static: bool,
55
56    /// whether this is an inline method
57    is_inline: bool,
58
59    /// whether the method is virtual
60    is_virtual: bool,
61
62    /// sets the pure
63    is_pure: bool,
64
65    /// whether the method is override
66    is_override: bool,
67
68    /// sets the method to be const
69    is_const: bool,
70
71    /// wheter the definition is inside of the class
72    is_inside: bool,
73
74    /// the body of the method, a sequence of statements
75    body: Block,
76}
77
78impl Method {
79    /// Creates a new method definition
80    pub fn new(name: &str, ret: Type) -> Self {
81        Self::with_string(String::from(name), ret)
82    }
83
84    pub fn with_string(name: String, ret: Type) -> Self {
85        Self {
86            name,
87            doc: None,
88            visibility: Visibility::Private,
89            params: Vec::new(),
90            ret,
91            is_static: false,
92            is_inline: false,
93            is_virtual: false,
94            is_pure: false,
95            is_override: false,
96            is_const: false,
97            is_inside: false,
98            body: Block::new(),
99        }
100    }
101
102    /// returns the name of the method
103    pub fn name(&self) -> &str {
104        &self.name
105    }
106
107    /// obtains the type for this function
108    pub fn to_type(&self) -> Type {
109        panic!("needs to implement a corresponding type.")
110    }
111
112    /// adds a string to the documentation comment to the variant
113    pub fn doc_str(&mut self, doc: &str) -> &mut Self {
114        if let Some(d) = &mut self.doc {
115            d.add_text(doc);
116        } else {
117            self.doc = Some(Doc::with_str(doc));
118        }
119        self
120    }
121
122    /// adds a documetnation comment to the variant
123    pub fn add_doc(&mut self, doc: Doc) -> &mut Self {
124        self.doc = Some(doc);
125        self
126    }
127
128    /// sets the visibility of the method
129    pub fn set_visibility(&mut self, vis: Visibility) -> &mut Self {
130        self.visibility = vis;
131        self
132    }
133
134    /// tests if the method is private
135    pub fn is_public(&self) -> bool {
136        self.visibility == Visibility::Public
137    }
138
139    /// tests if the method is protected
140    pub fn is_protected(&self) -> bool {
141        self.visibility == Visibility::Protected
142    }
143
144    /// tests if the method is private
145    pub fn is_private(&self) -> bool {
146        self.visibility == Visibility::Private || self.visibility == Visibility::Default
147    }
148
149    /// sets the visibility to public
150    pub fn set_public(&mut self) -> &mut Self {
151        self.set_visibility(Visibility::Public)
152    }
153
154    /// sets the visibility to protected
155    pub fn set_protected(&mut self) -> &mut Self {
156        self.set_visibility(Visibility::Protected)
157    }
158
159    /// sets the visibility to private
160    pub fn set_private(&mut self) -> &mut Self {
161        self.set_visibility(Visibility::Private)
162    }
163
164    /// adds an argument to the method
165    pub fn push_param(&mut self, arg: MethodParam) -> &mut Self {
166        self.params.push(arg);
167        self
168    }
169
170    /// creates a new param and adds it to the method
171    pub fn new_param(&mut self, name: &str, ty: Type) -> &mut MethodParam {
172        self.push_param(MethodParam::new(name, ty));
173        self.params.last_mut().unwrap()
174    }
175
176    /// obtains a reference to the param with the given name
177    pub fn param_by_name(&self, name: &str) -> Option<&MethodParam> {
178        self.params.iter().find(|f| f.name() == name)
179    }
180
181    /// obtains a mutable reference to the param with the given name
182    pub fn param_by_name_mut(&mut self, name: &str) -> Option<&mut MethodParam> {
183        self.params.iter_mut().find(|f| f.name() == name)
184    }
185
186    /// obtains a reference to the param with the given index (starting at 0)
187    pub fn param_by_idx(&self, idx: usize) -> Option<&MethodParam> {
188        self.params.get(idx)
189    }
190
191    /// obtains a mutable reference to the param with the given index mut
192    pub fn param_by_idx_mut(&mut self, idx: usize) -> Option<&mut MethodParam> {
193        self.params.get_mut(idx)
194    }
195
196    /// sets the method to be overridden
197    ///
198    /// # Example
199    ///
200    /// void foo()   -> void foo() override
201    pub fn toggle_override(&mut self, val: bool) -> &mut Self {
202        self.is_override = val;
203        self
204    }
205
206    /// sets the method to override
207    pub fn set_override(&mut self) -> &mut Self {
208        self.toggle_override(true)
209    }
210
211    /// sets the constant modifier of the method
212    ///
213    /// # Example
214    ///
215    /// void foo()   -> void foo() const
216    pub fn toggle_const(&mut self, val: bool) -> &mut Self {
217        self.is_const = val;
218        self
219    }
220
221    /// makes the method to be constant
222    pub fn set_const(&mut self) -> &mut Self {
223        self.toggle_const(true)
224    }
225
226    /// sets the method to be virtual
227    ///
228    /// # Example
229    ///
230    /// void foo()   -> virtual void foo() = 0
231    pub fn toggle_virtual(&mut self, val: bool) -> &mut Self {
232        if !val {
233            self.is_pure = false;
234        }
235        self.is_virtual = val;
236        self
237    }
238
239    /// makes the method to be virtual
240    pub fn set_virtual(&mut self) -> &mut Self {
241        self.toggle_virtual(true)
242    }
243
244    /// sets the method to be pure
245    ///
246    /// # Example
247    ///
248    /// void foo()   -> virtual void foo() = 0
249    pub fn toggle_pure(&mut self, val: bool) -> &mut Self {
250        if val {
251            self.body.clear();
252            self.is_virtual = true
253        }
254        self.is_pure = val;
255        self
256    }
257
258    /// turns the method into a pure method
259    pub fn set_pure(&mut self) -> &mut Self {
260        self.toggle_pure(true)
261    }
262
263    /// sets the method to be static
264    ///
265    /// # Example
266    ///
267    /// void foo()   -> static void foo()
268    pub fn toggle_static(&mut self, val: bool) -> &mut Self {
269        self.is_static = val;
270        self
271    }
272
273    /// makes the method to be an static method
274    pub fn set_static(&mut self) -> &mut Self {
275        self.toggle_static(true)
276    }
277
278    /// sets the method to be inline
279    ///
280    /// # Example
281    ///
282    /// void foo()   -> inline void foo()
283    pub fn toggle_inline(&mut self, val: bool) -> &mut Self {
284        self.is_inline = val;
285        self
286    }
287
288    /// makes the method to be an inline method
289    pub fn set_inline(&mut self) -> &mut Self {
290        self.toggle_inline(true)
291    }
292
293    /// sets the definition localtion of the method
294    pub fn toggle_inside_def(&mut self, val: bool) -> &mut Self {
295        self.is_inside = val;
296        self
297    }
298
299    /// this method is defined inside
300    pub fn set_inside_def(&mut self) -> &mut Self {
301        self.toggle_inside_def(true)
302    }
303
304    /// sets the body for the method
305    pub fn set_body(&mut self, body: Block) -> &mut Self {
306        if !body.is_empty() {
307            self.is_pure = false;
308        }
309        self.body = body;
310        self
311    }
312
313    /// obtains a mutable reference to the body
314    pub fn body(&mut self) -> &mut Block {
315        &mut self.body
316    }
317
318    /// Formats the attribute using the given formatter.
319    pub fn do_fmt(&self, fmt: &mut Formatter<'_>, decl_only: bool) -> fmt::Result {
320        if !self.body.is_empty() | self.doc.is_some() {
321            writeln!(fmt)?;
322        }
323
324        if let Some(ref docs) = self.doc {
325            docs.fmt(fmt)?;
326        }
327
328        if self.is_static && decl_only {
329            write!(fmt, "static ")?;
330        }
331
332        if self.is_inline {
333            write!(fmt, "inline ")?;
334        }
335
336        if self.is_virtual && decl_only {
337            write!(fmt, "virtual ")?;
338        }
339
340        self.ret.fmt(fmt)?;
341        if decl_only {
342            write!(fmt, " {}", self.name)?;
343        } else {
344            fmt.write_scoped_name(self.name.as_str())?;
345        }
346        if self.params.is_empty() {
347            write!(fmt, "(void)")?;
348        } else {
349            write!(fmt, "(")?;
350            for (i, arg) in self.params.iter().enumerate() {
351                if i != 0 {
352                    write!(fmt, ", ")?;
353                }
354                arg.fmt(fmt)?;
355            }
356            write!(fmt, ")")?;
357        }
358
359        if self.is_const && decl_only {
360            write!(fmt, " const")?;
361        }
362
363        if self.is_override && decl_only {
364            write!(fmt, " override")?;
365        }
366
367        if self.body.is_empty() && self.is_pure && decl_only {
368            return write!(fmt, " = 0;");
369        }
370
371        // if we want to have the declaration only, then do that,
372        // but only if it's not a inside method or an inline method
373        if self.body.is_empty() || (decl_only && !(self.is_inside || self.is_inline)) {
374            return writeln!(fmt, ";");
375        }
376
377        fmt.block(|f| self.body.fmt(f))?;
378        writeln!(fmt)
379    }
380
381    /// formats the method definition
382    pub fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
383        self.do_fmt(fmt, false)
384    }
385
386    /// formats the method declaration
387    pub fn fmt_decl(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
388        self.do_fmt(fmt, true)
389    }
390
391    /// formats the method definition
392    pub fn fmt_def(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
393        // inline or inside functions are defined in the declaration
394        if self.is_inline || self.is_inside {
395            return Ok(());
396        }
397        self.do_fmt(fmt, false)
398    }
399}