strid_macros/lib.rs
1//! You probably want the [`strid`] crate, which
2//! has the documentation this crate lacks.
3//!
4//! [`strid`]: https://docs.rs/strid/*/strid/
5
6#![warn(
7 missing_docs,
8 unused_import_braces,
9 unused_imports,
10 unused_qualifications
11)]
12#![deny(
13 missing_debug_implementations,
14 trivial_casts,
15 trivial_numeric_casts,
16 unused_must_use
17)]
18#![forbid(unsafe_code)]
19
20extern crate proc_macro;
21
22mod attr_grammar;
23mod codegen;
24mod grammar;
25
26use codegen::{Params, ParamsRef};
27use grammar::ItemStruct;
28use proc_macro::TokenStream;
29use proc_macro2::TokenStream as TokenStream2;
30use unsynn::*;
31
32/// Constructs a braid
33///
34/// Any attributes assigned to the the struct will be applied to both the owned
35/// and borrowed types, except for doc-comments, with will only be applied to the
36/// owned form.
37///
38/// Available options:
39/// * `ref_name = "RefName"`
40/// * Sets the name of the borrowed type
41/// * `ref_doc = "Alternate doc comment"`
42/// * Overrides the default doc comment for the borrowed type
43/// * `ref_attr = "#[derive(...)]"`
44/// * Provides an attribute to be placed only on the borrowed type
45/// * `owned_attr = "#[derive(...)]"`
46/// * Provides an attribute to be placed only on the owned type
47/// * either `validator [ = "Type" ]` or `normalizer [ = "Type" ]`
48/// * Indicates the type is validated or normalized. If not specified, it is assumed that the
49/// braid implements the relevant trait itself.
50/// * `clone = "impl|omit"` (default: `impl`)
51/// * Changes the automatic derivation of a `Clone` implementation on the owned type.
52/// * `debug = "impl|owned|omit"` (default `impl`)
53/// * Changes how automatic implementations of the `Debug` trait are provided. If `owned`, then
54/// the owned type will generate a `Debug` implementation that will just delegate to the
55/// borrowed implementation. If `omit`, then no implementations of `Debug` will be provided.
56/// * `display = "impl|owned|omit"` (default `impl`)
57/// * Changes how automatic implementations of the `Display` trait are provided. If `owned`, then
58/// the owned type will generate a `Display` implementation that will just delegate to the
59/// borrowed implementation. If `omit`, then no implementations of `Display` will be provided.
60/// * `ord = "impl|owned|omit"` (default `impl`)
61/// * Changes how automatic implementations of the `PartialOrd` and `Ord` traits are provided. If
62/// `owned`, then the owned type will generate implementations that will just delegate to the
63/// borrowed implementations. If `omit`, then no implementations will be provided.
64/// * `serde = "impl|omit"` (default `omit`)
65/// * Adds serialize and deserialize implementations
66/// * `no_expose`
67/// * Functions that expose the internal field type will not be exposed publicly.
68/// * `no_std`
69/// * Generates `no_std`-compatible braid (still requires `alloc`)
70#[proc_macro_attribute]
71pub fn braid(args: TokenStream, input: TokenStream) -> TokenStream {
72 let args_ts: TokenStream2 = args.into();
73 let input_ts: TokenStream2 = input.into();
74
75 let mut args_iter = args_ts.to_token_iter();
76 let parsed_args = match args_iter.parse::<attr_grammar::AttrArgs>() {
77 Ok(args) => args,
78 Err(e) => return compile_error(format!("failed to parse braid args: {e}")).into(),
79 };
80 let args = match Params::from_args(parsed_args) {
81 Ok(args) => args,
82 Err(e) => return compile_error(format!("failed to process braid args: {e}")).into(),
83 };
84
85 let mut input_iter = input_ts.to_token_iter();
86 let body = match input_iter.parse::<ItemStruct>() {
87 Ok(body) => body,
88 Err(e) => return compile_error(format!("failed to parse struct: {e}")).into(),
89 };
90
91 args.build(body).map_or_else(
92 |e| compile_error(e).into(),
93 |codegen| codegen.generate().into(),
94 )
95}
96
97/// Constructs a ref-only braid
98///
99/// Available options:
100/// * either `validator [ = "Type" ]`
101/// * Indicates the type is validated. If not specified, it is assumed that the braid implements
102/// the relevant trait itself.
103/// * `debug = "impl|omit"` (default `impl`)
104/// * Changes how automatic implementations of the `Debug` trait are provided. If `omit`, then no
105/// implementations of `Debug` will be provided.
106/// * `display = "impl|omit"` (default `impl`)
107/// * Changes how automatic implementations of the `Display` trait are provided. If `omit`, then
108/// no implementations of `Display` will be provided.
109/// * `ord = "impl|omit"` (default `impl`)
110/// * Changes how automatic implementations of the `PartialOrd` and `Ord` traits are provided. If
111/// `omit`, then no implementations will be provided.
112/// * `serde = "impl|omit"` (default `omit`)
113/// * Adds serialize and deserialize implementations
114/// * `no_std`
115/// * Generates a `no_std`-compatible braid that doesn't require `alloc`
116#[proc_macro_attribute]
117pub fn braid_ref(args: TokenStream, input: TokenStream) -> TokenStream {
118 let args_ts: TokenStream2 = args.into();
119 let input_ts: TokenStream2 = input.into();
120
121 let mut args_iter = args_ts.to_token_iter();
122 let parsed_args = match args_iter.parse::<attr_grammar::AttrArgs>() {
123 Ok(args) => args,
124 Err(e) => return compile_error(format!("failed to parse braid_ref args: {e}")).into(),
125 };
126 let args = match ParamsRef::from_args(parsed_args) {
127 Ok(args) => args,
128 Err(e) => return compile_error(format!("failed to process braid_ref args: {e}")).into(),
129 };
130
131 let mut input_iter = input_ts.to_token_iter();
132 let mut body = match input_iter.parse::<ItemStruct>() {
133 Ok(body) => body,
134 Err(e) => return compile_error(format!("failed to parse struct: {e}")).into(),
135 };
136
137 args.build(&mut body)
138 .map_or_else(|e| compile_error(e).into(), |tokens| tokens.into())
139}
140
141/// Helper to create a compile error.
142fn compile_error(msg: impl std::fmt::Display) -> TokenStream2 {
143 let msg = msg.to_string();
144 quote::quote! {
145 compile_error!(#msg)
146 }
147}
148
149fn as_validator(validator: &grammar::Type) -> proc_macro2::TokenStream {
150 let ty = validator.to_token_stream();
151 quote::quote! { <#ty as ::strid::Validator> }
152}
153
154fn as_normalizer(normalizer: &grammar::Type) -> proc_macro2::TokenStream {
155 let ty = normalizer.to_token_stream();
156 quote::quote! { <#ty as ::strid::Normalizer> }
157}