rstgen/swift/
mod.rs

1//! Specialization for Swift code generation.
2
3use std::collections::BTreeSet;
4use std::fmt::{self, Write};
5use {Cons, Custom, Formatter, Tokens};
6
7mod argument;
8mod class;
9mod comment;
10mod constructor;
11mod enum_;
12mod extension;
13mod field;
14mod method;
15mod modifier;
16mod protocol;
17mod struct_;
18
19pub use self::argument::Argument;
20pub use self::class::Class;
21pub use self::comment::BlockComment;
22pub use self::constructor::Constructor;
23pub use self::enum_::Enum;
24pub use self::extension::Extension;
25pub use self::field::Field;
26pub use self::method::Method;
27pub use self::modifier::Modifier;
28pub use self::protocol::Protocol;
29pub use self::struct_::Struct;
30
31/// Short primitive type.
32pub const SHORT: Swift<'static> = Swift::Primitive { primitive: "Int16" };
33
34/// Integer primitive type.
35pub const INTEGER: Swift<'static> = Swift::Primitive { primitive: "Int32" };
36
37/// Long primitive type.
38pub const LONG: Swift<'static> = Swift::Primitive { primitive: "Int64" };
39
40/// Float primitive type.
41pub const FLOAT: Swift<'static> = Swift::Primitive { primitive: "Float" };
42
43/// Double primitive type.
44pub const DOUBLE: Swift<'static> = Swift::Primitive {
45    primitive: "Double",
46};
47
48/// Char primitive type.
49pub const CHAR: Swift<'static> = Swift::Primitive {
50    primitive: "Character",
51};
52
53/// Boolean primitive type.
54pub const BOOLEAN: Swift<'static> = Swift::Primitive { primitive: "Bool" };
55
56/// Byte primitive type.
57pub const BYTE: Swift<'static> = Swift::Primitive { primitive: "Int8" };
58
59/// Void primitive type.
60pub const VOID: Swift<'static> = Swift::Primitive { primitive: "Void" };
61
62/// Name of an imported type.
63#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
64pub struct Name<'el> {
65    /// Module of the imported name.
66    module: Option<Cons<'el>>,
67    /// Name imported.
68    name: Cons<'el>,
69}
70
71/// Swift token specialization.
72#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
73pub enum Swift<'el> {
74    /// Primitive type.
75    Primitive {
76        /// The primitive-primitive type.
77        primitive: &'static str,
78    },
79
80    /// A regular type.
81    Type {
82        /// The name being referenced.
83        name: Name<'el>,
84    },
85    /// A map, [<key>: <value>].
86    Map {
87        /// Key of the map.
88        key: Box<Swift<'el>>,
89        /// Value of the map.
90        value: Box<Swift<'el>>,
91    },
92    /// An array, [<inner>].
93    Array {
94        /// Inner value of the array.
95        inner: Box<Swift<'el>>,
96    },
97}
98
99impl<'el> Swift<'el> {
100    fn type_imports<'a, 'b: 'a>(swift: &'b Swift<'b>, modules: &'a mut BTreeSet<&'b str>) {
101        use self::Swift::*;
102
103        match *swift {
104            Type { ref name, .. } => {
105                if let Some(module) = name.module.as_ref() {
106                    modules.insert(module);
107                }
108            }
109            Map {
110                ref key, ref value, ..
111            } => {
112                Self::type_imports(key, modules);
113                Self::type_imports(value, modules);
114            }
115            Array { ref inner, .. } => {
116                Self::type_imports(inner, modules);
117            }
118            Primitive { primitive } => {
119                // do nothing
120            }
121        };
122    }
123
124    fn imports<'a>(tokens: &'a Tokens<'a, Self>) -> Option<Tokens<'a, Self>> {
125        let mut modules = BTreeSet::new();
126
127        for custom in tokens.walk_custom() {
128            Self::type_imports(custom, &mut modules);
129        }
130
131        if modules.is_empty() {
132            return None;
133        }
134
135        let mut out = Tokens::new();
136
137        for module in modules {
138            let mut s = Tokens::new();
139
140            s.append("import ");
141            s.append(module);
142
143            out.push(s);
144        }
145
146        Some(out)
147    }
148}
149
150impl<'el> Custom for Swift<'el> {
151    type Extra = ();
152
153    fn format(&self, out: &mut Formatter, extra: &mut Self::Extra, level: usize) -> fmt::Result {
154        use self::Swift::*;
155
156        match *self {
157            Type {
158                name: Name { ref name, .. },
159                ..
160            } => {
161                out.write_str(name)?;
162            }
163            Map {
164                ref key, ref value, ..
165            } => {
166                out.write_str("[")?;
167                key.format(out, extra, level + 1)?;
168                out.write_str(": ")?;
169                value.format(out, extra, level + 1)?;
170                out.write_str("]")?;
171            }
172            Array { ref inner, .. } => {
173                out.write_str("[")?;
174                inner.format(out, extra, level + 1)?;
175                out.write_str("]")?;
176            }
177            Primitive { primitive } => {
178                out.write_str(primitive)?;
179            }
180        }
181
182        Ok(())
183    }
184
185    fn quote_string(out: &mut Formatter, input: &str) -> fmt::Result {
186        out.write_char('"')?;
187
188        for c in input.chars() {
189            match c {
190                '\t' => out.write_str("\\t")?,
191                '\n' => out.write_str("\\n")?,
192                '\r' => out.write_str("\\r")?,
193                '\'' => out.write_str("\\'")?,
194                '"' => out.write_str("\\\"")?,
195                '\\' => out.write_str("\\\\")?,
196                c => out.write_char(c)?,
197            };
198        }
199
200        out.write_char('"')?;
201        Ok(())
202    }
203
204    fn write_file<'a>(
205        tokens: Tokens<'a, Self>,
206        out: &mut Formatter,
207        extra: &mut Self::Extra,
208        level: usize,
209    ) -> fmt::Result {
210        let mut toks: Tokens<Self> = Tokens::new();
211
212        if let Some(imports) = Self::imports(&tokens) {
213            toks.push(imports);
214        }
215
216        toks.push_ref(&tokens);
217        toks.join_line_spacing().format(out, extra, level)
218    }
219}
220
221/// Setup an imported element.
222pub fn imported<'a, M, N>(module: M, name: N) -> Swift<'a>
223where
224    M: Into<Cons<'a>>,
225    N: Into<Cons<'a>>,
226{
227    Swift::Type {
228        name: Name {
229            module: Some(module.into()),
230            name: name.into(),
231        },
232    }
233}
234
235/// Setup a local element.
236pub fn local<'a, N>(name: N) -> Swift<'a>
237where
238    N: Into<Cons<'a>>,
239{
240    Swift::Type {
241        name: Name {
242            module: None,
243            name: name.into(),
244        },
245    }
246}
247
248/// Setup a map.
249pub fn map<'a, K, V>(key: K, value: V) -> Swift<'a>
250where
251    K: Into<Swift<'a>>,
252    V: Into<Swift<'a>>,
253{
254    Swift::Map {
255        key: Box::new(key.into()),
256        value: Box::new(value.into()),
257    }
258}
259
260/// Setup an array.
261pub fn array<'a, I>(inner: I) -> Swift<'a>
262where
263    I: Into<Swift<'a>>,
264{
265    Swift::Array {
266        inner: Box::new(inner.into()),
267    }
268}
269
270#[cfg(test)]
271mod tests {
272    use super::{array, imported, local, map, Swift};
273    use {Quoted, Tokens};
274
275    #[test]
276    fn test_string() {
277        let mut toks: Tokens<Swift> = Tokens::new();
278        toks.append("hello \n world".quoted());
279        let res = toks.to_string();
280
281        assert_eq!(Ok("\"hello \\n world\""), res.as_ref().map(|s| s.as_str()));
282    }
283
284    #[test]
285    fn test_imported() {
286        let dbg = imported("Foo", "Debug");
287        let mut toks: Tokens<Swift> = Tokens::new();
288        toks.push(toks!(&dbg));
289
290        assert_eq!(
291            Ok("import Foo\n\nDebug\n"),
292            toks.to_file().as_ref().map(|s| s.as_str())
293        );
294    }
295
296    #[test]
297    fn test_array() {
298        let dbg = array(imported("Foo", "Debug"));
299        let mut toks: Tokens<Swift> = Tokens::new();
300        toks.push(toks!(&dbg));
301
302        assert_eq!(
303            Ok("import Foo\n\n[Debug]\n"),
304            toks.to_file().as_ref().map(|s| s.as_str())
305        );
306    }
307
308    #[test]
309    fn test_map() {
310        let dbg = map(local("String"), imported("Foo", "Debug"));
311        let mut toks: Tokens<Swift> = Tokens::new();
312        toks.push(toks!(&dbg));
313
314        assert_eq!(
315            Ok("import Foo\n\n[String: Debug]\n"),
316            toks.to_file().as_ref().map(|s| s.as_str())
317        );
318    }
319}