1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164
//! # Quick Start
//!
//! ```
//! use degeneric_macros::{Degeneric};
//! use std::marker::PhantomData;
//!
//! use trait_set::trait_set;
//! use typed_builder::TypedBuilder;
//!
//! trait_set!(trait FactoryFn<T> = 'static + Send + Sync + Fn() -> T);
//!
//! #[derive(Degeneric, TypedBuilder)]
//! struct Container<T: Default, A: FactoryFn<T>, B> {
//! a: A,
//! b: B,
//! c: u32,
//! #[builder(default)]
//! _t: PhantomData<T>,
//! }
//!
//! fn my_fact() -> String {
//! format!("hello world!")
//! }
//!
//! let c = Container::builder().a(my_fact).b(true).c(20).build();
//! do_something(&c);
//! access_inner_types(&c);
//!
//! fn do_something(c: &impl ContainerTrait) {}
//! fn access_inner_types<C: ContainerTrait>(c: &C) {
//! let same_as_a: C::A;
//! }
//! ```
//!
//! # Elevator pitch
//!
//! ## The problem
//!
//! Degeneric is a utility library that solves the common problem of having too many generics.
//! Let's say we want to construct a dependency container like this:
//! ```
//! struct Container<Logger, HttpClient> {
//! logger: Logger,
//! client: HttpClient,
//! // ...and so on...
//! }
//!
//! let container = Container {
//! logger: String::from("logger"),
//! client: String::from("http"),
//! };
//!
//! accepts_container(container);
//! // now to consume such a container, one needs to write the function like this:
//! fn accepts_container<Logger, HttpClient>(c: Container<Logger, HttpClient>) {}
//! ```
//!
//! This creates a problem of ever growing list of generics in all functions that touch the
//! container and pollutes APIs with unnecessary generics.
//!
//! ## Degeneric solution
//!
//! Degeneric proposes solution to this problem by creating a trait and stuffing all of the generic
//! types into the trait as associated types. Instead of the pattern above, you'll end up with
//! this:
//! ```
//! use degeneric_macros::Degeneric;
//!
//! #[derive(Degeneric)]
//! struct Container<Logger, HttpClient> {
//! logger: Logger,
//! client: HttpClient,
//! }
//!
//! let c = Container {
//! logger: String::from("logger"),
//! client: String::from("http"),
//! };
//!
//! accepts_container(c);
//! fn accepts_container(c: impl ContainerTrait) {}
//! ```
//!
//! How is this different, you ask? Instead of accepting a whole lot of generic arguments, I can now write
//! the function without even using angular brackets and I think that's beautiful.
//! What is even more beautiful is that you can add more generics without having to modify the
//! signature of `accepts_container`.
//!
//! # Degeneric understands lifetimes
//!
//! ```
//! use std::borrow::Cow;
//! use std::fmt::Debug;
//!
//! use degeneric_macros::{Degeneric};
//! use typed_builder::TypedBuilder;
//!
//! #[derive(Degeneric, TypedBuilder)]
//! struct Container<'a, T: 'a + PartialEq<i32> + Debug> {
//! cow: &'a Cow<'a, str>,
//! reference: &'a T,
//! }
//!
//! let cow = Cow::Owned(String::from("hello lifetimes"));
//! {
//! let reference = 42;
//! let c = Container::builder().cow(&cow).reference(&reference).build();
//!
//! fn accept_container<'a>(cont: &impl ContainerTrait<'a>) {
//! assert_eq!(cont.cow().as_ref(), "hello lifetimes");
//! assert_eq!(*cont.reference(), &42_i32);
//! }
//!
//! accept_container(&c);
//! }
//! ```
//!
//! # Crates degeneric plays nice with
//!
//! + [trait-set](https://lib.rs/trait-set) - shorten and DRY up trait bounds
//! + [typed-builder](https://lib.rs/typed-builder) - generate a builder for your trait
//! + [easy-ext](https://lib.rs/easy-ext) - extend your trait with more methods
use proc_macro::TokenStream;
use syn::parse_macro_input;
use syn::ItemStruct;
mod degeneric;
mod type_tools;
#[proc_macro_derive(Degeneric)]
/// Usable only on structs.
///
/// Example:
/// ```
/// use degeneric_macros::Degeneric;
///
/// use std::cmp::PartialEq;
/// use std::fmt::Debug;
///
/// #[derive(Degeneric)]
/// struct Container<A: PartialEq<i32> + Debug, B: PartialEq<bool> + Debug> {
/// a: A,
/// b: B,
/// c: u32,
/// }
///
/// let c = Container {
/// a: 10,
/// b: true,
/// c: 42,
/// };
///
/// test_container(c);
/// fn test_container(c: impl ContainerTrait) {
/// assert_eq!(c.a(), &10);
/// assert_eq!(c.b(), &true);
/// assert_eq!(c.c(), &42);
/// }
/// ```
pub fn degeneric(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as ItemStruct);
let tokens = degeneric::process_struct(&input);
TokenStream::from(tokens)
}