1use std::collections::BTreeSet;
4use std::fmt::{self, Write};
5use std::rc::Rc;
6use {Cons, Custom, Formatter, IntoTokens, Tokens};
7
8static SEP: &'static str = "::";
9
10#[derive(Debug, Clone, Copy)]
12pub struct Ref;
13
14#[derive(Debug, Clone, Copy)]
16pub struct StaticRef;
17
18#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
20pub enum Reference<'el> {
21 Ref,
23 StaticRef,
25 Named(Cons<'el>),
27}
28
29impl From<Ref> for Reference<'static> {
30 fn from(_: Ref) -> Self {
31 Reference::Ref
32 }
33}
34
35impl From<StaticRef> for Reference<'static> {
36 fn from(_: StaticRef) -> Self {
37 Reference::StaticRef
38 }
39}
40
41impl From<Rc<String>> for Reference<'static> {
42 fn from(value: Rc<String>) -> Self {
43 Reference::Named(Cons::from(value))
44 }
45}
46
47impl<'el> From<&'el str> for Reference<'el> {
48 fn from(value: &'el str) -> Self {
49 Reference::Named(Cons::from(value))
50 }
51}
52
53#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
55pub struct Name<'el> {
56 reference: Option<Reference<'el>>,
57 name: Cons<'el>,
59 arguments: Vec<Rust<'el>>,
61}
62
63impl<'el> Name<'el> {
64 fn format(&self, out: &mut Formatter, extra: &mut (), level: usize) -> fmt::Result {
66 if let Some(reference) = self.reference.as_ref() {
67 match *reference {
68 Reference::StaticRef => {
69 out.write_str("&'static ")?;
70 }
71 Reference::Named(ref name) => {
72 out.write_str("&'")?;
73 out.write_str(name.as_ref())?;
74 out.write_str(" ")?;
75 }
76 Reference::Ref => {
77 out.write_str("&")?;
78 }
79 }
80 }
81
82 out.write_str(self.name.as_ref())?;
83
84 if !self.arguments.is_empty() {
85 let mut it = self.arguments.iter().peekable();
86
87 out.write_str("<")?;
88
89 while let Some(n) = it.next() {
90 n.format(out, extra, level + 1)?;
91
92 if it.peek().is_some() {
93 out.write_str(", ")?;
94 }
95 }
96
97 out.write_str(">")?;
98 }
99
100 Ok(())
101 }
102
103 pub fn with_arguments(self, arguments: Vec<Rust<'el>>) -> Name<'el> {
105 Name {
106 arguments: arguments,
107 ..self
108 }
109 }
110
111 pub fn reference<R: Into<Reference<'el>>>(self, reference: R) -> Name<'el> {
113 Name {
114 reference: Some(reference.into()),
115 ..self
116 }
117 }
118}
119
120impl<'el> From<Cons<'el>> for Name<'el> {
121 fn from(value: Cons<'el>) -> Self {
122 Name {
123 reference: None,
124 name: value,
125 arguments: vec![],
126 }
127 }
128}
129
130#[derive(Debug, Clone, Hash, PartialOrd, Ord, PartialEq, Eq)]
132pub struct Rust<'el> {
133 module: Option<Cons<'el>>,
135 alias: Option<Cons<'el>>,
137 name: Name<'el>,
139 qualified: bool,
141}
142
143into_tokens_impl_from!(Rust<'el>, Rust<'el>);
144into_tokens_impl_from!(&'el Rust<'el>, Rust<'el>);
145
146impl<'el> Rust<'el> {
147 fn walk_custom<'a, 'b: 'a>(
148 custom: &'a Rust<'b>,
149 modules: &mut BTreeSet<(Cons<'a>, Option<&'a Cons<'b>>)>,
150 ) {
151 if let Some(module) = custom.module.as_ref() {
152 if custom.qualified {
153 let module = Cons::from(format!("{}::{}", module, custom.name.name.as_ref()));
154 modules.insert((module, custom.alias.as_ref()));
155 } else {
156 modules.insert((Cons::from(module.as_ref()), custom.alias.as_ref()));
157 }
158 }
159
160 for arg in &custom.name.arguments {
161 Self::walk_custom(arg, modules);
162 }
163 }
164
165 fn imports<'a>(tokens: &'a Tokens<'a, Self>) -> Option<Tokens<'a, Self>> {
166 let mut modules = BTreeSet::new();
167
168 for custom in tokens.walk_custom() {
169 Rust::walk_custom(&custom, &mut modules);
170 }
171
172 if modules.is_empty() {
173 return None;
174 }
175
176 let mut out = Tokens::new();
177
178 for (module, alias) in modules {
179 let mut s = Tokens::new();
180
181 s.append("use ");
182 s.append(module);
183
184 if let Some(alias) = alias {
185 s.append(" as ");
186 s.append(alias.as_ref());
187 }
188
189 s.append(";");
190
191 out.push(s);
192 }
193
194 Some(out)
195 }
196
197 pub fn alias<A: Into<Cons<'el>>>(self, alias: A) -> Rust<'el> {
199 Rust {
200 alias: Some(alias.into()),
201 ..self
202 }
203 }
204
205 pub fn with_arguments(self, arguments: Vec<Rust<'el>>) -> Rust<'el> {
207 Rust {
208 name: self.name.with_arguments(arguments),
209 ..self
210 }
211 }
212
213 pub fn qualified(self) -> Rust<'el> {
215 Rust {
216 qualified: true,
217 ..self
218 }
219 }
220
221 pub fn reference<R: Into<Reference<'el>>>(self, reference: R) -> Rust<'el> {
223 Rust {
224 module: self.module,
225 name: self.name.reference(reference),
226 alias: self.alias,
227 qualified: self.qualified,
228 }
229 }
230}
231
232impl<'el> Custom for Rust<'el> {
233 type Extra = ();
234
235 fn format(&self, out: &mut Formatter, extra: &mut Self::Extra, level: usize) -> fmt::Result {
236 if let Some(alias) = self.alias.as_ref() {
237 out.write_str(alias)?;
238 out.write_str(SEP)?;
239 } else if let Some(part) = self.module.as_ref().and_then(|m| m.split(SEP).last()) {
240 out.write_str(part)?;
241 out.write_str(SEP)?;
242 }
243
244 self.name.format(out, extra, level)
245 }
246
247 fn quote_string(out: &mut Formatter, input: &str) -> fmt::Result {
248 out.write_char('"')?;
249
250 for c in input.chars() {
251 match c {
252 '\t' => out.write_str("\\t")?,
253 '\n' => out.write_str("\\n")?,
254 '\r' => out.write_str("\\r")?,
255 '\'' => out.write_str("\\'")?,
256 '"' => out.write_str("\\\"")?,
257 '\\' => out.write_str("\\\\")?,
258 c => out.write_char(c)?,
259 };
260 }
261
262 out.write_char('"')?;
263 Ok(())
264 }
265
266 fn write_file<'a>(
267 tokens: Tokens<'a, Self>,
268 out: &mut Formatter,
269 extra: &mut Self::Extra,
270 level: usize,
271 ) -> fmt::Result {
272 let mut toks: Tokens<Self> = Tokens::new();
273
274 if let Some(imports) = Self::imports(&tokens) {
275 toks.push(imports);
276 }
277
278 toks.push_ref(&tokens);
279 toks.join_line_spacing().format(out, extra, level)
280 }
281}
282
283pub fn imported<'a, M, N>(module: M, name: N) -> Rust<'a>
285where
286 M: Into<Cons<'a>>,
287 N: Into<Cons<'a>>,
288{
289 Rust {
290 module: Some(module.into()),
291 alias: None,
292 name: Name::from(name.into()),
293 qualified: false,
294 }
295}
296
297pub fn local<'a, N>(name: N) -> Rust<'a>
299where
300 N: Into<Cons<'a>>,
301{
302 Rust {
303 module: None,
304 alias: None,
305 name: Name::from(name.into()),
306 qualified: false,
307 }
308}
309
310#[cfg(test)]
311mod tests {
312 use super::{imported, local};
313 use quoted::Quoted;
314 use rust::Rust;
315 use tokens::Tokens;
316
317 #[test]
318 fn test_string() {
319 let mut toks: Tokens<Rust> = Tokens::new();
320 toks.append("hello \n world".quoted());
321 let res = toks.to_string();
322
323 assert_eq!(Ok("\"hello \\n world\""), res.as_ref().map(|s| s.as_str()));
324 }
325
326 #[test]
327 fn test_imported() {
328 let dbg = imported("std::fmt", "Debug");
329 let mut toks: Tokens<Rust> = Tokens::new();
330 toks.push(toks!(&dbg));
331
332 assert_eq!(
333 Ok("use std::fmt;\n\nfmt::Debug\n"),
334 toks.to_file().as_ref().map(|s| s.as_str())
335 );
336 }
337
338 #[test]
339 fn test_imported_alias() {
340 let dbg = imported("std::fmt", "Debug").alias("dbg");
341 let mut toks: Tokens<Rust> = Tokens::new();
342 toks.push(toks!(&dbg));
343
344 assert_eq!(
345 Ok("use std::fmt as dbg;\n\ndbg::Debug\n"),
346 toks.to_file().as_ref().map(|s| s.as_str())
347 );
348 }
349
350 #[test]
351 fn test_imported_with_arguments() {
352 let dbg = imported("std::fmt", "Debug")
353 .alias("dbg")
354 .with_arguments(vec![local("T"), local("U")]);
355 let mut toks: Tokens<Rust> = Tokens::new();
356 toks.push(toks!(&dbg));
357
358 assert_eq!(
359 Ok("use std::fmt as dbg;\n\ndbg::Debug<T, U>\n"),
360 toks.to_file().as_ref().map(|s| s.as_str())
361 );
362 }
363}