data_encoding_macro_internal/
lib.rs

1//! Internal library for data-encoding-macro
2//!
3//! Do **not** use this library. Use [data-encoding-macro] instead.
4//!
5//! This library is for internal use by data-encoding-macro because procedural
6//! macros require a separate crate.
7//!
8//! [data-encoding-macro]: https://crates.io/crates/data-encoding-macro
9
10#![warn(unused_results)]
11
12use std::collections::HashMap;
13
14use data_encoding::{BitOrder, Encoding, Specification, Translate, Wrap};
15use proc_macro::token_stream::IntoIter;
16use proc_macro::{TokenStream, TokenTree};
17
18fn parse_op(tokens: &mut IntoIter, op: char, key: &str) {
19    match tokens.next() {
20        Some(TokenTree::Punct(ref x)) if x.as_char() == op => (),
21        _ => panic!("expected {:?} after {}", op, key),
22    }
23}
24
25fn parse_map(mut tokens: IntoIter) -> HashMap<String, TokenTree> {
26    let mut map = HashMap::new();
27    while let Some(key) = tokens.next() {
28        let key = match key {
29            TokenTree::Ident(ident) => format!("{}", ident),
30            _ => panic!("expected key got {}", key),
31        };
32        parse_op(&mut tokens, ':', &key);
33        let value = match tokens.next() {
34            None => panic!("expected value for {}", key),
35            Some(value) => value,
36        };
37        parse_op(&mut tokens, ',', &key);
38        let _ = map.insert(key, value);
39    }
40    map
41}
42
43fn get_string(map: &mut HashMap<String, TokenTree>, key: &str) -> String {
44    let node = match map.remove(key) {
45        None => return String::new(),
46        Some(node) => node,
47    };
48    match syn::parse::<syn::LitStr>(node.into()) {
49        Ok(result) => result.value(),
50        _ => panic!("expected string for {}", key),
51    }
52}
53
54fn get_usize(map: &mut HashMap<String, TokenTree>, key: &str) -> usize {
55    let node = match map.remove(key) {
56        None => return 0,
57        Some(node) => node,
58    };
59    let literal = match node {
60        TokenTree::Literal(literal) => literal,
61        _ => panic!("expected literal for {}", key),
62    };
63    match literal.to_string().parse() {
64        Ok(result) => result,
65        Err(error) => panic!("expected usize for {}: {}", key, error),
66    }
67}
68
69fn get_padding(map: &mut HashMap<String, TokenTree>) -> Option<char> {
70    let node = match map.remove("padding") {
71        None => return None,
72        Some(node) => node,
73    };
74    if let Ok(result) = syn::parse::<syn::LitChar>(node.clone().into()) {
75        return Some(result.value());
76    }
77    match syn::parse::<syn::Ident>(node.into()) {
78        Ok(ref result) if result == "None" => None,
79        _ => panic!("expected None or char for padding"),
80    }
81}
82
83fn get_bool(map: &mut HashMap<String, TokenTree>, key: &str) -> Option<bool> {
84    let node = match map.remove(key) {
85        None => return None,
86        Some(node) => node,
87    };
88    match syn::parse::<syn::LitBool>(node.into()) {
89        Ok(result) => Some(result.value),
90        _ => panic!("expected bool for {}", key),
91    }
92}
93
94fn get_bit_order(map: &mut HashMap<String, TokenTree>) -> BitOrder {
95    let node = match map.remove("bit_order") {
96        None => return BitOrder::MostSignificantFirst,
97        Some(node) => node,
98    };
99    let msb = "MostSignificantFirst";
100    let lsb = "LeastSignificantFirst";
101    match node {
102        TokenTree::Ident(ref ident) if format!("{}", ident) == msb => {
103            BitOrder::MostSignificantFirst
104        }
105        TokenTree::Ident(ref ident) if format!("{}", ident) == lsb => {
106            BitOrder::LeastSignificantFirst
107        }
108        _ => panic!("expected {} or {} for bit_order", msb, lsb),
109    }
110}
111
112fn check_present<T>(hash_map: &HashMap<String, T>, key: &str) {
113    assert!(hash_map.contains_key(key), "{} is required", key);
114}
115
116fn get_encoding(hash_map: &mut HashMap<String, TokenTree>) -> Encoding {
117    check_present(hash_map, "symbols");
118    let spec = Specification {
119        symbols: get_string(hash_map, "symbols"),
120        bit_order: get_bit_order(hash_map),
121        check_trailing_bits: get_bool(hash_map, "check_trailing_bits").unwrap_or(true),
122        padding: get_padding(hash_map),
123        ignore: get_string(hash_map, "ignore"),
124        wrap: Wrap {
125            width: get_usize(hash_map, "wrap_width"),
126            separator: get_string(hash_map, "wrap_separator"),
127        },
128        translate: Translate {
129            from: get_string(hash_map, "translate_from"),
130            to: get_string(hash_map, "translate_to"),
131        },
132    };
133    spec.encoding().unwrap()
134}
135
136fn check_empty<T>(hash_map: HashMap<String, T>) {
137    assert!(hash_map.is_empty(), "Unexpected keys {:?}", hash_map.keys());
138}
139
140#[proc_macro]
141#[doc(hidden)]
142pub fn internal_new_encoding(input: TokenStream) -> TokenStream {
143    let mut hash_map = parse_map(input.into_iter());
144    let encoding = get_encoding(&mut hash_map);
145    check_empty(hash_map);
146    format!("{:?}", encoding.internal_implementation()).parse().unwrap()
147}
148
149#[proc_macro]
150#[doc(hidden)]
151pub fn internal_decode_array(input: TokenStream) -> TokenStream {
152    let mut hash_map = parse_map(input.into_iter());
153    let encoding = get_encoding(&mut hash_map);
154    check_present(&hash_map, "name");
155    let name = get_string(&mut hash_map, "name");
156    check_present(&hash_map, "input");
157    let input = get_string(&mut hash_map, "input");
158    check_empty(hash_map);
159    let output = encoding.decode(input.as_bytes()).unwrap();
160    format!("{}: [u8; {}] = {:?};", name, output.len(), output).parse().unwrap()
161}
162
163#[proc_macro]
164#[doc(hidden)]
165pub fn internal_decode_slice(input: TokenStream) -> TokenStream {
166    let mut hash_map = parse_map(input.into_iter());
167    let encoding = get_encoding(&mut hash_map);
168    check_present(&hash_map, "input");
169    let input = get_string(&mut hash_map, "input");
170    check_empty(hash_map);
171    format!("{:?}", encoding.decode(input.as_bytes()).unwrap()).parse().unwrap()
172}