extern crate proc_macro;
use proc_macro::{Span, TokenStream};
use syn::{parse_macro_input, Error, LitInt};
#[proc_macro]
pub fn encode_oid(input: TokenStream) -> TokenStream {
let token_stream = input.to_string();
let (s, relative) = if token_stream.starts_with("rel ") {
(&token_stream[4..], true)
} else {
(token_stream.as_ref(), false)
};
let items: Result<Vec<_>, _> = s.split('.').map(|x| x.trim().parse::<u128>()).collect();
let mut items: &[_] = match items.as_ref() {
Ok(v) => v.as_ref(),
Err(_) => return create_error("Could not parse OID"),
};
let mut v = Vec::new();
if !relative {
if items.len() < 2 {
if items.len() == 1 && items[0] == 0 {
return "[0]".parse().unwrap();
}
return create_error("Need at least two components for non-relative oid");
}
if items[0] > 2 || items[1] > 39 {
return create_error("First components are too big");
}
let first_byte = (items[0] * 40 + items[1]) as u8;
v.push(first_byte);
items = &items[2..];
}
for &int in items {
let enc = encode_base128(int);
v.extend_from_slice(&enc);
}
let mut s = String::with_capacity(2 + 6 * v.len());
s.push('[');
for byte in v.iter() {
s.insert_str(s.len(), &format!("0x{:02x}, ", byte));
}
s.push(']');
s.parse().unwrap()
}
#[inline]
fn create_error(msg: &str) -> TokenStream {
let s = format!("Invalid OID({})", msg);
Error::new(Span::call_site().into(), &s)
.to_compile_error()
.into()
}
fn encode_base128(int: u128) -> Vec<u8> {
let mut val = int;
let mut base128 = Vec::new();
let lo = val & 0x7f;
base128.push(lo as u8);
val >>= 7;
loop {
if val == 0 {
base128.reverse();
return base128;
}
let lo = val & 0x7f;
base128.push(lo as u8 | 0x80);
val >>= 7;
}
}
#[proc_macro]
pub fn encode_int(input: TokenStream) -> TokenStream {
let lit = parse_macro_input!(input as LitInt);
match impl_encode_int(lit) {
Ok(ts) => ts,
Err(e) => e.to_compile_error().into(),
}
}
fn impl_encode_int(lit: LitInt) -> Result<TokenStream, Error> {
let value = lit.base10_parse::<u64>()?;
let bytes = value.to_be_bytes();
let v: Vec<_> = bytes.iter().skip_while(|&c| *c == 0).collect();
let mut s = String::with_capacity(2 + 6 * v.len());
s.push('[');
for byte in v.iter() {
s.insert_str(s.len(), &format!("0x{:02x}, ", byte));
}
s.push(']');
Ok(s.parse().unwrap())
}