1extern crate proc_macro;
2
3use std::mem;
4
5use proc_macro2::{Group, Ident, Punct, Spacing, TokenStream, TokenTree};
6
7#[proc_macro]
9pub fn caesar(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
10 let input = TokenStream::from(input);
11
12 let output: TokenStream = decode_stream(input);
13
14 proc_macro::TokenStream::from(output)
15}
16
17fn decode_stream(ts: TokenStream) -> TokenStream {
18 ts.into_iter().map(decode_tree).collect()
19}
20
21fn decode_tree(tt: TokenTree) -> TokenTree {
22 match tt {
23 TokenTree::Group(g) => {
24 let mut dg = Group::new(g.delimiter(), decode_stream(g.stream()));
25 dg.set_span(g.span());
26 TokenTree::Group(dg)
27 }
28 TokenTree::Ident(i) => {
29 let gi = Ident::new(&caesar_decode(&i.to_string()), i.span());
30 TokenTree::Ident(gi)
31 }
32 TokenTree::Punct(..) | TokenTree::Literal(..) => tt,
33 }
34}
35
36fn caesar_decode(s: &str) -> String {
37 return s.chars().map(decode_char).collect();
38
39 fn decode_char(c: char) -> char {
40 match c {
41 'a'..='z' => rot(c, 'a'),
42 'A'..='Z' => rot(c, 'A'),
43 _ => c,
44 }
45 }
46
47 fn rot(c: char, base: char) -> char {
48 std::char::from_u32(((c as u32 - base as u32) + 13) % 26 + base as u32).unwrap()
49 }
50}
51
52#[test]
53fn decoding_works() {
54 assert_eq!(
55 &caesar_decode("Ornhgvshy vf orggre guna htyl."),
56 "Beautiful is better than ugly."
57 )
58}
59
60#[proc_macro]
61pub fn mirror(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
62 let input = TokenStream::from(input);
63 let output: TokenStream = mirror_stream(input);
64 proc_macro::TokenStream::from(output)
65}
66
67fn mirror_stream(ts: TokenStream) -> TokenStream {
68 let mut spacing = Spacing::Alone;
69 ts.into_iter()
70 .map(|tt| match tt {
71 TokenTree::Group(g) => {
72 let mut mg = Group::new(g.delimiter(), mirror_stream(g.stream()));
73 mg.set_span(g.span());
74 TokenTree::Group(mg)
75 }
76 TokenTree::Punct(p) => {
77 let c = mirror_char(p.as_char());
78 let spacing = mem::replace(&mut spacing, p.spacing());
79 let mut mp = Punct::new(c, spacing);
80 mp.set_span(p.span());
81 TokenTree::Punct(mp)
82 }
83 TokenTree::Ident(..) | TokenTree::Literal(..) => tt,
84 })
85 .collect::<Vec<_>>()
86 .into_iter()
87 .rev()
88 .collect()
89}
90
91fn mirror_char(c: char) -> char {
92 match c {
93 '<' => '>',
94 '>' => '<',
95 _ => c,
96 }
97}