#![allow(clippy::too_many_arguments)]
#![allow(clippy::needless_late_init)]
use proc_macro::TokenStream;
mod de;
mod en;
mod expander;
mod internals;
#[cfg(feature = "sneaky-fields")]
mod sneaky_fields;
#[cfg(feature = "test")]
mod test;
mod types;
mod visit;
mod zero_copy;
#[proc_macro_derive(Encode, attributes(musli))]
pub fn derive_encode(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let expander = expander::Expander::new(&input);
let dump = std::env::var("MUSLI_DUMP_ENCODE").ok();
match expander.expand_encode() {
Ok(tokens) => {
if let Some((dump, out)) = dump.as_ref().and_then(|d| d.split_once('=')) {
if input.ident.to_string().contains(dump) {
let _ = std::fs::write(out, format!("{}", tokens));
}
}
tokens.into()
}
Err(()) => to_compile_errors(expander.into_errors()).into(),
}
}
#[proc_macro_derive(Decode, attributes(musli))]
pub fn derive_decode(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let expander = expander::Expander::new(&input);
let dump = std::env::var("MUSLI_DUMP_DECODE").ok();
match expander.expand_decode() {
Ok(tokens) => {
if let Some((dump, out)) = dump.as_ref().and_then(|d| d.split_once('=')) {
if input.ident.to_string().contains(dump) {
let _ = std::fs::write(out, format!("{}", tokens));
}
}
tokens.into()
}
Err(()) => to_compile_errors(expander.into_errors()).into(),
}
}
#[proc_macro_attribute]
pub fn decoder(attr: TokenStream, input: TokenStream) -> TokenStream {
let attr = proc_macro2::TokenStream::from(attr);
if !attr.is_empty() {
return syn::Error::new_spanned(attr, "Arguments not supported")
.to_compile_error()
.into();
}
let input = syn::parse_macro_input!(input as types::Types);
match input.expand(
"decoder",
&types::DECODER_TYPES,
["Error"],
"__UseMusliDecoderAttributeMacro",
) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro_attribute]
pub fn encoder(attr: TokenStream, input: TokenStream) -> TokenStream {
let attr = proc_macro2::TokenStream::from(attr);
if !attr.is_empty() {
return syn::Error::new_spanned(attr, "Arguments not supported")
.to_compile_error()
.into();
}
let input = syn::parse_macro_input!(input as types::Types);
match input.expand(
"encoder",
&types::ENCODER_TYPES,
["Ok", "Error"],
"__UseMusliEncoderAttributeMacro",
) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro_attribute]
pub fn visitor(attr: TokenStream, input: TokenStream) -> TokenStream {
let attr = proc_macro2::TokenStream::from(attr);
if !attr.is_empty() {
return syn::Error::new_spanned(attr, "Arguments not supported")
.to_compile_error()
.into();
}
let input = syn::parse_macro_input!(input as types::Types);
match input.expand(
"visitor",
&types::VISITOR_TYPES,
["Ok"],
"__UseMusliVisitorAttributeMacro",
) {
Ok(tokens) => tokens.into(),
Err(err) => err.to_compile_error().into(),
}
}
#[proc_macro_derive(ZeroCopy, attributes(zero_copy))]
pub fn zero_copy(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let expander = zero_copy::Expander::new(input);
match expander.expand() {
Ok(stream) => stream.into(),
Err(errors) => to_compile_errors(errors).into(),
}
}
#[proc_macro_derive(Visit, attributes(visit))]
pub fn visit(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let expander = visit::Expander::new(&input);
match expander.expand() {
Ok(stream) => stream.into(),
Err(errors) => to_compile_errors(errors).into(),
}
}
#[proc_macro_attribute]
#[doc(hidden)]
#[cfg(feature = "sneaky-fields")]
pub fn sneaky_fields(attr: TokenStream, item: TokenStream) -> TokenStream {
sneaky_fields::expand(attr, item)
}
fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
let mut output = proc_macro2::TokenStream::new();
for e in errors {
output.extend(e.to_compile_error());
}
output
}
#[cfg(feature = "test")]
#[proc_macro_derive(Generate, attributes(generate))]
pub fn derive_generate(input: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(input as syn::DeriveInput);
let mut cx = test::Ctxt::default();
if let Ok(stream) = test::expand(&mut cx, &input) {
return stream.into();
}
let mut stream = proc_macro2::TokenStream::default();
for error in cx.errors {
stream.extend(error.to_compile_error());
}
stream.into()
}