use serde_derive_internals::ast::Field;
use syn::Fields;
#[derive(Debug, Default)]
pub struct TsifyContainerAttars {
pub into_wasm_abi: bool,
pub from_wasm_abi: bool,
pub namespace: bool,
pub string_enum: bool,
pub maps_as_objects: bool,
}
impl TsifyContainerAttars {
pub fn from_derive_input(input: &syn::DeriveInput) -> syn::Result<Self> {
let mut attrs = Self {
into_wasm_abi: false,
from_wasm_abi: false,
namespace: false,
string_enum: false,
maps_as_objects: false,
};
for attr in &input.attrs {
if !attr.path().is_ident("tsify") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("into_wasm_abi") {
if attrs.into_wasm_abi {
return Err(meta.error("duplicate attribute"));
}
attrs.into_wasm_abi = true;
return Ok(());
}
if meta.path.is_ident("from_wasm_abi") {
if attrs.from_wasm_abi {
return Err(meta.error("duplicate attribute"));
}
attrs.from_wasm_abi = true;
return Ok(());
}
if meta.path.is_ident("namespace") {
if !matches!(input.data, syn::Data::Enum(_)) {
return Err(meta.error("#[tsify(namespace)] can only be used on enums"));
}
if attrs.namespace {
return Err(meta.error("duplicate attribute"));
}
attrs.namespace = true;
return Ok(());
}
if meta.path.is_ident("string_enum") {
let syn::Data::Enum(enum_data) = &input.data else {
return Err(meta.error("#[tsify(string_enum)] can only be used on enums"));
};
if !enum_data.variants.iter().all(|variant| {
matches!(variant.fields, Fields::Unit)
}) {
return Err(meta.error("#[tsify(string_enum)] can only be used on enums with only unit variants"));
}
if attrs.string_enum {
return Err(meta.error("duplicate attribute"));
}
attrs.string_enum = true;
return Ok(());
}
if meta.path.is_ident("maps_as_objects") {
if attrs.maps_as_objects {
return Err(meta.error("duplicate attribute"));
}
attrs.maps_as_objects = true;
return Ok(());
}
Err(meta.error("unsupported tsify attribute, expected one of `into_wasm_abi`, `from_wasm_abi`, `namespace`, `string_enum`, `maps_as_objects`"))
})?;
}
Ok(attrs)
}
}
#[derive(Debug, Default)]
pub struct TsifyFieldAttrs {
pub type_override: Option<String>,
pub optional: bool,
}
impl TsifyFieldAttrs {
pub fn from_serde_field(field: &Field) -> syn::Result<Self> {
let mut attrs = Self {
type_override: None,
optional: false,
};
for attr in &field.original.attrs {
if !attr.path().is_ident("tsify") {
continue;
}
attr.parse_nested_meta(|meta| {
if meta.path.is_ident("type") {
if attrs.type_override.is_some() {
return Err(meta.error("duplicate attribute"));
}
let lit = meta.value()?.parse::<syn::LitStr>()?;
attrs.type_override = Some(lit.value());
return Ok(());
}
if meta.path.is_ident("optional") {
if attrs.optional {
return Err(meta.error("duplicate attribute"));
}
attrs.optional = true;
return Ok(());
}
Err(meta.error("unsupported tsify attribute, expected one of `type` or `optional`"))
})?;
}
if let Some(expr) = field.attrs.skip_serializing_if() {
let path = expr
.path
.segments
.iter()
.map(|segment| segment.ident.to_string())
.collect::<Vec<_>>()
.join("::");
attrs.optional |= &path == "Option::is_none";
}
Ok(attrs)
}
}