use crate::rust::util::*;
use crate::rust::{Attribute, ItemFn};
#[derive(Clone, Debug)]
pub struct ItemImpl {
pub attr: Attribute,
pub unsafe_: Option<Token![unsafe]>,
pub self_ty: Ident,
pub items: Vec<ItemFn>,
}
impl Parse for ItemImpl {
fn parse(input: ParseStream) -> Result<Self> {
let mut attr = Attribute::default();
attr.parse_outer(input)?;
input.parse::<Visibility>()?;
let unsafe_ = input.parse()?;
input.parse::<Token![impl]>()?;
ItemImpl::parse_remaining(input, attr, unsafe_)
}
}
impl ItemImpl {
pub fn parse_remaining(
input: ParseStream,
mut attr: Attribute,
unsafe_: Option<Token![unsafe]>,
) -> Result<Self> {
if let Some(lt_token) = input.parse::<Option<Token![<]>>()? {
return Err(Error::new(
lt_token.span(),
"generic parameters are not supported",
));
}
let fork = input.fork();
let leading_colon = fork.parse::<Option<Token![::]>>()?;
let ident = fork.parse::<Ident>();
let path_sep = fork.parse::<Option<Token![::]>>()?;
let lt_token = fork.parse::<Option<Token![<]>>()?;
if let Some(lt_token) = lt_token {
return Err(Error::new(
lt_token.span(),
"type arguments are not supported",
));
}
if let Some(leading_colon) = leading_colon {
return Err(Error::new(leading_colon.span(), "unsupported global path"));
}
if let Some(path_sep) = path_sep {
return Err(Error::new(
path_sep.span(),
"unsupported type path: only bare identifiers are supported with no path segments `::` nor type arguments. bring the type into scope with a `use` statement"
));
}
let self_ty;
if let Ok(ident) = ident {
input.advance_to(&fork);
self_ty = ident
} else {
let ty = input.call(syn::Type::parse);
if let Ok(ty) = ty {
return Err(Error::new(
ty.span(),
"unsupported type: only bare identifiers are supported",
));
} else {
return Err(ident.unwrap_err());
}
};
if let Some(for_) = input.parse::<Option<Token![for]>>()? {
return Err(Error::new(
for_.span(),
"unexpected token: trait implements are not supported",
));
}
if let Some(where_) = input.parse::<Option<Token![where]>>()? {
return Err(Error::new(
where_.span(),
"generic parameters and where clauses are not supported",
));
}
let content;
braced!(content in input);
attr.parse_inner(&content)?;
let mut items = Vec::new();
while !content.is_empty() {
let mut attr = Attribute::default();
attr.parse_outer(&content)?;
let vis = content.parse()?;
let fork = content.fork();
let item = ItemFn::parse_self_ty(&fork, attr, vis, Some(&self_ty));
if let Ok(item) = item {
let ident_str = item.ident.to_string();
match ident_str.as_str() {
"constructor" => {
return Err(Error::new(
item.ident.span(),
"`constructor` is a reserved JavaScript method for classes",
));
},
"ptr" | "take" => {
return Err(Error::new(
item.ident.span(),
format!("`{}` is a reserved name used by deno-bindgen2", ident_str),
));
},
_ => (),
}
content.advance_to(&fork);
items.push(item);
} else {
let syn_item = content.call(syn::ImplItem::parse)?;
match syn_item {
syn::ImplItem::Fn(_) => {
return Err(item.unwrap_err());
},
_ => (),
};
}
}
Ok(Self {
attr,
unsafe_,
self_ty,
items,
})
}
}
#[cfg(test)]
mod parse_tests {
use super::*;
#[test]
fn with_attrs_and_vis() {
dbg_quote!(
ItemImpl,
#[some_attr]
pub impl CustomType {}
);
}
#[test]
fn with_unsafe() {
dbg_quote!(ItemImpl, unsafe impl CustomType {});
}
#[test]
#[should_panic]
fn with_generics() {
dbg_quote!(ItemImpl, impl<T> CustomType {});
}
#[test]
fn with_empty() {
dbg_quote!(ItemImpl, impl CustomType {});
}
#[test]
#[should_panic]
fn with_for() {
dbg_quote!(ItemImpl, impl Debug for CustomType {});
}
#[test]
#[should_panic]
fn with_global_path() {
dbg_quote!(ItemImpl,
impl ::CustomType {
}
);
}
#[test]
#[should_panic]
fn with_path() {
dbg_quote!(ItemImpl,
impl my_mod::CustomType {
}
);
}
#[test]
#[should_panic]
fn with_where() {
dbg_quote!(ItemImpl,
impl CustomType
where
T: Drop
{
}
);
}
#[test]
fn with_item_fn() {
dbg_quote!(ItemImpl,
impl CustomType {
fn test_fn() {}
}
);
}
#[test]
fn with_nested_attrs_and_vis() {
dbg_quote!(
ItemImpl,
#[impl_outer_attr]
impl CustomType {
#![impl_inner_attr]
#[fn_outer_attr]
pub fn test_fn() {
#![fn_inner_attr]
}
}
);
}
#[test]
fn with_many() {
dbg_quote!(ItemImpl,
impl CustomType {
fn test_fn() {}
fn test_fn2() {}
fn test_fn3() {}
}
);
}
#[test]
#[should_panic]
fn with_self() {
dbg_quote!(ItemImpl,
impl CustomType {
fn test_fn(self) {}
}
);
}
#[test]
fn with_unsafe_self() {
dbg_quote!(
ItemImpl,
unsafe impl CustomType {
unsafe fn test_fn(self) {}
}
);
}
#[test]
fn with_self_ref() {
dbg_quote!(ItemImpl,
impl CustomType {
fn test_fn(&mut self) {}
}
);
}
#[test]
fn with_other_self() {
dbg_quote!(ItemImpl,
impl CustomType {
fn test_fn(arg0: Self) {}
}
);
}
#[test]
fn with_other_selves() {
dbg_quote!(ItemImpl,
impl CustomType {
fn test_fn(
&mut self,
arg0: (Vec<Self>, &mut Self),
arg3: Box<Self>
) -> Box<Self> {}
}
);
}
#[test]
fn with_other_items() {
dbg_quote!(ItemImpl,
impl CustomType {
fn test_fn() {}
type Some = usize;
fn test_fn2() {}
const SOME_STR: &str = "Str";
}
);
}
}
impl ItemImpl {
pub fn transform(&mut self) {
for item in &mut self.items {
item.transform();
}
}
}
impl ToTokens for ItemImpl {
fn to_tokens(&self, tokens: &mut TokenStream) {
let self_ty = &self.self_ty;
let items = &self.items;
tokens.extend(quote! {
#(#items)*
const _: () = {
const fn assert_impl<T: deno_bindgen2::DenoBindgen>() {}
assert_impl::<#self_ty>();
};
});
}
}
#[cfg(test)]
mod print_tests {
use super::*;
#[test]
fn test_print_impl() {
let mut raw = parse_quote!(ItemImpl,
impl CustomType {
fn new() -> Self {}
}
);
raw.transform();
println!(
"{}",
crate::prettify!(raw.to_token_stream().to_string().as_str())
);
}
}