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, _> = s.split('.').map(|x| x.trim().parse::()).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); } // "fn answer() -> u32 { 42 }".parse().unwrap() 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() } // encode int as base128 fn encode_base128(int: u128) -> Vec { 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(), } // let token_stream = input.to_string(); // let items: Result, _> = token_stream // .split('.') // .map(|x| x.trim().parse::()) // .collect(); // let err = Error::new(Span::call_site().into(), "invalid OID"); // if let Ok(items) = items { // let mut v = Vec::new(); // if items.len() < 2 || items[0] > 2 || items[1] > 39 { // return err.to_compile_error().into(); // } // let first_byte = (items[0] * 40 + items[1]) as u8; // v.push(first_byte); // for &int in &items[2..] { // let enc = encode_base128(int); // v.extend_from_slice(&enc); // } // // "fn answer() -> u32 { 42 }".parse().unwrap() // 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() // } else { // eprintln!("could not parse OID '{}'", token_stream); // err.to_compile_error().into() // } } fn impl_encode_int(lit: LitInt) -> Result { let value = lit.base10_parse::()?; 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()) }