dhcproto-macros-0.1.0/.cargo_vcs_info.json0000644000000001550000000000100141370ustar { "git": { "sha1": "e5e3c4e009d0c2252a2e9da4f1f83d85813295a3" }, "path_in_vcs": "dhcproto-macros" }dhcproto-macros-0.1.0/Cargo.toml0000644000000015600000000000100121360ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "dhcproto-macros" version = "0.1.0" description = """ A macro library for generating option types for dhcproto """ keywords = [ "dhcp", "dhcpv4", "dhcpv6", ] categories = [ "network-programming", "encoding", "parser-implementations", ] license = "MIT" repository = "https://github.com/bluecatengineering/dhcproto" [lib] proc-macro = true [dependencies] dhcproto-macros-0.1.0/Cargo.toml.orig000064400000000000000000000007351046102023000156220ustar 00000000000000[package] edition = "2021" name = "dhcproto-macros" version = "0.1.0" description = """ A macro library for generating option types for dhcproto """ categories = ["network-programming", "encoding","parser-implementations"] repository = "https://github.com/bluecatengineering/dhcproto" keywords = ["dhcp","dhcpv4","dhcpv6"] license = "MIT" [lib] proc-macro = true # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] dhcproto-macros-0.1.0/src/lib.rs000064400000000000000000000125521046102023000146360ustar 00000000000000use proc_macro::{Group, Ident, TokenStream, TokenTree}; struct Entry { code: u8, id: Ident, description: String, data_type: Option, } fn parse_input(input: TokenStream) -> Vec { let mut entries = Vec::new(); for x in input.into_iter() { if let TokenTree::Group(group) = x { let mut tokens = group.stream().into_iter().filter_map(|x| { if let TokenTree::Punct(_) = x { None } else { Some(x) } }); let code = if let Some(TokenTree::Literal(lit)) = tokens.next() { lit.to_string().parse::().unwrap() } else { panic!("expected code"); }; let id = if let Some(TokenTree::Ident(id)) = tokens.next() { id } else { panic!("expected id"); }; let description = if let Some(TokenTree::Literal(description)) = tokens.next() { description.to_string() } else { panic!("expected description"); }; let data_type = match tokens.next() { Some(TokenTree::Group(x)) => Some(x), None => None, e => panic!("expected nothing or id not {e:?}"), }; entries.push(Entry { code, id, description, data_type, }) } } entries } fn generate_optioncode_code<'a>(entries: &'a [Entry]) -> impl Iterator + 'a { let enum_impl = std::iter::once( " /// DHCP Options #[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub enum OptionCode {" .to_owned(), ) .chain(entries.iter().map(|e| { let description = &e.description[1..&e.description.len() - 1]; let id = &e.id; let code = e.code; format!("/// {code} - {description}\n{id},") })) .chain(std::iter::once( " /// Unknown code Unknown(u8), } " .to_owned(), )); let impl_option_from_u8 = std::iter::once( " impl std::convert::From for OptionCode { fn from(x : u8) -> Self{ match x { " .to_owned(), ) .chain(entries.iter().map(|e| { let id = &e.id; let code = e.code; format!("{code} => Self::{id},") })) .chain(std::iter::once("_ => Self::Unknown(x)}}}".to_owned())); let impl_u8_from_option = std::iter::once( " impl std::convert::From for u8 { fn from(x : OptionCode) -> Self{ match x { " .to_owned(), ) .chain(entries.iter().map(|e| { let id = &e.id; let code = e.code; format!("OptionCode::{id} => {code},") })) .chain(std::iter::once( "OptionCode::Unknown(code) => code }}}".to_owned(), )); enum_impl .chain(impl_option_from_u8) .chain(impl_u8_from_option) } fn generate_dhcpoption_code<'a>(entries: &'a [Entry]) -> impl Iterator + 'a { let impl_dhcp_option = std::iter::once( " /// DHCP Options #[cfg_attr(feature = \"serde\", derive(Serialize, Deserialize))] #[derive(Debug, Clone, PartialEq, Eq)] pub enum DhcpOption {" .to_owned(), ) .chain(entries.iter().map(|e| { let description = &e.description[1..&e.description.len() - 1]; let id = &e.id; let code = e.code; if let Some(data_description) = &e.data_type { format!("/// {code} - {description}\n{id}{data_description},") } else { format!("/// {code} - {description}\n{id},") } })) .chain(std::iter::once( " /// Unknown option Unknown(UnknownOption), } " .to_owned(), )); let impl_optioncode_from_dhcpoption_ref = std::iter::once( " impl From<&DhcpOption> for OptionCode { fn from(opt: &DhcpOption) -> Self { use DhcpOption::*; match opt { " .to_owned(), ) .chain(entries.iter().map(|e| { let id = &e.id; let var_field = if let Some(data_description) = &e.data_type { std::iter::once("(_") .chain(data_description.stream().into_iter().filter_map(|e| { if let TokenTree::Punct(p) = e { (p.as_char() == ',').then_some(",_") } else { None } })) .chain(std::iter::once(")")) .collect() } else { "".to_owned() }; format!("{id}{var_field} => OptionCode::{id},") })) .chain(std::iter::once( "Unknown(n) => OptionCode::Unknown(n.code)}}}".to_owned(), )); impl_dhcp_option.chain(impl_optioncode_from_dhcpoption_ref) } #[proc_macro] pub fn declare_codes(input: TokenStream) -> TokenStream { let entries = parse_input(input); let enum_code = generate_optioncode_code(&entries); let dhcpoption_code = generate_dhcpoption_code(&entries); enum_code .chain(dhcpoption_code) .collect::() .parse() .unwrap() }