ct_utils/ct_if.rs
1//! This module defines a types to encode an IF test within the type system.
2//!
3//! The idea behind this concept is to match the condition, which is a type,
4//! with the structure the test is implemented for.
5//! This allows for a basic test similar to the following
6//! ```rust
7//! let result = std::any::TypeId::of::<i32>() == std::any::TypeId::of::<i32>();
8//! if result {
9//! // Condition matched
10//! } else {
11//! // Condition failed to match
12//! }
13//! ```
14//!
15//! # Example
16//! ```rust,no_run
17//! # extern crate ct_utils;
18//! use ct_utils::prelude::*;
19//!
20//! // Trait to negate the signed-ness of a specific integer.
21//! trait DeSignature<Target> {
22//! type Result: ?Sized;
23//! }
24//!
25//! impl<Target> DeSignature<Target> for i32 {
26//! // Depending on Target, CTIf::Path will become signed or unsigned.
27//! // Result will receive the type contained by CTIf::Path.
28//! type Result = <IfCheck<i32> as CTIf<Target, u32, i32>>::Path;
29//! }
30//! ```
31//!
32//! # Notes
33//! - If the receiving type parameter is constrained, the generic [`CTIf`] causes
34//! compilation errors due to missing constraints.
35//! You have to create your own specialized implementation of [`CTIf`], which is
36//! made easy by utilizing the macro [`ctif_specialize`].
37//!
38
39use std::marker::PhantomData;
40
41/// Represents a boolean value encoded within the typesystem.
42///
43/// See also [`CTFalse`] and [`CTTrue`].
44pub trait CTBool {
45 /// Returns a boolean value corresponding to the implemented type.
46 fn to_bool() -> bool;
47}
48
49/// Generic version of an IF test encoded within the type system.
50///
51/// Condition is the type which must match.
52/// OptionTrue will be passed into [`CTIf::Path`] if the condition match is TRUE.
53/// OptionFalse will be passed into [`CTIf::Path`] if the condition match is FALSE.
54///
55/// Specialized versions of this trait are necessary when the receiving type parameter
56/// is constrained. See [`ctif_specialize`] to create such a specialized version.
57pub trait CTIf<Condition, OptionTrue, OptionFalse>
58where
59 OptionTrue: 'static + ?Sized,
60 OptionFalse: 'static + ?Sized,
61{
62 /// Type representing how the condition on the implemented type matches.
63 type Result: CTBool;
64 /// Type which holds either of the two generic option arguments, depending on how the
65 /// condition matches.
66 type Path: 'static + ?Sized;
67}
68
69/// Type representing a FALSE value.
70pub enum CTFalse {}
71/// Type representing a TRUE value.
72pub enum CTTrue {}
73
74/// Actual type used to perform an IF check through the type system.
75///
76/// Performing an IF test through the type system requires both this structure as well
77/// as the [`CTIf`] trait (or a specialized one). The typed that needs to be matched
78/// is passed into this struct as generic argument for X.
79/// The resulting type must then be cast as [`CTIf`] trait, which includes the desired type.
80/// [`CTIf::Path`] will then hold the match relevant type.
81/// eg
82/// ```rust,ignore
83/// <IfCheck<Subject> as CTIf<Desired, TypeTrue, TypeFalse>>::Path;
84/// ```
85pub struct IfCheck<X> {
86 _marker: PhantomData<X>,
87}
88
89impl CTBool for CTFalse {
90 fn to_bool() -> bool {
91 false
92 }
93}
94
95impl CTBool for CTTrue {
96 fn to_bool() -> bool {
97 true
98 }
99}
100
101impl<X, CondFail, OptionTrue, OptionFalse> CTIf<CondFail, OptionTrue, OptionFalse> for IfCheck<X>
102where
103 OptionTrue: 'static + ?Sized,
104 OptionFalse: 'static + ?Sized,
105{
106 default type Result = CTFalse;
107 default type Path = OptionFalse;
108}
109
110impl<X, OptionTrue, OptionFalse> CTIf<X, OptionTrue, OptionFalse> for IfCheck<X>
111where
112 OptionTrue: 'static + ?Sized,
113 OptionFalse: 'static + ?Sized,
114{
115 type Result = CTTrue;
116 type Path = OptionTrue;
117}
118
119/// Macro for specializing [`CTIf`].
120///
121/// If receiving associated types are constrained, the generic [`CTIf`] is not usable any more
122/// because there is no syntax carrying over these constraints accross the IF test.
123/// Solving this requires building a new trait similar to [`CTIf`] with constraints which are
124/// specific to your use case. These constraints will be applied to [`CTIf::Path`].
125///
126/// This macro helps in building that specialization. It creates a specialized trait and implements
127/// it for [`IfCheck`], the latter can be re-used accross specializations.
128/// Implementers only need to provide a new name for the trait and the required constraints.
129/// Provided outer attributes are applied to the newly created trait as well.
130///
131/// Using this specialized trait is analogue to using [`CTIf`] and [`IfCheck`].
132///
133/// # Example
134/// ```rust,ignore
135/// ctif_specialize {
136/// /// Optional doc-attribute explaining things about the special IF test.
137/// trait_name = "MySpecialIf",
138/// conditions = [Add<u32>, Mul<u32>]
139/// }
140/// ```
141///
142/// That macro call will expand to the following code.
143/// ```rust, ignore
144/// /// Optional doc-attribute explaining things about the special IF test.
145/// pub trait MySpecialIf<Condition, OptionTrue, OptionFalse> {
146/// type Result: CTBool;
147/// type Path: Add<u32> + Mul<u32>;
148/// }
149/// ```
150#[macro_export]
151macro_rules! ctif_specialize {
152 (
153 $( #[$meta_links:meta] )*
154 trait_name = $trait:ident,
155 conditions = [$($cond:path),+]
156 ) => {
157 $( #[$meta_links] )*
158 pub trait $trait<Condition, OptionTrue, OptionFalse> {
159 /// Auto generated
160 type Result: $crate::ct_if::CTBool;
161 /// Auto generated
162 type Path: $( $cond +)+;
163 }
164
165 impl<X, CondFail, OptionTrue, OptionFalse> $trait<CondFail, OptionTrue, OptionFalse> for $crate::ct_if::IfCheck<X>
166 where
167 OptionTrue: $( $cond +)+,
168 OptionFalse: $( $cond +)+,
169 {
170 default type Result = $crate::ct_if::CTFalse;
171 default type Path = OptionFalse;
172 }
173
174 impl<X, OptionTrue, OptionFalse> $trait<X, OptionTrue, OptionFalse> for $crate::ct_if::IfCheck<X>
175 where
176 OptionTrue: $( $cond +)+,
177 OptionFalse: $( $cond +)+,
178 {
179 type Result = $crate::ct_if::CTTrue;
180 type Path = OptionTrue;
181 }
182 };
183}
184
185#[cfg(test)]
186mod test {
187 use super::*;
188 use std::any::TypeId;
189
190 fn match_on<Subject: CTBool>() -> TypeId {
191 TypeId::of::<<IfCheck<Subject> as CTIf<CTTrue, CTTrue, CTFalse>>::Path>()
192 }
193
194 #[test]
195 fn test_true() {
196 assert_eq!(match_on::<CTTrue>(), TypeId::of::<CTTrue>());
197 }
198
199 #[test]
200 fn test_false() {
201 assert_eq!(match_on::<CTFalse>(), TypeId::of::<CTFalse>());
202 }
203}