1#![cfg_attr(feature = "document-features", doc = "\n## Features")]
50#![cfg_attr(feature = "document-features", doc = document_features::document_features!())]
51#![cfg_attr(docsrs, feature(doc_cfg))]
52
53use proc_macro::TokenStream;
54
55use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
56use std::str::FromStr;
57
58use proc_macro_error::{abort_call_site, emit_error, proc_macro_error};
59use quote::quote;
60use syn::{parse, LitStr};
61
62macro_rules! make_macro {
70 ($macro_name:ident: $ty:ty =? $dummy:expr; $generate_fn:ident => $body:expr; $tyname:expr; $feat:expr) => {
71 #[cfg_attr(docsrs, doc(cfg(feature = $feat)))]
72 #[doc = "generates [`"]
73 #[doc = $tyname]
74 #[doc = "`]"]
75 #[proc_macro]
76 #[proc_macro_error]
77 pub fn $macro_name(input: TokenStream) -> TokenStream {
78 let inval = match parse::<LitStr>(input) {
79 Ok(ls) => ls,
80 Err(e) => {
81 abort_call_site!(
82 e.to_string();
83 help = "Use the string literal for this type's FromStr impl";
84 );
85 }
86 };
87
88 let val = match <$ty>::from_str(inval.value().as_str()) {
90 Ok(v) => v,
92 Err(e) => {
93 emit_error!(inval, e);
96 $dummy
98 }
99 };
100 $generate_fn(val).into()
101 }
102
103 fn $generate_fn(input: $ty) -> proc_macro2::TokenStream {
104 Some(input).map($body).unwrap()
105 }
106 };
107 ($macro_name:ident: $ty:ty =? $dummy:expr; $generate_fn:ident => $body:expr) => {
108 make_macro!($macro_name: $ty =? $dummy; $generate_fn => $body; stringify!($ty); "default");
109 };
110 ($macro_name:ident: $ty:ty =? $dummy:expr; $generate_fn:ident => $body:expr; $feat:expr) => {
111 make_macro!($macro_name: $ty =? $dummy; $generate_fn => $body; stringify!($ty); $feat);
112 }
113}
114
115make_macro! {
118 ip: IpAddr =? IpAddr::V4(Ipv4Addr::UNSPECIFIED);
119 ipaddr_tokens => |input| match input {
120 IpAddr::V4(ip) => {
121 let inner = ip4_tokens(ip);
122 quote! { ::std::net::IpAddr::V4(#inner) }
123 }
124 IpAddr::V6(ip) => {
125 let inner = ip6_tokens(ip);
126 quote! { ::std::net::IpAddr::V6(#inner) }
127 }
128 }
129}
130
131make_macro! {
132 ip4: Ipv4Addr =? Ipv4Addr::UNSPECIFIED;
133 ip4_tokens => |input| {
134 let octets = input.octets();
135 quote! { ::std::net::Ipv4Addr::new(#(#octets),*) }
136 }
137}
138
139make_macro! {
140 ip6: Ipv6Addr =? Ipv6Addr::UNSPECIFIED;
141 ip6_tokens => |input| {
142 let segments = input.segments();
143 quote! { ::std::net::Ipv6Addr::new(#(#segments),*) }
144 }
145}
146
147make_macro! {
150 sock: SocketAddr =? SocketAddr::V4(SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0));
151 sockaddr_tokens => |addr| {
152 match addr {
153 SocketAddr::V4(sock) => {
154 let inner = sock4_tokens(sock);
155 quote! { ::std::net::SocketAddr::V4(#inner) }
156 }
157 SocketAddr::V6(sock) => {
158 let inner = sock6_tokens(sock);
159 quote! { ::std::net::SocketAddr::V6(#inner) }
160 }
161 }
162 }
163}
164
165make_macro! {
166 sock4: SocketAddrV4 =? SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
167 sock4_tokens => |input| {
168 let ip = ip4_tokens(*input.ip());
169 let port = input.port();
170 quote! { ::std::net::SocketAddrV4::new(#ip, #port) }
171 }
172}
173
174make_macro! {
175 sock6: SocketAddrV6 =? SocketAddrV6::new(Ipv6Addr::UNSPECIFIED, 0, 0, 0);
176 sock6_tokens => |addr| {
177 let ip = ip6_tokens(*addr.ip());
178 let port = addr.port();
179 quote! { ::std::net::SocketAddrV6::new(#ip, #port, 0, 0) }
180 }
181}
182
183cfg_if::cfg_if! {
186 if #[cfg(feature = "ipnet")] {
187 use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
188
189 make_macro!{
190 net: IpNetwork =? IpNetwork::V4(Ipv4Network::new(Ipv4Addr::UNSPECIFIED, 0).unwrap());
191 net_tokens => |net| match net {
192 IpNetwork::V4(net) => {
193 let inner = net4_tokens(net);
194 quote! { ipnetwork::IpNetwork::V4(#inner) }
195 }
196 IpNetwork::V6(net) => {
197 let inner = net6_tokens(net);
198 quote! { ipnetwork::IpNetwork::V6(#inner) }
199 }
200 };
201 "ipnet"
202 }
203
204 make_macro!{
205 net4: Ipv4Network =? Ipv4Network::new(Ipv4Addr::UNSPECIFIED, 0).unwrap();
206 net4_tokens => |net| {
207 let ip = ip4_tokens(net.ip());
208 let prefix = net.prefix();
209 quote! { ipnetwork::Ipv4Network::new_checked(#ip, #prefix).unwrap() }
210 };
211 "ipnet"
212 }
213
214
215 make_macro!{
216 net6: Ipv6Network =? Ipv6Network::new(Ipv6Addr::UNSPECIFIED, 0).unwrap();
217 net6_tokens => |net| {
218 let ip = ip6_tokens(net.ip());
219 let prefix = net.prefix();
220 quote! { ipnetwork::Ipv6Network::new_checked(#ip, #prefix).unwrap() }
221 };
222 "ipnet"
223 }
224 }
225}
226
227cfg_if::cfg_if! {
230 if #[cfg(feature = "mac")] {
231 use macaddr::{MacAddr, MacAddr6, MacAddr8};
232
233 make_macro!{
234 mac: MacAddr =? MacAddr::V6(MacAddr6::from([0x00; 6]));
235 mac_tokens => |addr| match addr {
236 MacAddr::V6(addr) => {
237 let inner = mac6_tokens(addr);
238 quote! { macaddr::MacAddr::V6(#inner) }
239 }
240 MacAddr::V8(addr) => {
241 let inner = mac8_tokens(addr);
242 quote! { macaddr::MacAddr::V8(#inner) }
243 }
244 };
245 "mac"
246 }
247
248 make_macro! {
249 mac6: MacAddr6 =? MacAddr6::from([0x00; 6]);
250 mac6_tokens => |addr| {
251 let bytes = addr.into_array();
252 quote! { macaddr::MacAddr6::new(#(#bytes),*) }
253 };
254 "mac"
255 }
256
257 make_macro!{
258 mac8: MacAddr8 =? MacAddr8::from([0x00; 8]);
259 mac8_tokens => |addr| {
260 let bytes = addr.into_array();
261 quote! { macaddr::MacAddr8::new(#(#bytes),*) }
262 };
263 "mac"
264 }
265 }
266}
267
268#[cfg(test)]
269mod tests {
270 #[test]
271 fn compilation() {
272 let t = trybuild::TestCases::new();
273
274 t.compile_fail("tests/fail/ip.rs");
275 t.compile_fail("tests/fail/ip4.rs");
276 t.compile_fail("tests/fail/ip6.rs");
277 t.compile_fail("tests/fail/sock.rs");
278 t.compile_fail("tests/fail/sock4.rs");
279 t.compile_fail("tests/fail/sock6.rs");
280 t.compile_fail("tests/fail/net.rs");
281 t.compile_fail("tests/fail/net4.rs");
282 t.compile_fail("tests/fail/net6.rs");
283 t.compile_fail("tests/fail/mac.rs");
284 t.compile_fail("tests/fail/mac6.rs");
285 t.compile_fail("tests/fail/mac8.rs");
286
287 t.pass("tests/pass/ip.rs");
288 t.pass("tests/pass/ip4.rs");
289 t.pass("tests/pass/ip6.rs");
290 t.pass("tests/pass/sock.rs");
291 t.pass("tests/pass/sock4.rs");
292 t.pass("tests/pass/sock6.rs");
293 t.pass("tests/pass/net.rs");
294 t.pass("tests/pass/net4.rs");
295 t.pass("tests/pass/net6.rs");
296 t.pass("tests/pass/mac.rs");
297 t.pass("tests/pass/mac6.rs");
298 t.pass("tests/pass/mac8.rs");
299 }
300}