1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
//! This is a companion crate for [`iref`][iref] providing macros to build
//! `'static` URI/IRIs and URI/IRI references at compile time.
//!
//! [iref]: <https://github.com/timothee-haudebourg/iref>
//!
//! ## Basic usage
//!
//! Use the `uri!` (resp. `iri!`) macro to build URI (resp. IRI) statically, and
//! the `uri_ref!` (resp `iri_ref!`) macro to build URI (resp. IRI) references
//! statically.
//!
//! ```rust
//! use iref::{Iri, IriRef};
//! use static_iref::{iri, iri_ref};
//!
//! const IRI: &'static Iri = iri!("https://www.rust-lang.org/foo/bar#frag");
//! const IRI_REF: &'static IriRef = iri_ref!("/foo/bar#frag");
//! ```
use iref::{IriBuf, IriRefBuf, UriBuf, UriRefBuf};
use proc_macro::TokenStream;
use quote::quote;

/// Build an URI with a `'static` lifetime at compile time.
///
/// This macro expects a single string literal token representing the URI.
#[proc_macro]
pub fn uri(tokens: TokenStream) -> TokenStream {
	match syn::parse::<syn::LitStr>(tokens) {
		Ok(lit) => match UriBuf::new(lit.value().into_bytes()) {
			Ok(uri) => {
				let value = uri.as_bytes();
				quote! {
					unsafe {
						::iref::Uri::new_unchecked(&[#(#value),*])
					}
				}
				.into()
			}
			Err(_) => produce_error("invalid URI"),
		},
		Err(e) => e.to_compile_error().into(),
	}
}

/// Build an URI reference with a `'static` lifetime at compile time.
///
/// This macro expects a single string literal token representing the URI reference.
#[proc_macro]
pub fn uri_ref(tokens: TokenStream) -> TokenStream {
	match syn::parse::<syn::LitStr>(tokens) {
		Ok(lit) => match UriRefBuf::new(lit.value().into_bytes()) {
			Ok(uri_ref) => {
				let value = uri_ref.as_bytes();
				quote! {
					unsafe {
						::iref::UriRef::new_unchecked(&[#(#value),*])
					}
				}
				.into()
			}
			Err(_) => produce_error("invalid URI reference"),
		},
		Err(e) => e.to_compile_error().into(),
	}
}

/// Build an IRI with a `'static` lifetime at compile time.
///
/// This macro expects a single string literal token representing the IRI.
#[proc_macro]
pub fn iri(tokens: TokenStream) -> TokenStream {
	match syn::parse::<syn::LitStr>(tokens) {
		Ok(lit) => match IriBuf::new(lit.value()) {
			Ok(iri) => {
				let value = iri.as_str();
				quote! {
					unsafe {
						::iref::Iri::new_unchecked(#value)
					}
				}
				.into()
			}
			Err(_) => produce_error("invalid IRI"),
		},
		Err(e) => e.to_compile_error().into(),
	}
}

/// Build an IRI reference with a `'static` lifetime at compile time.
///
/// This macro expects a single string literal token representing the IRI reference.
#[proc_macro]
pub fn iri_ref(tokens: TokenStream) -> TokenStream {
	match syn::parse::<syn::LitStr>(tokens) {
		Ok(lit) => match IriRefBuf::new(lit.value()) {
			Ok(iri_ref) => {
				let value = iri_ref.as_str();
				quote! {
					unsafe {
						::iref::IriRef::new_unchecked(#value)
					}
				}
				.into()
			}
			Err(_) => produce_error("invalid IRI reference"),
		},
		Err(e) => e.to_compile_error().into(),
	}
}

fn produce_error(msg: &str) -> TokenStream {
	format!("compile_error!(\"{}\")", msg).parse().unwrap()
}