musli_macros/
lib.rs

1//! [<img alt="github" src="https://img.shields.io/badge/github-udoprog/musli-8da0cb?style=for-the-badge&logo=github" height="20">](https://github.com/udoprog/musli)
2//! [<img alt="crates.io" src="https://img.shields.io/crates/v/musli-macros.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/musli-macros)
3//! [<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-musli--macros-66c2a5?style=for-the-badge&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K" height="20">](https://docs.rs/musli-macros)
4//!
5//! This crate provides the macros used in [Müsli]. The API of these macros is
6//! not expected to be stable and **must not** be used directly.
7//!
8//! Use the macros through the [`musli`] or [`musli_core`] crates instead.
9//!
10//! Please refer to <https://docs.rs/musli> for documentation.
11//!
12//! [`musli_core`]: <https://docs.rs/musli_core>
13//! [`musli`]: <https://docs.rs/musli>
14//! [Müsli]: <https://docs.rs/musli>
15
16#![allow(clippy::too_many_arguments)]
17#![allow(clippy::needless_late_init)]
18#![allow(clippy::type_complexity)]
19#![allow(missing_docs)]
20
21mod de;
22mod en;
23mod expander;
24mod internals;
25mod types;
26
27use proc_macro::TokenStream;
28use quote::ToTokens;
29use syn::parse::ParseStream;
30
31const MUSLI_CRATE: &str = "musli";
32const MUSLI_CORE_CRATE: &str = "musli_core";
33
34#[proc_macro_derive(Encode, attributes(musli))]
35#[doc(hidden)]
36pub fn musli_derive_encode(input: TokenStream) -> TokenStream {
37    derive_encode(input, MUSLI_CRATE)
38}
39
40#[proc_macro_derive(Decode, attributes(musli))]
41#[doc(hidden)]
42pub fn musli_derive_decode(input: TokenStream) -> TokenStream {
43    derive_decode(input, MUSLI_CRATE)
44}
45
46fn derive_encode(input: TokenStream, crate_default: &str) -> TokenStream {
47    let input = syn::parse_macro_input!(input as syn::DeriveInput);
48    let expander = expander::Expander::new(&input, crate_default);
49
50    match expander.expand_encode() {
51        Ok(tokens) => tokens.into(),
52        Err(()) => to_compile_errors(expander.into_errors()).into(),
53    }
54}
55
56fn derive_decode(input: TokenStream, crate_default: &str) -> TokenStream {
57    let input = syn::parse_macro_input!(input as syn::DeriveInput);
58    let expander = expander::Expander::new(&input, crate_default);
59
60    match expander.expand_decode() {
61        Ok(tokens) => tokens.into(),
62        Err(()) => to_compile_errors(expander.into_errors()).into(),
63    }
64}
65
66#[proc_macro_attribute]
67#[doc(hidden)]
68pub fn musli_trait_defaults(attr: TokenStream, input: TokenStream) -> TokenStream {
69    trait_defaults(attr, input, MUSLI_CRATE)
70}
71
72#[proc_macro_attribute]
73#[doc(hidden)]
74pub fn musli_core_trait_defaults(attr: TokenStream, input: TokenStream) -> TokenStream {
75    trait_defaults(attr, input, MUSLI_CORE_CRATE)
76}
77
78fn trait_defaults(
79    attr: TokenStream,
80    input: TokenStream,
81    default_crate: &'static str,
82) -> TokenStream {
83    let attr = syn::parse_macro_input!(attr as types::Attr);
84    let parser = move |input: ParseStream| types::parse(input, attr, default_crate);
85
86    match syn::parse::Parser::parse(parser, input) {
87        Ok(types) => types.to_token_stream().into(),
88        Err(err) => err.to_compile_error().into(),
89    }
90}
91
92fn to_compile_errors(errors: Vec<syn::Error>) -> proc_macro2::TokenStream {
93    let mut output = proc_macro2::TokenStream::new();
94
95    for e in errors {
96        output.extend(e.to_compile_error());
97    }
98
99    output
100}