degeneric_macros/lib.rs
1//! [](https://github.com/tomsik68/degeneric-macros/blob/master/LICENSE)
2//! [](https://github.com/tomsik68/degeneric-macros/actions/workflows/rust.yml)
3//! [](https://crates.io/crates/degeneric-macros)
4//! [](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}