use core::{fmt, net::*, str::FromStr};
use proc_macro::{
Delimiter,
Spacing::{Alone, Joint},
Span, TokenStream, TokenTree,
};
#[proc_macro]
pub fn netlit(item: TokenStream) -> TokenStream {
match _netlit(item) {
Some(it) => it,
None => TokenStream::from_iter([
colon2(),
ident("core"),
colon2(),
ident("compile_error"),
bang(),
parenthesized(lit(
"expected `a.b.c.d` or `a.b.c.d:p` or `a:..:h` or `[a:..:h]:p`",
)),
]),
}
}
fn tight(mut buf: &mut dyn fmt::Write, ts: TokenStream) -> fmt::Result {
for tt in ts {
match tt {
TokenTree::Group(group) => {
let (open, close) = match group.delimiter() {
Delimiter::Parenthesis => ("(", ")"),
Delimiter::Brace => ("{", "}"),
Delimiter::Bracket => ("[", "]"),
Delimiter::None => ("", ""),
};
write!(buf, "{open}")?;
tight(&mut buf, group.stream())?;
write!(buf, "{close}")?
}
TokenTree::Ident(ident) => write!(buf, "{ident}")?,
TokenTree::Punct(punct) => write!(buf, "{}", punct.as_char())?,
TokenTree::Literal(literal) => write!(buf, "{literal}")?,
}
}
Ok(())
}
fn _netlit(input: TokenStream) -> Option<TokenStream> {
let mut buf = String::new();
tight(&mut buf, input).expect("String::write_fmt is infallible");
None.or(tri(&buf, ipv4addr))
.or(tri(&buf, ipv6addr))
.or(tri(&buf, socketaddrv4))
.or(tri(&buf, socketaddrv6))
}
fn tri<T: FromStr>(s: &str, f: fn(T) -> TokenStream) -> Option<TokenStream> {
Some(f(s.parse().ok()?))
}
fn ipv4addr(addr: Ipv4Addr) -> TokenStream {
let mut args = TokenStream::new();
for oct in addr.octets() {
args.extend([lit(oct), comma()]);
}
TokenStream::from_iter([
colon2(),
ident("core"),
colon2(),
ident("net"),
colon2(),
ident("Ipv4Addr"),
colon2(),
ident("new"),
parenthesized(args),
])
}
fn ipv6addr(addr: Ipv6Addr) -> TokenStream {
let mut args = TokenStream::new();
for seg in addr.segments() {
args.extend([lit(seg), comma()]);
}
TokenStream::from_iter([
colon2(),
ident("core"),
colon2(),
ident("net"),
colon2(),
ident("Ipv6Addr"),
colon2(),
ident("new"),
parenthesized(args),
])
}
fn socketaddrv4(addr: SocketAddrV4) -> TokenStream {
TokenStream::from_iter([
colon2(),
ident("core"),
colon2(),
ident("net"),
colon2(),
ident("SocketAddrV4"),
colon2(),
ident("new"),
parenthesized(TokenStream::from_iter([
ipv4addr(*addr.ip()),
comma(),
lit(addr.port()),
])),
])
}
fn socketaddrv6(addr: SocketAddrV6) -> TokenStream {
TokenStream::from_iter([
colon2(),
ident("core"),
colon2(),
ident("net"),
colon2(),
ident("SocketAddrV6"),
colon2(),
ident("new"),
parenthesized(TokenStream::from_iter([
ipv6addr(*addr.ip()),
comma(),
lit(addr.port()),
comma(),
lit(addr.flowinfo()),
comma(),
lit(addr.scope_id()),
])),
])
}
fn colon2() -> TokenStream {
use proc_macro::Punct;
TokenStream::from_iter([
TokenTree::from(Punct::new(':', Joint)),
TokenTree::from(Punct::new(':', Alone)),
])
}
fn comma() -> TokenStream {
TokenStream::from(TokenTree::Punct(proc_macro::Punct::new(',', Alone)))
}
fn bang() -> TokenStream {
TokenStream::from(TokenTree::Punct(proc_macro::Punct::new('!', Alone)))
}
fn ident(s: &str) -> TokenStream {
use proc_macro::Ident;
TokenStream::from(TokenTree::from(Ident::new(s, Span::call_site())))
}
fn parenthesized(inner: TokenStream) -> TokenStream {
use proc_macro::Group;
TokenStream::from(TokenTree::Group(Group::new(Delimiter::Parenthesis, inner)))
}
fn lit<T: IntoLiteral>(n: T) -> TokenStream {
TokenStream::from(TokenTree::Literal(T::into_literal(n)))
}
trait IntoLiteral {
fn into_literal(self) -> proc_macro::Literal;
}
macro_rules! impl_into_literal {
($($ty:ty = $method:ident);* $(;)?) => {$(
impl IntoLiteral for $ty {
fn into_literal(self) -> proc_macro::Literal {
proc_macro::Literal::$method(self)
}
}
)*};
}
impl_into_literal! {
u8 = u8_unsuffixed;
u16 = u16_unsuffixed;
u32 = u32_unsuffixed;
&str = string;
}