blob: 8f2b819cb3597c6e974ab61a460487dddf2028e3 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
extern crate proc_macro;
#[macro_use]
extern crate quote;
use proc_macro2::TokenStream;
use std::fmt::Formatter;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
use std::num::ParseIntError;
use std::str::FromStr;
/// Declares a proc_macro with `name` using `generator` to generate any of `ty`.
macro_rules! declare_macro {
($name:ident, $generator:ident, $($ty:ident),+) => {
#[proc_macro]
pub fn $name(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
Emitter::<$generator, $($ty),+>::emit(input).into()
}
}
}
/// Empty slot in an [`Emitter`].
struct Skip;
/// The mock error returned by [`Skip`].
#[derive(Debug)]
struct SkipError;
impl std::fmt::Display for SkipError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
std::fmt::Debug::fmt(self, f)
}
}
impl std::error::Error for SkipError {}
impl FromStr for Skip {
type Err = SkipError;
fn from_str(_s: &str) -> Result<Self, Self::Err> {
Err(SkipError {})
}
}
/// Generates a [`TokenStream`] representation of `T`.
trait Generator<T> {
/// Generates the [`TokenStream`] for `input`.
fn generate(input: T) -> TokenStream;
/// Get an optional string representation of type `T`.
///
/// Used in error reporting when parsing fails.
fn type_str() -> Option<&'static str> {
Some(std::any::type_name::<T>())
}
}
impl<O> Generator<Skip> for O {
fn generate(_input: Skip) -> TokenStream {
unreachable!()
}
fn type_str() -> Option<&'static str> {
None
}
}
/// Attempts to emit the resulting [`TokenStream`] for `input` parsed as `T`.
fn try_emit<G, T>(input: &str) -> Result<TokenStream, T::Err>
where
G: Generator<T>,
T: FromStr,
{
Ok(G::generate(T::from_str(input)?))
}
/// Provides common structor for parsing types from [`TokenStream`]s and
/// emitting the resulting [`TokenStream`].
///
/// `Emitter` can parse any of `Tn` and pass into a [`Generator`] `G`, encoding
/// the common logic for declarations build from string parsing at compile-time.
struct Emitter<G, T1 = Skip, T2 = Skip, T3 = Skip, T4 = Skip> {
_g: std::marker::PhantomData<G>,
_t1: std::marker::PhantomData<T1>,
_t2: std::marker::PhantomData<T2>,
_t3: std::marker::PhantomData<T3>,
_t4: std::marker::PhantomData<T4>,
}
impl<G, T1, T2, T3, T4> Emitter<G, T1, T2, T3, T4>
where
G: Generator<T1> + Generator<T2> + Generator<T3> + Generator<T4>,
T1: FromStr,
T2: FromStr,
T3: FromStr,
T4: FromStr,
T1::Err: std::error::Error,
T2::Err: std::error::Error,
T3::Err: std::error::Error,
T4::Err: std::error::Error,
{
/// Emits the resulting [`TokenStream`] (or an error one) after attempting
/// to parse `input` into all of the `Emitter`'s types sequentially.
fn emit(input: proc_macro::TokenStream) -> TokenStream {
// Always require a string literal.
let input = proc_macro2::TokenStream::from(input);
let s = match syn::parse2::<syn::LitStr>(input.clone()) {
Ok(s) => s.value(),
Err(e) => return e.to_compile_error().into(),
};
match try_emit::<G, T1>(&s)
.or_else(|e1| try_emit::<G, T2>(&s).map_err(|e2| (e1, e2)))
.or_else(|(e1, e2)| try_emit::<G, T3>(&s).map_err(|e3| (e1, e2, e3)))
.or_else(|(e1, e2, e3)| try_emit::<G, T4>(&s).map_err(|e4| (e1, e2, e3, e4)))
{
Ok(ts) => ts,
Err((e1, e2, e3, e4)) => syn::Error::new_spanned(
input,
format!("failed to parse as {}", Self::error_str(&e1, &e2, &e3, &e4)),
)
.to_compile_error()
.into(),
}
}
/// Get the error string reported to the compiler when parsing fails with
/// this `Emitter`.
fn error_str(
e1: &dyn std::error::Error,
e2: &dyn std::error::Error,
e3: &dyn std::error::Error,
e4: &dyn std::error::Error,
) -> String {
[
(<G as Generator<T1>>::type_str(), e1),
(<G as Generator<T2>>::type_str(), e2),
(<G as Generator<T3>>::type_str(), e3),
(<G as Generator<T4>>::type_str(), e4),
]
.iter()
.filter_map(|(ts, e)| ts.map(|t| format!("{}: \"{}\"", t, e)))
.collect::<Vec<_>>()
.join(", or ")
}
}
/// Generator for `std` types.
enum StdGen {}
impl Generator<IpAddr> for StdGen {
fn generate(input: IpAddr) -> TokenStream {
let (t, inner) = match input {
IpAddr::V4(v4) => (quote! { V4 }, Self::generate(v4)),
IpAddr::V6(v6) => (quote! { V6 }, Self::generate(v6)),
};
quote! {
std::net::IpAddr::#t(#inner)
}
}
}
impl Generator<Ipv4Addr> for StdGen {
fn generate(input: Ipv4Addr) -> TokenStream {
let octets = input.octets();
quote! {
std::net::Ipv4Addr::new(#(#octets),*)
}
}
}
impl Generator<Ipv6Addr> for StdGen {
fn generate(input: Ipv6Addr) -> TokenStream {
let segments = input.segments();
quote! {
std::net::Ipv6Addr::new(#(#segments),*)
}
}
}
impl Generator<SocketAddr> for StdGen {
fn generate(input: SocketAddr) -> TokenStream {
let (t, inner) = match input {
SocketAddr::V4(v4) => (quote! { V4 }, Self::generate(v4)),
SocketAddr::V6(v6) => (quote! { V6 }, Self::generate(v6)),
};
quote! {
std::net::SocketAddr::#t(#inner)
}
}
}
impl Generator<SocketAddrV4> for StdGen {
fn generate(input: SocketAddrV4) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
quote! {
std::net::SocketAddrV4::new(#addr, #port)
}
}
}
impl Generator<SocketAddrV6> for StdGen {
fn generate(input: SocketAddrV6) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
let flowinfo = input.flowinfo();
let scope_id = input.scope_id();
quote! {
std::net::SocketAddrV6::new(#addr, #port, #flowinfo, #scope_id)
}
}
}
declare_macro!(std_ip, StdGen, IpAddr);
declare_macro!(std_ip_v4, StdGen, Ipv4Addr);
declare_macro!(std_ip_v6, StdGen, Ipv6Addr);
declare_macro!(std_socket_addr, StdGen, SocketAddr, SocketAddrV4);
declare_macro!(std_socket_addr_v4, StdGen, SocketAddrV4);
declare_macro!(std_socket_addr_v6, StdGen, SocketAddrV6);
/// Generator for FIDL types.
enum FidlGen {}
impl Generator<IpAddr> for FidlGen {
fn generate(input: IpAddr) -> TokenStream {
let (t, inner) = match input {
IpAddr::V4(v4) => (quote! { Ipv4 }, Self::generate(v4)),
IpAddr::V6(v6) => (quote! { Ipv6 }, Self::generate(v6)),
};
quote! {
fidl_fuchsia_net::IpAddress::#t(#inner)
}
}
}
impl Generator<Ipv4Addr> for FidlGen {
fn generate(input: Ipv4Addr) -> TokenStream {
let octets = input.octets();
quote! {
fidl_fuchsia_net::Ipv4Address{ addr: [#(#octets),*]}
}
}
}
impl Generator<Ipv6Addr> for FidlGen {
fn generate(input: Ipv6Addr) -> TokenStream {
let octets = input.octets();
quote! {
fidl_fuchsia_net::Ipv6Address{ addr: [#(#octets),*]}
}
}
}
impl Generator<SocketAddr> for FidlGen {
fn generate(input: SocketAddr) -> TokenStream {
let (t, inner) = match input {
SocketAddr::V4(v4) => (quote! { Ipv4 }, Self::generate(v4)),
SocketAddr::V6(v6) => (quote! { Ipv6 }, Self::generate(v6)),
};
quote! {
fidl_fuchsia_net::SocketAddress::#t(#inner)
}
}
}
impl Generator<SocketAddrV4> for FidlGen {
fn generate(input: SocketAddrV4) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
quote! {
fidl_fuchsia_net::Ipv4SocketAddress {
address: #addr,
port: #port
}
}
}
}
impl Generator<SocketAddrV6> for FidlGen {
fn generate(input: SocketAddrV6) -> TokenStream {
let addr = Self::generate(input.ip().clone());
let port = input.port();
let scope_id = u64::from(input.scope_id());
quote! {
fidl_fuchsia_net::Ipv6SocketAddress {
address: #addr,
port: #port,
zone_index: #scope_id
}
}
}
}
/// Helper struct to parse Mac addresses from string.
#[derive(Default)]
struct MacAddress([u8; 6]);
#[derive(thiserror::Error, Debug)]
enum MacParseError {
#[error("invalid length for MacAddress, should be 6")]
InvalidLength,
#[error("invalid byte length (\"{0}\") in MacAddress, should be 2")]
InvalidByte(String),
#[error("failed to parse byte \"{0}\": {1}")]
IntError(String, ParseIntError),
}
impl FromStr for MacAddress {
type Err = MacParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut mac = Self::default();
let Self(octets) = &mut mac;
let mut parse_iter = s.split(':');
let mut save_iter = octets.iter_mut();
loop {
match (parse_iter.next(), save_iter.next()) {
(Some(s), Some(b)) => {
if s.len() != 2 {
return Err(MacParseError::InvalidByte(s.to_string()));
}
*b = u8::from_str_radix(s, 16)
.map_err(|e| MacParseError::IntError(s.to_string(), e))?;
}
(None, Some(_)) | (Some(_), None) => break Err(MacParseError::InvalidLength),
(None, None) => break Ok(mac),
}
}
}
}
impl Generator<MacAddress> for FidlGen {
fn generate(input: MacAddress) -> TokenStream {
let MacAddress(octets) = input;
quote! {
fidl_fuchsia_net::MacAddress {
octets: [#(#octets),*]
}
}
}
}
/// Helper struct to parse Cidr addresses from string.
struct CidrAddress {
address: std::net::IpAddr,
prefix: u8,
}
#[derive(thiserror::Error, Debug)]
enum CidrParseError {
#[error("missing address")]
MissingIp,
#[error("missing prefix length")]
MissingPrefix,
#[error("unexpected trailing data \"{0}\"")]
TrailingInformation(String),
#[error("failed to parse IP \"{0}\": {1}")]
IpParseError(String, std::net::AddrParseError),
#[error("failed to parse prefix \"{0}\": {1}")]
PrefixParseError(String, std::num::ParseIntError),
#[error("bad prefix value {0} for {1}")]
BadPrefix(u8, std::net::IpAddr),
}
impl FromStr for CidrAddress {
type Err = CidrParseError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut parse_iter = s.split('/');
let ip_str = parse_iter.next().ok_or(CidrParseError::MissingIp)?;
let prefix_str = parse_iter.next().ok_or(CidrParseError::MissingPrefix)?;
if let Some(trailing) = parse_iter.next() {
return Err(CidrParseError::TrailingInformation(trailing.to_string()));
}
let address = std::net::IpAddr::from_str(ip_str)
.map_err(|e| CidrParseError::IpParseError(ip_str.to_string(), e))?;
let prefix = u8::from_str_radix(prefix_str, 10)
.map_err(|e| CidrParseError::PrefixParseError(prefix_str.to_string(), e))?;
let addr_len = 8 * match address {
std::net::IpAddr::V4(a) => a.octets().len(),
std::net::IpAddr::V6(a) => a.octets().len(),
};
if usize::from(prefix) > addr_len {
return Err(CidrParseError::BadPrefix(prefix, address));
}
Ok(CidrAddress { address, prefix })
}
}
impl Generator<CidrAddress> for FidlGen {
fn generate(input: CidrAddress) -> TokenStream {
let CidrAddress { address, prefix } = input;
let address = Self::generate(address);
quote! {
fidl_fuchsia_net::Subnet {
addr: #address,
prefix_len: #prefix
}
}
}
}
declare_macro!(fidl_ip, FidlGen, IpAddr);
declare_macro!(fidl_ip_v4, FidlGen, Ipv4Addr);
declare_macro!(fidl_ip_v6, FidlGen, Ipv6Addr);
declare_macro!(fidl_socket_addr, FidlGen, SocketAddr);
declare_macro!(fidl_socket_addr_v4, FidlGen, SocketAddrV4);
declare_macro!(fidl_socket_addr_v6, FidlGen, SocketAddrV6);
declare_macro!(fidl_mac, FidlGen, MacAddress);
declare_macro!(fidl_subnet, FidlGen, CidrAddress);