tuple_traits/contains.rs
1use std::marker::PhantomData;
2
3use crate::cons::Cons;
4
5/// Index value to indicate the target is in the left of a [`Cons`].
6pub enum Here {}
7/// Index value to indicate that the target is at some other index of a [`Cons`].
8pub struct There<T>(PhantomData<T>);
9
10/// Determine whether a target is located at some index. This trait will be implemented for all
11/// types that contain the target type.
12///
13/// Currently, there is no mechanism to retrieve the target value, only determine if it does exist
14/// or not. This allows for using zero-sized types to be used as flags within the type system.
15///
16/// If `Index` is allowed to be inferred, it's possible to check if some arbitrary value contains
17/// `Target` at any location. With very long tuples there is a chance that the type checker will
18/// raise a error due to too much recursion, however it should be fine to raise this limit if your
19/// needs require it..
20///
21/// # Examples
22///
23/// Allowing `Index` to be inferred allows for searching at any depth.
24///
25/// ```
26/// # use tuple_traits::Contains;
27/// #
28/// struct A;
29/// struct B;
30/// struct C;
31///
32/// fn requires_c<T, Index>(value: T)
33/// where
34/// T: Contains<C, Index>
35/// {
36/// }
37///
38/// requires_c((A, B, C));
39/// ```
40///
41/// The trait bound will not be satisfied for any tuples that do not contain the target value.
42///
43/// ```compile_fail
44/// # use tuple_traits::Contains;
45/// #
46/// # struct A;
47/// # struct B;
48/// # struct C;
49/// #
50/// # fn requires_c<T, Index>(value: T)
51/// # where
52/// # T: Contains<C, Index>
53/// # {
54/// # }
55/// #
56/// requires_c((A, B));
57/// ```
58///
59/// The target value can also be a generic on the function without a parameter, however this isn't
60/// recommended due to Rust requiring all generics to be listed by the caller.
61/// ```
62/// # use tuple_traits::Contains;
63/// #
64/// # struct A;
65/// # struct B;
66/// # struct C;
67/// #
68/// fn requires_c<T, Index>()
69/// where
70/// T: Contains<C, Index>
71/// {
72/// }
73///
74/// requires_c::<(A, B, C), _>();
75/// ```
76///
77/// Technique based off of [Lloyd's blog post] on type-level recursion in Rust.
78///
79/// [Lloyd's blog post]: https://beachape.com/blog/2017/03/12/gentle-intro-to-type-level-recursion-in-Rust-from-zero-to-frunk-hlist-sculpting
80#[diagnostic::on_unimplemented(
81 label = "`{Target}` must appear within here",
82 message = "`{Target}` does not appear within `{Self}`",
83 note = "ensure that `{Target}` is present on `{Self}` in order to use it here"
84)]
85pub trait Contains<Target, Index> {}
86
87/// Base implementation, where the target is on the left of a cons.
88impl<Target, C> Contains<Target, Here> for C where C: Cons<Left = Target> {}
89
90/// Recursive implementation, advancing the index to try resolve the trait again.
91impl<Tail, Target, TailIndex, C> Contains<Target, There<TailIndex>> for C
92where
93 C: Cons<Right = Tail>,
94 Tail: Contains<Target, TailIndex>,
95{
96}