1use 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
31pub const SHORT: Swift<'static> = Swift::Primitive { primitive: "Int16" };
33
34pub const INTEGER: Swift<'static> = Swift::Primitive { primitive: "Int32" };
36
37pub const LONG: Swift<'static> = Swift::Primitive { primitive: "Int64" };
39
40pub const FLOAT: Swift<'static> = Swift::Primitive { primitive: "Float" };
42
43pub const DOUBLE: Swift<'static> = Swift::Primitive {
45 primitive: "Double",
46};
47
48pub const CHAR: Swift<'static> = Swift::Primitive {
50 primitive: "Character",
51};
52
53pub const BOOLEAN: Swift<'static> = Swift::Primitive { primitive: "Bool" };
55
56pub const BYTE: Swift<'static> = Swift::Primitive { primitive: "Int8" };
58
59pub const VOID: Swift<'static> = Swift::Primitive { primitive: "Void" };
61
62#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
64pub struct Name<'el> {
65 module: Option<Cons<'el>>,
67 name: Cons<'el>,
69}
70
71#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
73pub enum Swift<'el> {
74 Primitive {
76 primitive: &'static str,
78 },
79
80 Type {
82 name: Name<'el>,
84 },
85 Map {
87 key: Box<Swift<'el>>,
89 value: Box<Swift<'el>>,
91 },
92 Array {
94 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 }
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
221pub 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
235pub 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
248pub 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
260pub 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}