use std::path::Path;
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use serde_json::Value as JsonValue;
use url::Url;
#[macro_export]
macro_rules! literal_struct {
($tokens:ident, $struct:path, $($field:ident),+) => {
$tokens.append_all(quote! {
$struct {
$($field: #$field),+
}
})
};
}
pub fn str_lit(s: impl AsRef<str>) -> TokenStream {
let s = s.as_ref();
quote! { #s.into() }
}
pub fn opt_lit(item: Option<&impl ToTokens>) -> TokenStream {
match item {
None => quote! { ::core::option::Option::None },
Some(item) => quote! { ::core::option::Option::Some(#item) },
}
}
pub fn opt_lit_owned(item: Option<impl ToTokens>) -> TokenStream {
match item {
None => quote! { ::core::option::Option::None },
Some(item) => quote! { ::core::option::Option::Some(#item) },
}
}
pub fn opt_str_lit(item: Option<impl AsRef<str>>) -> TokenStream {
opt_lit(item.map(str_lit).as_ref())
}
pub fn opt_vec_lit<Raw, Tokens>(
item: Option<impl IntoIterator<Item = Raw>>,
map: impl Fn(Raw) -> Tokens,
) -> TokenStream
where
Tokens: ToTokens,
{
opt_lit(item.map(|list| vec_lit(list, map)).as_ref())
}
pub fn vec_lit<Raw, Tokens>(
list: impl IntoIterator<Item = Raw>,
map: impl Fn(Raw) -> Tokens,
) -> TokenStream
where
Tokens: ToTokens,
{
let items = list.into_iter().map(map);
quote! { vec![#(#items),*] }
}
pub fn path_buf_lit(s: impl AsRef<Path>) -> TokenStream {
let s = s.as_ref().to_string_lossy().into_owned();
quote! { ::std::path::PathBuf::from(#s) }
}
pub fn url_lit(url: &Url) -> TokenStream {
let url = url.as_str();
quote! { #url.parse().unwrap() }
}
pub fn map_lit<Map, Key, Value, TokenStreamKey, TokenStreamValue, FuncKey, FuncValue>(
map_type: TokenStream,
map: Map,
map_key: FuncKey,
map_value: FuncValue,
) -> TokenStream
where
<Map as IntoIterator>::IntoIter: ExactSizeIterator,
Map: IntoIterator<Item = (Key, Value)>,
TokenStreamKey: ToTokens,
TokenStreamValue: ToTokens,
FuncKey: Fn(Key) -> TokenStreamKey,
FuncValue: Fn(Value) -> TokenStreamValue,
{
let ident = quote::format_ident!("map");
let map = map.into_iter();
if map.len() > 0 {
let items = map.map(|(key, value)| {
let key = map_key(key);
let value = map_value(value);
quote! { #ident.insert(#key, #value); }
});
quote! {{
let mut #ident = #map_type::new();
#(#items)*
#ident
}}
} else {
quote! { #map_type::new() }
}
}
pub fn json_value_number_lit(num: &serde_json::Number) -> TokenStream {
let prefix = quote! { ::serde_json::Value };
if num.is_u64() {
let num = num.as_u64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else if num.is_i64() {
let num = num.as_i64().unwrap();
quote! { #prefix::Number(#num.into()) }
} else if num.is_f64() {
let num = num.as_f64().unwrap();
quote! { #prefix::Number(::serde_json::Number::from_f64(#num).unwrap()) }
} else {
quote! { #prefix::Null }
}
}
pub fn json_value_lit(jv: &JsonValue) -> TokenStream {
let prefix = quote! { ::serde_json::Value };
match jv {
JsonValue::Null => quote! { #prefix::Null },
JsonValue::Bool(bool) => quote! { #prefix::Bool(#bool) },
JsonValue::Number(number) => json_value_number_lit(number),
JsonValue::String(str) => {
let s = str_lit(str);
quote! { #prefix::String(#s) }
}
JsonValue::Array(vec) => {
let items = vec.iter().map(json_value_lit);
quote! { #prefix::Array(vec![#(#items),*]) }
}
JsonValue::Object(map) => {
let map = map_lit(quote! { ::serde_json::Map }, map, str_lit, json_value_lit);
quote! { #prefix::Object(#map) }
}
}
}