use crate::rust::util::*;
use crate::rust::Item;
#[derive(Clone, Debug, PartialEq)]
pub enum Marker {
DenoBindgen, NonBlocking,
}
#[cfg(feature = "macro")]
impl Marker {
pub fn deno_bindgen(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = TokenStream::from(input);
let mut item: Item = match syn::parse2(input.clone()) {
Ok(item) => item,
Err(err) => return err.to_compile_error().into(),
};
item.transform();
quote! {
#[cfg_attr(not(deno_bindgen), doc = "deno_bindgen")]
#input
#item
}
.into()
}
pub fn non_blocking(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
let input = TokenStream::from(input);
quote! {
#[cfg_attr(not(deno_bindgen), doc = "deno_bindgen_non_blocking")]
#input
}
.into()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct Meta {
pub lit_str: LitStr,
}
impl TryFrom<&Meta> for Marker {
fn try_from(value: &Meta) -> Result<Self> {
match value.lit_str.value().as_str() {
"deno_bindgen" => Ok(Self::DenoBindgen),
"deno_bindgen_non_blocking" => Ok(Self::NonBlocking),
_ => Err(Error::new(
value.lit_str.span(),
"unknown value. expected one of `deno_bindgen`, `deno_bindgen_non_blocking`, `deno_bindgen_constructor`"
)),
}
}
type Error = Error;
}
impl Parse for Meta {
fn parse(input: ParseStream) -> Result<Self> {
let key = input.parse::<Ident>()?;
if input.is_empty() {
let key_str = key.to_string();
match key_str.as_str() {
"non_blocking" => {
let lit_str = LitStr::new(
format!("deno_bindgen_{key_str}").as_str(),
Span::mixed_site(),
);
return Ok(Self { lit_str });
},
_ => (),
}
}
input.parse::<Token![=]>()?;
if key.to_string().as_str() == "doc" {
let lit_str = input.parse()?;
if !input.is_empty() {
Err(input.error("unknown token"))
} else {
Ok(Self { lit_str })
}
} else {
Err(Error::new(key.span(), "expected `doc` key"))
}
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Attribute {
pub markers: Vec<Marker>,
pub meta: Vec<Meta>,
}
impl Attribute {
pub fn has_deno_bindgen(&self) -> bool {
self.markers
.iter()
.find(|marker| match marker {
Marker::DenoBindgen => true,
_ => false,
})
.is_some()
}
pub fn has_non_blocking(&self) -> bool {
self.markers
.iter()
.find(|marker| match marker {
Marker::NonBlocking => true,
_ => false,
})
.is_some()
}
}
impl Attribute {
pub fn parse_outer(&mut self, input: ParseStream) -> Result<()> {
while input.peek(Token![#]) {
input.parse::<Token![#]>()?;
if input.peek(Token![!]) {
return Err(Error::new(
input.span(),
"attempted to parse inner attribute in a parser for outer attributes",
));
}
let content;
bracketed!(content in input);
let fork = content.fork();
if let Ok(meta) = fork.parse::<Meta>() {
content.advance_to(&fork);
if let Ok(marker) = Marker::try_from(&meta) {
self.markers.push(marker);
} else {
self.meta.push(meta);
}
} else {
content.parse::<syn::Meta>()?;
}
}
Ok(())
}
pub fn parse_inner(&mut self, input: ParseStream) -> Result<()> {
while input.peek(Token![#]) && input.peek2(Token![!]) {
input.parse::<Token![#]>()?;
input.parse::<Token![!]>()?;
let content;
bracketed!(content in input);
let fork = content.fork();
if let Ok(meta) = fork.parse::<Meta>() {
content.advance_to(&fork);
self.meta.push(meta);
} else {
content.parse::<syn::Meta>()?;
}
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
impl Parse for Attribute {
fn parse(input: ParseStream) -> Result<Self> {
let mut attr = Self::default();
let ahead = input.lookahead1();
if ahead.peek(Token![#]) {
if input.peek2(Token![!]) {
attr.parse_inner(input)?;
} else {
attr.parse_outer(input)?;
}
} else {
return Err(ahead.error());
}
Ok(attr)
}
}
#[test]
fn test_attr() {
dbg_quote!(Attribute,
#[some_attr]
#[doc = "some unknown value"]
#[another_attr]
);
}
#[test]
#[should_panic]
fn test_mix_attr() {
dbg_quote!(Attribute,
#[outer]
#![innter]
);
}
#[test]
fn test_marker() {
dbg_quote!(Attribute,
#[doc = "deno_bindgen_constructor"]
#[doc = "deno_bindgen"]
);
}
#[test]
fn test_cfg_attr() {
dbg_quote!(Attribute,
#[cfg_attr(not(deno_bindgen), doc = "deno_bindgen_constructor")]
);
}
#[test]
fn test_live_attr() {
dbg_quote!(Attribute,
#[constructor]
);
}
}