]> git.lizzy.rs Git - rust.git/blob - src/libsyntax_ext/format_foreign.rs
Auto merge of #38992 - nagisa:i128-minvallit, r=eddyb
[rust.git] / src / libsyntax_ext / format_foreign.rs
1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 macro_rules! try_opt {
12     ($e:expr) => {
13         match $e {
14             Some(v) => v,
15             None => return None,
16         }
17     };
18 }
19
20 pub mod printf {
21     use super::strcursor::StrCursor as Cur;
22
23     /// Represents a single `printf`-style substitution.
24     #[derive(Clone, Eq, PartialEq, Debug)]
25     pub enum Substitution<'a> {
26         /// A formatted output substitution.
27         Format(Format<'a>),
28         /// A literal `%%` escape.
29         Escape,
30     }
31
32     impl<'a> Substitution<'a> {
33         pub fn as_str(&self) -> &str {
34             match *self {
35                 Substitution::Format(ref fmt) => fmt.span,
36                 Substitution::Escape => "%%",
37             }
38         }
39
40         /// Translate this substitution into an equivalent Rust formatting directive.
41         ///
42         /// This ignores cases where the substitution does not have an exact equivalent, or where
43         /// the substitution would be unnecessary.
44         pub fn translate(&self) -> Option<String> {
45             match *self {
46                 Substitution::Format(ref fmt) => fmt.translate(),
47                 Substitution::Escape => None,
48             }
49         }
50     }
51
52     #[derive(Clone, Eq, PartialEq, Debug)]
53     /// A single `printf`-style formatting directive.
54     pub struct Format<'a> {
55         /// The entire original formatting directive.
56         pub span: &'a str,
57         /// The (1-based) parameter to be converted.
58         pub parameter: Option<u16>,
59         /// Formatting flags.
60         pub flags: &'a str,
61         /// Minimum width of the output.
62         pub width: Option<Num>,
63         /// Precision of the conversion.
64         pub precision: Option<Num>,
65         /// Length modifier for the conversion.
66         pub length: Option<&'a str>,
67         /// Type of parameter being converted.
68         pub type_: &'a str,
69     }
70
71     impl<'a> Format<'a> {
72         /// Translate this directive into an equivalent Rust formatting directive.
73         ///
74         /// Returns `None` in cases where the `printf` directive does not have an exact Rust
75         /// equivalent, rather than guessing.
76         pub fn translate(&self) -> Option<String> {
77             use std::fmt::Write;
78
79             let (c_alt, c_zero, c_left, c_plus) = {
80                 let mut c_alt = false;
81                 let mut c_zero = false;
82                 let mut c_left = false;
83                 let mut c_plus = false;
84                 for c in self.flags.chars() {
85                     match c {
86                         '#' => c_alt = true,
87                         '0' => c_zero = true,
88                         '-' => c_left = true,
89                         '+' => c_plus = true,
90                         _ => return None
91                     }
92                 }
93                 (c_alt, c_zero, c_left, c_plus)
94             };
95
96             // Has a special form in Rust for numbers.
97             let fill = if c_zero { Some("0") } else { None };
98
99             let align = if c_left { Some("<") } else { None };
100
101             // Rust doesn't have an equivalent to the `' '` flag.
102             let sign = if c_plus { Some("+") } else { None };
103
104             // Not *quite* the same, depending on the type...
105             let alt = c_alt;
106
107             let width = match self.width {
108                 Some(Num::Next) => {
109                     // NOTE: Rust doesn't support this.
110                     return None;
111                 }
112                 w @ Some(Num::Arg(_)) => w,
113                 w @ Some(Num::Num(_)) => w,
114                 None => None,
115             };
116
117             let precision = self.precision;
118
119             // NOTE: although length *can* have an effect, we can't duplicate the effect in Rust, so
120             // we just ignore it.
121
122             let (type_, use_zero_fill, is_int) = match self.type_ {
123                 "d" | "i" | "u" => (None, true, true),
124                 "f" | "F" => (None, false, false),
125                 "s" | "c" => (None, false, false),
126                 "e" | "E" => (Some(self.type_), true, false),
127                 "x" | "X" | "o" => (Some(self.type_), true, true),
128                 "p" => (Some(self.type_), false, true),
129                 "g" => (Some("e"), true, false),
130                 "G" => (Some("E"), true, false),
131                 _ => return None,
132             };
133
134             let (fill, width, precision) = match (is_int, width, precision) {
135                 (true, Some(_), Some(_)) => {
136                     // Rust can't duplicate this insanity.
137                     return None;
138                 },
139                 (true, None, Some(p)) => (Some("0"), Some(p), None),
140                 (true, w, None) => (fill, w, None),
141                 (false, w, p) => (fill, w, p),
142             };
143
144             let align = match (self.type_, width.is_some(), align.is_some()) {
145                 ("s", true, false) => Some(">"),
146                 _ => align,
147             };
148
149             let (fill, zero_fill) = match (fill, use_zero_fill) {
150                 (Some("0"), true) => (None, true),
151                 (fill, _) => (fill, false),
152             };
153
154             let alt = match type_ {
155                 Some("x") | Some("X") => alt,
156                 _ => false,
157             };
158
159             let has_options = fill.is_some()
160                 || align.is_some()
161                 || sign.is_some()
162                 || alt
163                 || zero_fill
164                 || width.is_some()
165                 || precision.is_some()
166                 || type_.is_some()
167                 ;
168
169             // Initialise with a rough guess.
170             let cap = self.span.len() + if has_options { 2 } else { 0 };
171             let mut s = String::with_capacity(cap);
172
173             s.push_str("{");
174
175             if let Some(arg) = self.parameter {
176                 try_opt!(write!(s, "{}", try_opt!(arg.checked_sub(1))).ok());
177             }
178
179             if has_options {
180                 s.push_str(":");
181
182                 let align = if let Some(fill) = fill {
183                     s.push_str(fill);
184                     align.or(Some(">"))
185                 } else {
186                     align
187                 };
188
189                 if let Some(align) = align {
190                     s.push_str(align);
191                 }
192
193                 if let Some(sign) = sign {
194                     s.push_str(sign);
195                 }
196
197                 if alt {
198                     s.push_str("#");
199                 }
200
201                 if zero_fill {
202                     s.push_str("0");
203                 }
204
205                 if let Some(width) = width {
206                     try_opt!(width.translate(&mut s).ok());
207                 }
208
209                 if let Some(precision) = precision {
210                     s.push_str(".");
211                     try_opt!(precision.translate(&mut s).ok());
212                 }
213
214                 if let Some(type_) = type_ {
215                     s.push_str(type_);
216                 }
217             }
218
219             s.push_str("}");
220             Some(s)
221         }
222     }
223
224     /// A general number used in a `printf` formatting directive.
225     #[derive(Copy, Clone, Eq, PartialEq, Debug)]
226     pub enum Num {
227         // The range of these values is technically bounded by `NL_ARGMAX`... but, at least for GNU
228         // libc, it apparently has no real fixed limit.  A `u16` is used here on the basis that it
229         // is *vanishingly* unlikely that *anyone* is going to try formatting something wider, or
230         // with more precision, than 32 thousand positions which is so wide it couldn't possibly fit
231         // on a screen.
232
233         /// A specific, fixed value.
234         Num(u16),
235         /// The value is derived from a positional argument.
236         Arg(u16),
237         /// The value is derived from the "next" unconverted argument.
238         Next,
239     }
240
241     impl Num {
242         fn from_str(s: &str, arg: Option<&str>) -> Self {
243             if let Some(arg) = arg {
244                 Num::Arg(arg.parse().expect(&format!("invalid format arg `{:?}`", arg)))
245             } else if s == "*" {
246                 Num::Next
247             } else {
248                 Num::Num(s.parse().expect(&format!("invalid format num `{:?}`", s)))
249             }
250         }
251
252         fn translate(&self, s: &mut String) -> ::std::fmt::Result {
253             use std::fmt::Write;
254             match *self {
255                 Num::Num(n) => write!(s, "{}", n),
256                 Num::Arg(n) => {
257                     let n = try!(n.checked_sub(1).ok_or(::std::fmt::Error));
258                     write!(s, "{}$", n)
259                 },
260                 Num::Next => write!(s, "*"),
261             }
262         }
263     }
264
265     /// Returns an iterator over all substitutions in a given string.
266     pub fn iter_subs(s: &str) -> Substitutions {
267         Substitutions {
268             s: s,
269         }
270     }
271
272     /// Iterator over substitutions in a string.
273     pub struct Substitutions<'a> {
274         s: &'a str,
275     }
276
277     impl<'a> Iterator for Substitutions<'a> {
278         type Item = Substitution<'a>;
279         fn next(&mut self) -> Option<Self::Item> {
280             match parse_next_substitution(self.s) {
281                 Some((sub, tail)) => {
282                     self.s = tail;
283                     Some(sub)
284                 },
285                 None => None,
286             }
287         }
288     }
289
290     enum State {
291         Start,
292         Flags,
293         Width,
294         WidthArg,
295         Prec,
296         PrecInner,
297         Length,
298         Type,
299     }
300
301     /// Parse the next substitution from the input string.
302     pub fn parse_next_substitution(s: &str) -> Option<(Substitution, &str)> {
303         use self::State::*;
304
305         let at = {
306             let start = try_opt!(s.find('%'));
307             match s[start+1..].chars().next() {
308                 Some('%') => return Some((Substitution::Escape, &s[start+2..])),
309                 Some(_) => {/* fall-through */},
310                 None => return None,
311             }
312
313             Cur::new_at_start(&s[start..])
314         };
315
316         // This is meant to be a translation of the following regex:
317         //
318         // ```regex
319         // (?x)
320         // ^ %
321         // (?: (?P<parameter> \d+) \$ )?
322         // (?P<flags> [-+ 0\#']* )
323         // (?P<width> \d+ | \* (?: (?P<widtha> \d+) \$ )? )?
324         // (?: \. (?P<precision> \d+ | \* (?: (?P<precisiona> \d+) \$ )? ) )?
325         // (?P<length>
326         //     # Standard
327         //     hh | h | ll | l | L | z | j | t
328         //
329         //     # Other
330         //     | I32 | I64 | I | q
331         // )?
332         // (?P<type> . )
333         // ```
334
335         // Used to establish the full span at the end.
336         let start = at;
337         // The current position within the string.
338         let mut at = try_opt!(at.at_next_cp());
339         // `c` is the next codepoint, `next` is a cursor after it.
340         let (mut c, mut next) = try_opt!(at.next_cp());
341
342         // Update `at`, `c`, and `next`, exiting if we're out of input.
343         macro_rules! move_to {
344             ($cur:expr) => {
345                 {
346                     at = $cur;
347                     let (c_, next_) = try_opt!(at.next_cp());
348                     c = c_;
349                     next = next_;
350                 }
351             };
352         }
353
354         // Constructs a result when parsing fails.
355         //
356         // Note: `move` used to capture copies of the cursors as they are *now*.
357         let fallback = move || {
358             return Some((
359                 Substitution::Format(Format {
360                     span: start.slice_between(next).unwrap(),
361                     parameter: None,
362                     flags: "",
363                     width: None,
364                     precision: None,
365                     length: None,
366                     type_: at.slice_between(next).unwrap(),
367                 }),
368                 next.slice_after()
369             ));
370         };
371
372         // Next parsing state.
373         let mut state = Start;
374
375         // Sadly, Rust isn't *quite* smart enough to know these *must* be initialised by the end.
376         let mut parameter: Option<u16> = None;
377         let mut flags: &str = "";
378         let mut width: Option<Num> = None;
379         let mut precision: Option<Num> = None;
380         let mut length: Option<&str> = None;
381         let mut type_: &str = "";
382         let end: Cur;
383
384         if let Start = state {
385             match c {
386                 '1'...'9' => {
387                     let end = at_next_cp_while(next, is_digit);
388                     match end.next_cp() {
389                         // Yes, this *is* the parameter.
390                         Some(('$', end2)) => {
391                             state = Flags;
392                             parameter = Some(at.slice_between(end).unwrap().parse().unwrap());
393                             move_to!(end2);
394                         },
395                         // Wait, no, actually, it's the width.
396                         Some(_) => {
397                             state = Prec;
398                             parameter = None;
399                             flags = "";
400                             width = Some(Num::from_str(at.slice_between(end).unwrap(), None));
401                             move_to!(end);
402                         },
403                         // It's invalid, is what it is.
404                         None => return fallback(),
405                     }
406                 },
407                 _ => {
408                     state = Flags;
409                     parameter = None;
410                     move_to!(at);
411                 }
412             }
413         }
414
415         if let Flags = state {
416             let end = at_next_cp_while(at, is_flag);
417             state = Width;
418             flags = at.slice_between(end).unwrap();
419             move_to!(end);
420         }
421
422         if let Width = state {
423             match c {
424                 '*' => {
425                     state = WidthArg;
426                     move_to!(next);
427                 },
428                 '1' ... '9' => {
429                     let end = at_next_cp_while(next, is_digit);
430                     state = Prec;
431                     width = Some(Num::from_str(at.slice_between(end).unwrap(), None));
432                     move_to!(end);
433                 },
434                 _ => {
435                     state = Prec;
436                     width = None;
437                     move_to!(at);
438                 }
439             }
440         }
441
442         if let WidthArg = state {
443             let end = at_next_cp_while(at, is_digit);
444             match end.next_cp() {
445                 Some(('$', end2)) => {
446                     state = Prec;
447                     width = Some(Num::from_str("", Some(at.slice_between(end).unwrap())));
448                     move_to!(end2);
449                 },
450                 _ => {
451                     state = Prec;
452                     width = Some(Num::Next);
453                     move_to!(end);
454                 }
455             }
456         }
457
458         if let Prec = state {
459             match c {
460                 '.' => {
461                     state = PrecInner;
462                     move_to!(next);
463                 },
464                 _ => {
465                     state = Length;
466                     precision = None;
467                     move_to!(at);
468                 }
469             }
470         }
471
472         if let PrecInner = state {
473             match c {
474                 '*' => {
475                     let end = at_next_cp_while(next, is_digit);
476                     match end.next_cp() {
477                         Some(('$', end2)) => {
478                             state = Length;
479                             precision = Some(Num::from_str("*", next.slice_between(end)));
480                             move_to!(end2);
481                         },
482                         _ => {
483                             state = Length;
484                             precision = Some(Num::Next);
485                             move_to!(end);
486                         }
487                     }
488                 },
489                 '0' ... '9' => {
490                     let end = at_next_cp_while(next, is_digit);
491                     state = Length;
492                     precision = Some(Num::from_str(at.slice_between(end).unwrap(), None));
493                     move_to!(end);
494                 },
495                 _ => return fallback(),
496             }
497         }
498
499         if let Length = state {
500             let c1_next1 = next.next_cp();
501             match (c, c1_next1) {
502                 ('h', Some(('h', next1)))
503                 | ('l', Some(('l', next1)))
504                 => {
505                     state = Type;
506                     length = Some(at.slice_between(next1).unwrap());
507                     move_to!(next1);
508                 },
509
510                 ('h', _) | ('l', _) | ('L', _)
511                 | ('z', _) | ('j', _) | ('t', _)
512                 | ('q', _)
513                 => {
514                     state = Type;
515                     length = Some(at.slice_between(next).unwrap());
516                     move_to!(next);
517                 },
518
519                 ('I', _) => {
520                     let end = next.at_next_cp()
521                         .and_then(|end| end.at_next_cp())
522                         .map(|end| (next.slice_between(end).unwrap(), end));
523                     let end = match end {
524                         Some(("32", end)) => end,
525                         Some(("64", end)) => end,
526                         _ => next
527                     };
528                     state = Type;
529                     length = Some(at.slice_between(end).unwrap());
530                     move_to!(end);
531                 },
532
533                 _ => {
534                     state = Type;
535                     length = None;
536                     move_to!(at);
537                 }
538             }
539         }
540
541         if let Type = state {
542             drop(c);
543             type_ = at.slice_between(next).unwrap();
544
545             // Don't use `move_to!` here, as we *can* be at the end of the input.
546             at = next;
547         }
548
549         drop(c);
550         drop(next);
551
552         end = at;
553
554         let f = Format {
555             span: start.slice_between(end).unwrap(),
556             parameter: parameter,
557             flags: flags,
558             width: width,
559             precision: precision,
560             length: length,
561             type_: type_,
562         };
563         Some((Substitution::Format(f), end.slice_after()))
564     }
565
566     fn at_next_cp_while<F>(mut cur: Cur, mut pred: F) -> Cur
567     where F: FnMut(char) -> bool {
568         loop {
569             match cur.next_cp() {
570                 Some((c, next)) => if pred(c) {
571                     cur = next;
572                 } else {
573                     return cur;
574                 },
575                 None => return cur,
576             }
577         }
578     }
579
580     fn is_digit(c: char) -> bool {
581         match c {
582             '0' ... '9' => true,
583             _ => false
584         }
585     }
586
587     fn is_flag(c: char) -> bool {
588         match c {
589             '0' | '-' | '+' | ' ' | '#' | '\'' => true,
590             _ => false
591         }
592     }
593
594     #[cfg(test)]
595     mod tests {
596         use super::{
597             Format as F,
598             Num as N,
599             Substitution as S,
600             iter_subs,
601             parse_next_substitution as pns,
602         };
603
604         macro_rules! assert_eq_pnsat {
605             ($lhs:expr, $rhs:expr) => {
606                 assert_eq!(
607                     pns($lhs).and_then(|(s, _)| s.translate()),
608                     $rhs.map(<String as From<&str>>::from)
609                 )
610             };
611         }
612
613         #[test]
614         fn test_escape() {
615             assert_eq!(pns("has no escapes"), None);
616             assert_eq!(pns("has no escapes, either %"), None);
617             assert_eq!(pns("*so* has a %% escape"), Some((S::Escape," escape")));
618             assert_eq!(pns("%% leading escape"), Some((S::Escape, " leading escape")));
619             assert_eq!(pns("trailing escape %%"), Some((S::Escape, "")));
620         }
621
622         #[test]
623         fn test_parse() {
624             macro_rules! assert_pns_eq_sub {
625                 ($in_:expr, {
626                     $param:expr, $flags:expr,
627                     $width:expr, $prec:expr, $len:expr, $type_:expr,
628                 }) => {
629                     assert_eq!(
630                         pns(concat!($in_, "!")),
631                         Some((
632                             S::Format(F {
633                                 span: $in_,
634                                 parameter: $param,
635                                 flags: $flags,
636                                 width: $width,
637                                 precision: $prec,
638                                 length: $len,
639                                 type_: $type_,
640                             }),
641                             "!"
642                         ))
643                     )
644                 };
645             }
646
647             assert_pns_eq_sub!("%!",
648                 { None, "", None, None, None, "!", });
649             assert_pns_eq_sub!("%c",
650                 { None, "", None, None, None, "c", });
651             assert_pns_eq_sub!("%s",
652                 { None, "", None, None, None, "s", });
653             assert_pns_eq_sub!("%06d",
654                 { None, "0", Some(N::Num(6)), None, None, "d", });
655             assert_pns_eq_sub!("%4.2f",
656                 { None, "", Some(N::Num(4)), Some(N::Num(2)), None, "f", });
657             assert_pns_eq_sub!("%#x",
658                 { None, "#", None, None, None, "x", });
659             assert_pns_eq_sub!("%-10s",
660                 { None, "-", Some(N::Num(10)), None, None, "s", });
661             assert_pns_eq_sub!("%*s",
662                 { None, "", Some(N::Next), None, None, "s", });
663             assert_pns_eq_sub!("%-10.*s",
664                 { None, "-", Some(N::Num(10)), Some(N::Next), None, "s", });
665             assert_pns_eq_sub!("%-*.*s",
666                 { None, "-", Some(N::Next), Some(N::Next), None, "s", });
667             assert_pns_eq_sub!("%.6i",
668                 { None, "", None, Some(N::Num(6)), None, "i", });
669             assert_pns_eq_sub!("%+i",
670                 { None, "+", None, None, None, "i", });
671             assert_pns_eq_sub!("%08X",
672                 { None, "0", Some(N::Num(8)), None, None, "X", });
673             assert_pns_eq_sub!("%lu",
674                 { None, "", None, None, Some("l"), "u", });
675             assert_pns_eq_sub!("%Iu",
676                 { None, "", None, None, Some("I"), "u", });
677             assert_pns_eq_sub!("%I32u",
678                 { None, "", None, None, Some("I32"), "u", });
679             assert_pns_eq_sub!("%I64u",
680                 { None, "", None, None, Some("I64"), "u", });
681             assert_pns_eq_sub!("%'d",
682                 { None, "'", None, None, None, "d", });
683             assert_pns_eq_sub!("%10s",
684                 { None, "", Some(N::Num(10)), None, None, "s", });
685             assert_pns_eq_sub!("%-10.10s",
686                 { None, "-", Some(N::Num(10)), Some(N::Num(10)), None, "s", });
687             assert_pns_eq_sub!("%1$d",
688                 { Some(1), "", None, None, None, "d", });
689             assert_pns_eq_sub!("%2$.*3$d",
690                 { Some(2), "", None, Some(N::Arg(3)), None, "d", });
691             assert_pns_eq_sub!("%1$*2$.*3$d",
692                 { Some(1), "", Some(N::Arg(2)), Some(N::Arg(3)), None, "d", });
693             assert_pns_eq_sub!("%-8ld",
694                 { None, "-", Some(N::Num(8)), None, Some("l"), "d", });
695         }
696
697         #[test]
698         fn test_iter() {
699             let s = "The %d'th word %% is: `%.*s` %!\n";
700             let subs: Vec<_> = iter_subs(s).map(|sub| sub.translate()).collect();
701             assert_eq!(
702                 subs.iter().map(|ms| ms.as_ref().map(|s| &s[..])).collect::<Vec<_>>(),
703                 vec![Some("{}"), None, Some("{:.*}"), None]
704             );
705         }
706
707         /// Check that the translations are what we expect.
708         #[test]
709         fn test_trans() {
710             assert_eq_pnsat!("%c", Some("{}"));
711             assert_eq_pnsat!("%d", Some("{}"));
712             assert_eq_pnsat!("%u", Some("{}"));
713             assert_eq_pnsat!("%x", Some("{:x}"));
714             assert_eq_pnsat!("%X", Some("{:X}"));
715             assert_eq_pnsat!("%e", Some("{:e}"));
716             assert_eq_pnsat!("%E", Some("{:E}"));
717             assert_eq_pnsat!("%f", Some("{}"));
718             assert_eq_pnsat!("%g", Some("{:e}"));
719             assert_eq_pnsat!("%G", Some("{:E}"));
720             assert_eq_pnsat!("%s", Some("{}"));
721             assert_eq_pnsat!("%p", Some("{:p}"));
722
723             assert_eq_pnsat!("%06d",        Some("{:06}"));
724             assert_eq_pnsat!("%4.2f",       Some("{:4.2}"));
725             assert_eq_pnsat!("%#x",         Some("{:#x}"));
726             assert_eq_pnsat!("%-10s",       Some("{:<10}"));
727             assert_eq_pnsat!("%*s",         None);
728             assert_eq_pnsat!("%-10.*s",     Some("{:<10.*}"));
729             assert_eq_pnsat!("%-*.*s",      None);
730             assert_eq_pnsat!("%.6i",        Some("{:06}"));
731             assert_eq_pnsat!("%+i",         Some("{:+}"));
732             assert_eq_pnsat!("%08X",        Some("{:08X}"));
733             assert_eq_pnsat!("%lu",         Some("{}"));
734             assert_eq_pnsat!("%Iu",         Some("{}"));
735             assert_eq_pnsat!("%I32u",       Some("{}"));
736             assert_eq_pnsat!("%I64u",       Some("{}"));
737             assert_eq_pnsat!("%'d",         None);
738             assert_eq_pnsat!("%10s",        Some("{:>10}"));
739             assert_eq_pnsat!("%-10.10s",    Some("{:<10.10}"));
740             assert_eq_pnsat!("%1$d",        Some("{0}"));
741             assert_eq_pnsat!("%2$.*3$d",    Some("{1:02$}"));
742             assert_eq_pnsat!("%1$*2$.*3$s", Some("{0:>1$.2$}"));
743             assert_eq_pnsat!("%-8ld",       Some("{:<8}"));
744         }
745     }
746 }
747
748 pub mod shell {
749     use super::strcursor::StrCursor as Cur;
750
751     #[derive(Clone, Eq, PartialEq, Debug)]
752     pub enum Substitution<'a> {
753         Ordinal(u8),
754         Name(&'a str),
755         Escape,
756     }
757
758     impl<'a> Substitution<'a> {
759         pub fn as_str(&self) -> String {
760             match *self {
761                 Substitution::Ordinal(n) => format!("${}", n),
762                 Substitution::Name(n) => format!("${}", n),
763                 Substitution::Escape => "$$".into(),
764             }
765         }
766
767         pub fn translate(&self) -> Option<String> {
768             match *self {
769                 Substitution::Ordinal(n) => Some(format!("{{{}}}", n)),
770                 Substitution::Name(n) => Some(format!("{{{}}}", n)),
771                 Substitution::Escape => None,
772             }
773         }
774     }
775
776     /// Returns an iterator over all substitutions in a given string.
777     pub fn iter_subs(s: &str) -> Substitutions {
778         Substitutions {
779             s: s,
780         }
781     }
782
783     /// Iterator over substitutions in a string.
784     pub struct Substitutions<'a> {
785         s: &'a str,
786     }
787
788     impl<'a> Iterator for Substitutions<'a> {
789         type Item = Substitution<'a>;
790         fn next(&mut self) -> Option<Self::Item> {
791             match parse_next_substitution(self.s) {
792                 Some((sub, tail)) => {
793                     self.s = tail;
794                     Some(sub)
795                 },
796                 None => None,
797             }
798         }
799     }
800
801     /// Parse the next substitution from the input string.
802     pub fn parse_next_substitution(s: &str) -> Option<(Substitution, &str)> {
803         let at = {
804             let start = try_opt!(s.find('$'));
805             match s[start+1..].chars().next() {
806                 Some('$') => return Some((Substitution::Escape, &s[start+2..])),
807                 Some(c @ '0' ... '9') => {
808                     let n = (c as u8) - b'0';
809                     return Some((Substitution::Ordinal(n), &s[start+2..]));
810                 },
811                 Some(_) => {/* fall-through */},
812                 None => return None,
813             }
814
815             Cur::new_at_start(&s[start..])
816         };
817
818         let at = try_opt!(at.at_next_cp());
819         match at.next_cp() {
820             Some((c, inner)) => {
821                 if !is_ident_head(c) {
822                     None
823                 } else {
824                     let end = at_next_cp_while(inner, is_ident_tail);
825                     Some((Substitution::Name(at.slice_between(end).unwrap()), end.slice_after()))
826                 }
827             },
828             _ => None
829         }
830     }
831
832     fn at_next_cp_while<F>(mut cur: Cur, mut pred: F) -> Cur
833     where F: FnMut(char) -> bool {
834         loop {
835             match cur.next_cp() {
836                 Some((c, next)) => if pred(c) {
837                     cur = next;
838                 } else {
839                     return cur;
840                 },
841                 None => return cur,
842             }
843         }
844     }
845
846     fn is_ident_head(c: char) -> bool {
847         match c {
848             'a' ... 'z' | 'A' ... 'Z' | '_' => true,
849             _ => false
850         }
851     }
852
853     fn is_ident_tail(c: char) -> bool {
854         match c {
855             '0' ... '9' => true,
856             c => is_ident_head(c)
857         }
858     }
859
860     #[cfg(test)]
861     mod tests {
862         use super::{
863             Substitution as S,
864             parse_next_substitution as pns,
865         };
866
867         macro_rules! assert_eq_pnsat {
868             ($lhs:expr, $rhs:expr) => {
869                 assert_eq!(
870                     pns($lhs).and_then(|(f, _)| f.translate()),
871                     $rhs.map(<String as From<&str>>::from)
872                 )
873             };
874         }
875
876         #[test]
877         fn test_escape() {
878             assert_eq!(pns("has no escapes"), None);
879             assert_eq!(pns("has no escapes, either $"), None);
880             assert_eq!(pns("*so* has a $$ escape"), Some((S::Escape, " escape")));
881             assert_eq!(pns("$$ leading escape"), Some((S::Escape, " leading escape")));
882             assert_eq!(pns("trailing escape $$"), Some((S::Escape, "")));
883         }
884
885         #[test]
886         fn test_parse() {
887             macro_rules! assert_pns_eq_sub {
888                 ($in_:expr, $kind:ident($arg:expr)) => {
889                     assert_eq!(pns(concat!($in_, "!")), Some((S::$kind($arg.into()), "!")))
890                 };
891             }
892
893             assert_pns_eq_sub!("$0", Ordinal(0));
894             assert_pns_eq_sub!("$1", Ordinal(1));
895             assert_pns_eq_sub!("$9", Ordinal(9));
896             assert_pns_eq_sub!("$N", Name("N"));
897             assert_pns_eq_sub!("$NAME", Name("NAME"));
898         }
899
900         #[test]
901         fn test_iter() {
902             use super::iter_subs;
903             let s = "The $0'th word $$ is: `$WORD` $!\n";
904             let subs: Vec<_> = iter_subs(s).map(|sub| sub.translate()).collect();
905             assert_eq!(
906                 subs.iter().map(|ms| ms.as_ref().map(|s| &s[..])).collect::<Vec<_>>(),
907                 vec![Some("{0}"), None, Some("{WORD}")]
908             );
909         }
910
911         #[test]
912         fn test_trans() {
913             assert_eq_pnsat!("$0", Some("{0}"));
914             assert_eq_pnsat!("$9", Some("{9}"));
915             assert_eq_pnsat!("$1", Some("{1}"));
916             assert_eq_pnsat!("$10", Some("{1}"));
917             assert_eq_pnsat!("$stuff", Some("{stuff}"));
918             assert_eq_pnsat!("$NAME", Some("{NAME}"));
919             assert_eq_pnsat!("$PREFIX/bin", Some("{PREFIX}"));
920         }
921
922     }
923 }
924
925 mod strcursor {
926     use std;
927
928     pub struct StrCursor<'a> {
929         s: &'a str,
930         at: usize,
931     }
932
933     impl<'a> StrCursor<'a> {
934         pub fn new_at_start(s: &'a str) -> StrCursor<'a> {
935             StrCursor {
936                 s: s,
937                 at: 0,
938             }
939         }
940
941         pub fn at_next_cp(mut self) -> Option<StrCursor<'a>> {
942             match self.try_seek_right_cp() {
943                 true => Some(self),
944                 false => None
945             }
946         }
947
948         pub fn next_cp(mut self) -> Option<(char, StrCursor<'a>)> {
949             let cp = match self.cp_after() {
950                 Some(cp) => cp,
951                 None => return None,
952             };
953             self.seek_right(cp.len_utf8());
954             Some((cp, self))
955         }
956
957         fn slice_before(&self) -> &'a str {
958             &self.s[0..self.at]
959         }
960
961         pub fn slice_after(&self) -> &'a str {
962             &self.s[self.at..]
963         }
964
965         pub fn slice_between(&self, until: StrCursor<'a>) -> Option<&'a str> {
966             if !str_eq_literal(self.s, until.s) {
967                 None
968             } else {
969                 use std::cmp::{max, min};
970                 let beg = min(self.at, until.at);
971                 let end = max(self.at, until.at);
972                 Some(&self.s[beg..end])
973             }
974         }
975
976         fn cp_after(&self) -> Option<char> {
977             self.slice_after().chars().next()
978         }
979
980         fn try_seek_right_cp(&mut self) -> bool {
981             match self.slice_after().chars().next() {
982                 Some(c) => {
983                     self.at += c.len_utf8();
984                     true
985                 },
986                 None => false,
987             }
988         }
989
990         fn seek_right(&mut self, bytes: usize) {
991             self.at += bytes;
992         }
993     }
994
995     impl<'a> Copy for StrCursor<'a> {}
996
997     impl<'a> Clone for StrCursor<'a> {
998         fn clone(&self) -> StrCursor<'a> {
999             *self
1000         }
1001     }
1002
1003     impl<'a> std::fmt::Debug for StrCursor<'a> {
1004         fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
1005             write!(fmt, "StrCursor({:?} | {:?})", self.slice_before(), self.slice_after())
1006         }
1007     }
1008
1009     fn str_eq_literal(a: &str, b: &str) -> bool {
1010         a.as_bytes().as_ptr() == b.as_bytes().as_ptr()
1011             && a.len() == b.len()
1012     }
1013 }