1#![doc = include_str!("../README.md")]
2
3use proc_macro::{
4 Delimiter, Group, Ident, Literal, Punct, Spacing::*, Span, TokenStream,
5 TokenTree,
6};
7
8#[must_use]
9fn stream<I>(iter: I) -> TokenStream
10where I: IntoIterator,
11 TokenStream: FromIterator<I::Item>,
12{
13 TokenStream::from_iter(iter)
14}
15
16fn err<T>(msg: &str, span: Span) -> Result<T, TokenStream> {
17 let s = |mut t: TokenTree| {
18 t.set_span(span);
19 t
20 };
21 Err(stream([
22 s(Punct::new(':', Joint).into()),
23 s(Punct::new(':', Joint).into()),
24 s(Ident::new("core", span).into()),
25 s(Punct::new(':', Joint).into()),
26 s(Punct::new(':', Joint).into()),
27 s(Ident::new("compile_error", span).into()),
28 s(Punct::new('!', Joint).into()),
29 s(Group::new(Delimiter::Brace, stream([
30 s(Literal::string(msg).into()),
31 ])).into()),
32 ]))
33}
34
35fn split_suffix(s: &str, span: Span) -> Result<(&str, &str), TokenStream> {
36 if s.starts_with('"') {
37 let i = s.rfind('"').unwrap();
38 return Ok(s.split_at(i+1));
39 }
40 if !s.starts_with('r') {
41 return err("invalid string literal", span);
42 }
43 let i = s.rfind(['"', '#']).unwrap();
44 Ok(s.split_at(i+1))
45}
46
47#[proc_macro_attribute]
66pub fn parse_string_suffix(attr: TokenStream, item: TokenStream) -> TokenStream {
67 if let Some(attr) = attr.into_iter().next() {
68 return err::<()>("invalid input", attr.span()).unwrap_err();
69 }
70 item.into_iter()
71 .map(do_token)
72 .collect()
73}
74
75fn gen_parse(str: &str, suf: &str, span: Span) -> TokenTree {
76 let s = |mut tt1: TokenTree| {
77 tt1.set_span(span);
78 tt1
79 };
80 s(Group::new(Delimiter::None, stream([
81 s(str.parse::<Literal>().unwrap().into()),
82 s(Punct::new('.', Joint).into()),
83 Ident::new("parse", span).into(),
84 s(Punct::new(':', Joint).into()),
85 s(Punct::new(':', Joint).into()),
86 s(Punct::new('<', Joint).into()),
87 Ident::new(suf, span).into(),
88 s(Punct::new('>', Joint).into()),
89 s(Group::new(Delimiter::Parenthesis, TokenStream::new()).into()),
90 s(Punct::new('.', Joint).into()),
91 Ident::new("unwrap", span).into(),
92 s(Group::new(Delimiter::Parenthesis, TokenStream::new()).into()),
93 ])).into())
94}
95
96fn do_token(tt: TokenTree) -> TokenTree {
97 let s = |mut tt1: TokenTree| {
98 tt1.set_span(tt.span());
99 tt1
100 };
101 match tt {
102 TokenTree::Group(ref group) => {
103 let t = parse_string_suffix(TokenStream::new(), group.stream());
104 s(Group::new(group.delimiter(), t).into())
105 },
106 TokenTree::Ident(_) => tt,
107 TokenTree::Punct(_) => tt,
108 TokenTree::Literal(ref lit) => {
109 match split_suffix(&lit.to_string(), lit.span()) {
110 Ok((str, suf)) if !suf.is_empty() => {
111 gen_parse(str, suf, tt.span())
112 },
113 _ => tt,
114 }
115 },
116 }
117}