use proc_macro2::{Delimiter, Ident, Punct, Spacing, Span, TokenStream, TokenTree};
use quote::{ToTokens, TokenStreamExt};
use syn::{FnArg, Type};
use crate::wit::extract_witified_map;
use crate::{ProviderBindgenConfig, StructLookup, TypeLookup};
pub(crate) trait ToRustType {
fn to_rust_type(&self, cfg: &ProviderBindgenConfig) -> anyhow::Result<TokenStream>;
}
pub(crate) fn count_preceeding_supers(t: &Type) -> usize {
if let Type::Path(t) = t {
t.path
.segments
.iter()
.filter(|s| s.ident == "super")
.count()
} else {
0
}
}
pub(crate) fn convert_to_owned_type_arg(
struct_lookup: &StructLookup,
type_lookup: &TypeLookup,
arg: &FnArg,
replace_witified_maps: bool,
) -> (Ident, TokenStream) {
let arg_name: Ident;
let mut tokens = TokenStream::new();
match &arg
.to_token_stream()
.into_iter()
.collect::<Vec<TokenTree>>()[..]
{
simple_ref @ [
TokenTree::Ident(ref n), TokenTree::Punct(_), TokenTree::Punct(ref p), TokenTree::Ident(ref t), ] if p.as_char() == '&' => {
arg_name = n.clone();
match t.to_string().as_str() {
"str" => {
tokens.append_all([
&simple_ref[0],
&simple_ref[1],
&TokenTree::Ident(Ident::new("String", t.span())),
]);
},
_ => {
tokens.append_all([
&simple_ref[0], &simple_ref[1], &simple_ref[3]
]);
}
}
},
arr_ref @ [
TokenTree::Ident(ref n), TokenTree::Punct(_), TokenTree::Punct(ref p), TokenTree::Group(ref t), ] if p.as_char() == '&' && t.delimiter() == Delimiter::Bracket => {
arg_name = n.clone();
tokens.append_all([
&arr_ref[0], &arr_ref[1], &TokenTree::Ident(Ident::new("Vec", Span::call_site())), &TokenTree::Punct(Punct::new('<', Spacing::Joint)), ]);
tokens.extend(t.stream());
tokens.append(TokenTree::Punct(Punct::new('>', Spacing::Joint))); },
wrapped_ref @ [
TokenTree::Ident(ref n), TokenTree::Punct(_), TokenTree::Ident(_), TokenTree::Punct(ref p), TokenTree::Punct(ref p2), .., TokenTree::Punct(_) ] if p.as_char() == '<' && p2.as_char() == '&' => {
arg_name = n.clone();
let type_section = &wrapped_ref[4..wrapped_ref.len()];
match type_section {
[
TokenTree::Punct(_), TokenTree::Ident(ref n),
TokenTree::Punct(_) ] if n.to_string().as_str() == "str" => {
tokens.append_all([
&wrapped_ref[0], &wrapped_ref[1], &wrapped_ref[2], &wrapped_ref[3], &TokenTree::Ident(Ident::new("String", n.span())),
&wrapped_ref[5], ]);
},
[
TokenTree::Punct(_), TokenTree::Group(g),
TokenTree::Punct(_), ] if g.to_string().as_str() == "[u8]" => {
tokens.append_all([
&wrapped_ref[0], &wrapped_ref[1], &wrapped_ref[2], &wrapped_ref[3], &TokenTree::Ident(Ident::new("Vec", Span::call_site())), &TokenTree::Punct(Punct::new('<', Spacing::Joint)), &TokenTree::Ident(Ident::new("u8", Span::call_site())), &TokenTree::Punct(Punct::new('>', Spacing::Joint)), &TokenTree::Punct(Punct::new('>', Spacing::Joint)), ]);
},
rest => {
let arg_type = &rest[1].to_string();
if let Some((struct_path, _)) = struct_lookup.get(arg_type) {
tokens.append_all(&wrapped_ref[0..5]);
tokens.append_all([ struct_path.to_token_stream() ]);
tokens.append_all(&wrapped_ref[6..]);
} else if let Some((type_path, _)) = type_lookup.get(arg_type) {
tokens.append_all(&wrapped_ref[0..5]);
tokens.append_all([ type_path.to_token_stream() ]);
tokens.append_all(&wrapped_ref[6..]);
} else {
tokens.append_all(wrapped_ref);
};
},
}
},
ts if replace_witified_maps
&& ts.len() > 2 && matches!(ts[0], TokenTree::Ident(ref n) if n.to_string().ends_with("_map"))
&& extract_witified_map(&ts[2..]).is_some() => {
let raw_arg_name = ts[0].to_string();
arg_name = Ident::new(raw_arg_name.as_str(), ts[0].span());
let trimmed_arg_name = Ident::new(raw_arg_name.trim_end_matches("_map"), ts[0].span());
let map_type = extract_witified_map(&ts[2..]).expect("failed to parse WIT-ified map type");
tokens.append_all(quote::quote!(#trimmed_arg_name: #map_type));
},
ts => {
if let TokenTree::Ident(name) = &ts[0] {
arg_name = name.clone();
} else {
panic!("unexpectedly missing the first token in FnArg");
}
tokens.append_all(ts);
}
}
(arg_name, tokens)
}