proc_caesar/
lib.rs

1extern crate proc_macro;
2
3use std::mem;
4
5use proc_macro2::{Group, Ident, Punct, Spacing, TokenStream, TokenTree};
6
7/// Applies Caesar chipher to the input source code.
8#[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}