Skip to main content

eure_macros/
lib.rs

1use darling::FromDeriveInput;
2use syn::parse_macro_input;
3
4use crate::attrs::{ContainerAttrs, extract_container_attr_spans};
5use crate::config::MacroConfig;
6use crate::context::MacroContext;
7
8mod attrs;
9mod build_schema;
10pub(crate) mod config;
11pub(crate) mod context;
12mod from_eure;
13mod into_eure;
14mod must_be_text;
15mod util;
16
17#[proc_macro_derive(IntoEure, attributes(eure))]
18pub fn into_eure_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
19    let input = parse_macro_input!(input as syn::DeriveInput);
20    match create_context(input) {
21        Ok(context) => into_eure::derive(context).into(),
22        Err(err) => err.to_compile_error().into(),
23    }
24}
25
26#[proc_macro_derive(FromEure, attributes(eure))]
27pub fn from_eure_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
28    let input = parse_macro_input!(input as syn::DeriveInput);
29    match create_context(input) {
30        Ok(context) => from_eure::derive(context).into(),
31        Err(err) => err.to_compile_error().into(),
32    }
33}
34
35#[proc_macro_derive(BuildSchema, attributes(eure))]
36pub fn build_schema_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
37    let input = parse_macro_input!(input as syn::DeriveInput);
38    match create_context(input) {
39        Ok(context) => build_schema::derive(context).into(),
40        Err(err) => err.to_compile_error().into(),
41    }
42}
43
44fn create_context(input: syn::DeriveInput) -> syn::Result<MacroContext> {
45    let attrs = ContainerAttrs::from_derive_input(&input).expect("Failed to parse eure attributes");
46    let attr_spans = extract_container_attr_spans(&input);
47    let mut config = MacroConfig::from_attrs(attrs, attr_spans)?;
48    if has_non_exhaustive_attr(&input.attrs) {
49        config.non_exhaustive = true;
50    }
51    Ok(MacroContext::new(config, input))
52}
53
54fn has_non_exhaustive_attr(attrs: &[syn::Attribute]) -> bool {
55    attrs
56        .iter()
57        .any(|attr| attr.path().is_ident("non_exhaustive"))
58}
59
60/// Creates a zero-sized type that only parses from a specific Text value.
61///
62/// # Syntax
63///
64/// ```ignore
65/// MustBeText!("content")           // Implicit language: `content`
66/// MustBeText!(plaintext, "content") // Plaintext language: "content"
67/// MustBeText!(rust, "content")      // Other language: rust`content`
68/// ```
69///
70/// # Example
71///
72/// ```ignore
73/// use eure_macros::MustBeText;
74///
75/// // This type only successfully parses from the text value `any`
76/// let marker = MustBeText!("any");
77/// ```
78#[proc_macro]
79#[allow(non_snake_case)]
80pub fn MustBeText(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
81    let input = parse_macro_input!(input as must_be_text::MustBeTextInput);
82    must_be_text::expand(input).into()
83}