degeneric_macros/
lib.rs

1//! [![GitHub license](https://img.shields.io/github/license/tomsik68/degeneric-macros?style=for-the-badge)](https://github.com/tomsik68/degeneric-macros/blob/master/LICENSE)
2//! [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/tomsik68/degeneric-macros/rust.yml?branch=master&style=for-the-badge)](https://github.com/tomsik68/degeneric-macros/actions/workflows/rust.yml)
3//! [![Crates.io](https://img.shields.io/crates/v/degeneric-macros?style=for-the-badge)](https://crates.io/crates/degeneric-macros)
4//! [![Crates.io (latest)](https://img.shields.io/crates/dv/degeneric-macros?style=for-the-badge)](https://crates.io/crates/degeneric-macros)
5//!
6//! # Quick Start
7//!
8//! ```
9//! use degeneric_macros::{Degeneric};
10//! use std::marker::PhantomData;
11//!
12//! use trait_set::trait_set;
13//! use typed_builder::TypedBuilder;
14//!
15//! trait_set!(trait FactoryFn<T> = 'static + Send + Sync + Fn() -> T);
16//!
17//! #[derive(Degeneric, TypedBuilder)]
18//! #[degeneric(trait_decl = "pub trait ContainerTrait")]
19//! /// This is doc for ContainerTrait!
20//! struct Container<T: Default, A: FactoryFn<T>, B> {
21//!     a: A,
22//!     b: B,
23//!     c: u32,
24//!     #[builder(default)]
25//!     _t: PhantomData<T>,
26//! }
27//!
28//! fn my_fact() -> String {
29//!     format!("hello world!")
30//! }
31//!
32//! let c = Container::builder().a(my_fact).b(true).c(20).build();
33//! do_something(&c);
34//! access_inner_types(&c);
35//!
36//! fn do_something(c: &impl ContainerTrait) {}
37//! fn access_inner_types<C: ContainerTrait>(c: &C) {
38//!     let same_as_a: C::A;
39//! }
40//! ```
41//!
42//! # Elevator pitch
43//!
44//! ## The problem
45//!
46//! Degeneric is a utility library that solves the common problem of having too many generics.
47//! Let's say we want to construct a dependency container like this:
48//! ```
49//! struct Container<Logger, HttpClient> {
50//!     logger: Logger,
51//!     client: HttpClient,
52//!     // ...and so on...
53//! }
54//!
55//! let container = Container {
56//!     logger: String::from("logger"),
57//!     client: String::from("http"),
58//! };
59//!
60//! accepts_container(container);
61//! // now to consume such a container, one needs to write the function like this:
62//! fn accepts_container<Logger, HttpClient>(c: Container<Logger, HttpClient>) {}
63//! ```
64//!
65//! This creates a problem of ever growing list of generics in all functions that touch the
66//! container and pollutes APIs with unnecessary generics.
67//!
68//! ## Degeneric solution
69//!
70//! Degeneric proposes solution to this problem by creating a trait and stuffing all of the generic
71//! types into the trait as associated types. Instead of the pattern above, you'll end up with
72//! this:
73//! ```
74//! use degeneric_macros::Degeneric;
75//!
76//! #[derive(Degeneric)]
77//! #[degeneric(trait_decl = "pub trait ContainerTrait")]
78//! struct Container<Logger, HttpClient> {
79//!     logger: Logger,
80//!     client: HttpClient,
81//! }
82//!
83//! let c = Container {
84//!     logger: String::from("logger"),
85//!     client: String::from("http"),
86//! };
87//!
88//! accepts_container(c);
89//! fn accepts_container(c: impl ContainerTrait) {}
90//! ```
91//!
92//! How is this different, you ask? Instead of accepting a whole lot of generic arguments, I can now write
93//! the function without even using angular brackets and I think that's beautiful.
94//! What is even more beautiful is that you can add more generics without having to modify the
95//! signature of `accepts_container`.
96//!
97//! # Degeneric understands lifetimes
98//!
99//! ```
100//! use std::borrow::Cow;
101//! use std::fmt::Debug;
102//!
103//! use degeneric_macros::{Degeneric};
104//! use typed_builder::TypedBuilder;
105//!
106//! #[derive(Degeneric, TypedBuilder)]
107//! #[degeneric(trait_decl = "trait ContainerTrait")]
108//! struct Container<'a, T: 'a + PartialEq<i32> + Debug> {
109//!     cow: &'a Cow<'a, str>,
110//!     reference: &'a T,
111//! }
112//!
113//! let cow = Cow::Owned(String::from("hello lifetimes"));
114//! {
115//!     let reference = 42;
116//!     let c = Container::builder().cow(&cow).reference(&reference).build();
117//!
118//!     fn accept_container<'a>(cont: &impl ContainerTrait<'a>) {
119//!         assert_eq!(cont.cow().as_ref(), "hello lifetimes");
120//!         assert_eq!(cont.reference(), &42_i32);
121//!     }
122//!
123//!     accept_container(&c);
124//! }
125//! ```
126//!
127//! ## Degeneric can be used with galemu!
128//!
129//! If you're into hiding generics, you'll be surprised that the [galemu](https://crates.io/crates/galemu) crate makes it possible to
130//! hide even lifetimes!
131//!
132//! The way this example works is, that your Container contains an impl GCon. This object is able
133//! to produce [`galemu::Bound`]`<GCon::Transaction>`.
134//!
135//! The particular implementation of `GTran` is provided by [`galemu::create_gal_wrapper_type`].
136//! One must manually implement GTran on it.
137//!
138//! In principle, galemu lifts the lifetime of `Transaction<'a>` into the [`galemu::BoundExt`] trait.
139//! The lifetime inference happens in `Connection::transaction`. At that point, it's apparent that
140//! the connection's lifetime is passed to Transaction.
141//!
142//! ```
143//! use std::fmt::Debug;
144//! use std::borrow::Cow;
145//! use std::ops::Deref;
146//!
147//! use degeneric_macros::Degeneric;
148//!
149//! use galemu::{Bound, BoundExt, create_gal_wrapper_type};
150//!
151//! // begin galemu
152//!
153//! struct Connection {
154//!     count: usize
155//! }
156//!
157//! struct Transaction<'conn> {
158//!     conn: &'conn mut Connection
159//! }
160//!
161//! impl Connection {
162//!     fn transaction(&mut self) -> Transaction {
163//!         Transaction { conn: self }
164//!     }
165//! }
166//!
167//! trait GCon {
168//!     type Transaction: GTran;
169//!
170//!     fn create_transaction(&mut self) -> Bound<Self::Transaction>;
171//! }
172//!
173//! trait GTran: for<'s> BoundExt<'s> {
174//!     fn commit<'s>(me: Bound<'s, Self>);
175//!     fn abort<'s>(me: Bound<'s, Self>);
176//! }
177//!
178//! create_gal_wrapper_type!{ struct TransWrap(Transaction<'a>); }
179//!
180//! impl GCon for Connection {
181//!     type Transaction = TransWrap;
182//!
183//!     fn create_transaction(&mut self) -> Bound<Self::Transaction> {
184//!         let transaction = self.transaction();
185//!         TransWrap::new(transaction)
186//!     }
187//! }
188//!
189//! impl GTran for TransWrap {
190//!     fn commit<'s>(me: Bound<'s, Self>) {
191//!         let trans = TransWrap::into_inner(me);
192//!         trans.conn.count += 10;
193//!     }
194//!
195//!     fn abort<'s>(me: Bound<'s, Self>) {
196//!         let trans = TransWrap::into_inner(me);
197//!         trans.conn.count += 3;
198//!     }
199//! }
200//!
201//! // end galemu
202//!
203//! #[derive(Degeneric)]
204//! #[degeneric(trait_decl = "pub trait ContainerTrait")]
205//! struct Container<T: GCon> {
206//!     conn: T,
207//! }
208//!
209//! let conn = Connection { count : 0 };
210//!
211//! let cont = Container {
212//!     conn,
213//! };
214//!
215//! fn commit_transaction(mut c: impl ContainerTrait) {
216//!     let conn = c.conn_mut();
217//!     let tran = conn.create_transaction();
218//!     GTran::commit(tran);
219//! }
220//!
221//! commit_transaction(cont);
222//! ```
223//!
224//! ## Degeneric + dynamize
225//!
226//! Degeneric supports dynamizing the generated trait. How does that work?
227//!
228//! Here's a minimal example on how to dynamize the generated trait:
229//!
230//! ```
231//! use degeneric_macros::Degeneric;
232//!
233//! #[derive(Degeneric)]
234//! #[degeneric(dynamize, trait_decl = "pub trait GeneratedContainerTrait")]
235//! struct Container<T: std::any::Any> {
236//!     item: T,
237//! }
238//! ```
239//!
240//! By convention, dynamize generates a `DynGeneratedContainerTrait` where the types are boxed.
241//! Please refer to [dynamize documentation](https://docs.rs/dynamize/latest/dynamize/#dynamize)
242//! for more information.
243//!
244//! ## Degeneric + haz
245//!
246//! Degeneric is able to serve as a derive macro for the excellent
247//! [`haz`](https://crates.io/crates/haz) crate.
248//!
249//! ```
250//! use degeneric_macros::Degeneric;
251//! use haz::Has;
252//!
253//! # #[derive(Default)]
254//! # struct Host;
255//! # #[derive(Default)]
256//! # struct Port;
257//! # #[derive(Default)]
258//! # struct Verbosity;
259//! # #[derive(Default)]
260//! # struct Restriction;
261//!
262//!  #[derive(Degeneric, Default)]
263//!  #[degeneric(haz)]
264//!  struct Config {
265//!    host: Host,
266//!    port: Port,
267//!    verbosity: Verbosity,
268//!    restriction: Restriction,
269//!  }
270//!
271//!  fn assert_has_all_the_things<T: Has<Host> + Has<Port> + Has<Verbosity> + Has<Restriction>>(_: T) {}
272//!  assert_has_all_the_things(Config::default());
273//! ```
274//!
275//! # Degeneric understands where clause
276//!
277//! ```
278//! use degeneric_macros::{Degeneric};
279//! use std::fmt::Debug;
280//!
281//! #[derive(Degeneric)]
282//! #[degeneric(trait_decl = "pub trait ContainerTrait")]
283//! struct Container<T> where T: Default + Debug + PartialEq {
284//!     item: T,
285//! }
286//!
287//! let c = Container {
288//!     item: vec![""],
289//! };
290//!
291//! fn construct_default_value<C: ContainerTrait>(c: C) {
292//!     let v: C::T = Default::default();
293//!     assert_eq!(v, Default::default());
294//! }
295//!
296//! construct_default_value(c);
297//!
298//!
299//! ```
300//!
301//! # Generate getters only for some fields
302//!
303//! The `no_getter` attribute can be used to skip generating a getter.
304//!
305//! ```compile_fail
306//! use degeneric_macros::{Degeneric};
307//!
308//! #[derive(Degeneric)]
309//! #[degeneric(trait_decl = "pub(crate) trait Something")]
310//! struct Container<'a, T: 'a, S: 'a> {
311//!     item: &'a T,
312//!     item2: S,
313//!     #[degeneric(no_getter)]
314//!     dt: PhantomData<S>,
315//! }
316//!
317//! let c = Container {
318//!     item: "hello",
319//!     item2: format!("this won't have getter!"),
320//!     dt: PhantomData<S>,
321//! };
322//!
323//! fn accept_container<C: Something>(c: C) {
324//!     /// ERROR: dt doesn't have a getter!
325//!     assert_eq!(c.dt(), format!("this won't have getter!"));
326//! }
327//!
328//! accept_container(c);
329//! ```
330//!
331//! # Degeneric figures out mutability
332//!
333//! Some fields may have mutable getters, some not. Degeneric recognizes immutable pointers and
334//! references and skips generating mutable getter for them.
335//!
336//! ```
337//! use degeneric_macros::{Degeneric};
338//! #[derive(Degeneric)]
339//! #[degeneric(trait_decl = "pub(crate) trait Something")]
340//! struct Container<'a, T: 'a> {
341//!     x: &'a T,
342//!     y: T,
343//! }
344//!
345//! let mut c = Container {
346//!     x: &(),
347//!     y: (),
348//! };
349//!
350//! fn accept_container<'a>(mut c: impl Something<'a>) {
351//!     // OK
352//!     c.x();
353//!     c.y();
354//!     c.y_mut();
355//! }
356//!
357//! accept_container(c);
358//! ```
359//!
360//! ```compile_fail
361//! use degeneric_macros::{Degeneric};
362//!
363//! #[derive(Degeneric)]
364//! #[degeneric(trait_decl = "pub(crate) trait Something")]
365//! struct Container<'a, T> {
366//!     x: &'a T,
367//! }
368//!
369//! let c = Container {
370//!     x: &(),
371//! };
372//!
373//! fn accept_container<'a>(c: impl Something<'a>) {
374//!     // ERROR: x is a reference which can't be made mut
375//!     c.x_mut();
376//! }
377//! ```
378//!
379//! # Add attributes everywhere!
380//!
381//! For some attributes, you can just add them on the field and they'll be forwarded to all getters automatically.
382//! Here's a list of such attributes:
383//! - `#[allow]`
384//! - `#[doc]`
385//! - `#[cfg(...)]`
386//! - `#[cfg_attr(...)]`
387//!
388//! If you need more granularity, you can add attributes only on:
389//!
390//! - Trait declaration: `#[degeneric(trait_decl_attr = "#[doc = \"Trait declaration\"]")]`
391//! - Trait impl block: `#[degeneric(trait_impl_attr = "#[doc = \"Trait implementation\"]")]`
392//! - Field immutable getter implementation: `#[degeneric(getter_impl_attr = "#[doc = \"Getter implementation\"])]`
393//! - Field mutable getter declaration: `#[degeneric(mut_getter_decl_attr = "#[doc = \"Mutable Getter declaration\"])]`
394//!
395//! ```compile_fail
396//! use degeneric_macros::Degeneric;
397//!
398//! #[derive(Degeneric)]
399//! #[degeneric(trait_decl = "pub(crate) trait Something")]
400//! #[degeneric(trait_decl_impl_attr = "#[cfg(foo)]")]
401//! /// This is documentation for the `Something` trait
402//! struct Container<T> {
403//!     x: T,
404//! }
405//!
406//! // this will error because the Something trait exists only in the foo configuration
407//! #[cfg(not(foo))]
408//! fn accept_container(c: impl Something) {}
409//! ```
410//!
411//! # Crates degeneric plays nice with
412//!
413//! + [galemu](https://lib.rs/galemu) - hide lifetimes!
414//! + [trait-set](https://lib.rs/trait-set) - shorten and DRY up trait bounds
415//! + [typed-builder](https://lib.rs/typed-builder) - generate a builder for your trait
416//! + [easy-ext](https://lib.rs/easy-ext) - extend your trait with more methods
417//!
418//! # CloneExt
419//!
420//! Apart from solving the dependency injection problem, degeneric also helps with cloning.
421//! There might be a situation where you're holding a non-cloneable type inside another type. In
422//! these situations, it might be possible to clone the value by different means.
423//!
424//! Failing example:
425//!
426//! ```compile_fail
427//!
428//! #[derive(Default)]
429//! struct NonClone;
430//!
431//! #[derive(Clone)]
432//! struct Container {
433//!     nc: PhantomData<NonClone>,
434//! }
435//! ```
436//!
437//! In such situations, one can resort to degeneric's CloneExt derive macro. Currently, it
438//! offers a single attribute to adjust the way fields are cloned:
439//!
440//! ```
441//! #[derive(Default)]
442//! struct NonClone;
443//!
444//! #[derive(Default, degeneric_macros::CloneExt)]
445//! struct Container {
446//!     #[clone_ext(clone_behavior(call_function="Default::default"))]
447//!     nc: NonClone,
448//! }
449//!
450//! Container::default().clone();
451//! ```
452
453/// proc_macro_error unwrap
454macro_rules! pme_unwrap {
455    ($e:expr, $span:expr, $($args:expr),*) => {
456        {
457            match $e {
458                Ok(x) => x,
459                #[allow(unused)]
460                Err(err) => {
461                    proc_macro_error::abort!($span, $($args),*);
462                }
463            }
464        }
465    };
466}
467
468mod clone_ext;
469mod degeneric;
470
471use proc_macro::TokenStream;
472use proc_macro_error::proc_macro_error;
473use syn::parse_macro_input;
474
475#[proc_macro_derive(Degeneric, attributes(degeneric))]
476#[proc_macro_error]
477/// Usable only on structs.
478///
479/// Example:
480/// ```
481/// use degeneric_macros::Degeneric;
482///
483/// use std::cmp::PartialEq;
484/// use std::fmt::Debug;
485///
486/// #[derive(Degeneric)]
487/// #[degeneric(trait_decl = "trait ContainerTrait")]
488/// // attribute for both trait declaration and trait impl
489/// #[degeneric(trait_impl_attr = "#[cfg(not(foo))]")]
490/// /// ContainerTrait contains the implementation of `A` and `B` types.
491/// struct Container<
492///     /** The A type is the more important one. */ A: PartialEq<i32> + Debug,
493///     /** You could live without B I guess. */ B: PartialEq<bool> + Debug> {
494///     a: A,
495///     b: B,
496///     c: u32,
497/// }
498///
499/// let c = Container {
500///     a: 10,
501///     b: true,
502///     c: 42,
503/// };
504///
505/// test_container(c);
506/// fn test_container(c: impl ContainerTrait) {
507///     assert_eq!(c.a(), &10);
508///     assert_eq!(c.b(), &true);
509///     assert_eq!(c.c(), &42);
510/// }
511/// ```
512pub fn degeneric(input: TokenStream) -> TokenStream {
513    let input = parse_macro_input!(input);
514    let tokens =
515        self::degeneric::process_struct(&input).unwrap_or_else(|err| err.to_compile_error());
516    TokenStream::from(tokens)
517}
518
519#[proc_macro_derive(CloneExt, attributes(clone_ext))]
520#[proc_macro_error]
521/// There might be a situation where you're holding a non-cloneable type inside another type. In
522/// these situations, it might be possible to clone the value by different means.
523///
524/// Failing example:
525///
526/// ```compile_fail
527///
528/// #[derive(Default)]
529/// struct NonClone;
530///
531/// #[derive(Clone)]
532/// struct Container {
533///     nc: PhantomData<NonClone>,
534/// }
535/// ```
536///
537/// In such situations, one can resort to degeneric's CloneExt derive macro. Currently, it
538/// offers a single attribute to adjust the way fields are cloned:
539///
540/// ```
541/// #[derive(Default)]
542/// struct NonClone;
543///
544/// #[derive(Default, degeneric_macros::CloneExt)]
545/// struct Container {
546///     #[clone_ext(clone_behavior(call_function="Default::default"))]
547///     nc: NonClone,
548/// }
549///
550/// Container::default().clone();
551/// ```
552pub fn clone_ext(input: TokenStream) -> TokenStream {
553    let input = parse_macro_input!(input);
554    let tokens =
555        self::clone_ext::process_struct(&input).unwrap_or_else(|err| err.to_compile_error());
556    TokenStream::from(tokens)
557}