odem_rs_sync/control/imp.rs
1//! Utility crate that exposes traits with identically named trait methods that
2//! are used to detect whether expressions implement [Expr] or [Publisher] and
3//! provide reasonable default behavior if they don't.
4
5use super::Expr;
6use crate::Publisher;
7
8use core::pin::Pin;
9
10/* ******************************************************** Control Terminals */
11
12/// Fallback trait that is implemented for *all* reference-types to be imported
13/// via `use` and selected when the instance a trait method is called on
14/// *doesn't* implement the [Publisher]- or [Expr]-traits respectively.
15///
16/// The mechanism affords us a certain optionality when it comes to implementing
17/// those two traits for custom types by automatically wrapping instances into
18/// wrapper types ([DefaultPub] and [DefaultExpr]) that provide reasonable
19/// default implementations for the `Publisher` and `Expr` traits.
20///
21/// If neither of the two traits is implemented, it is considered an error
22/// and the compilation will fail during type-checking.
23///
24/// This implementation can be seen as an application of the *specialization
25/// in stable Rust*-trick by **David Tolnay** and described [here].
26///
27/// [here]: https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md
28pub trait DefaultTerm: core::ops::Deref {
29 /// Dereferences the expression once.
30 ///
31 /// This method is needed because we cannot import `core::ops::Deref`
32 /// without a block, and blocks don't extend the lifetime of the last
33 /// expression since the 2024 edition.
34 fn deref_term(&self) -> &Self::Target {
35 self
36 }
37
38 /// Trait method that is selected if the instance in question *doesn't*
39 /// implement the [Expr]-trait.
40 fn expr_term(&self) -> &DefaultExpr<Self::Target>;
41
42 /// Trait method that is selected if the instance in question *doesn't*
43 /// implement the [Publisher]-trait.
44 fn pub_term(&self) -> &DefaultPub<Self::Target>;
45
46 /// Trait method that is selected if the instance in question implements
47 /// the [Expr]- or the [Publisher]-trait.
48 fn expr_or_publisher(&self) -> &Self::Target {
49 self
50 }
51}
52
53/// Trait declaration that is competing with the [DefaultTerm]-trait and is
54/// *only* implemented for types that implement the [Publisher]-trait.
55///
56/// Since the compiler prefers copying before auto-referencing, the
57/// [Self::pub_term]-method will always have precedence if it is in scope.
58pub trait PubTerm {
59 /// Trait method that is selected if the instance in question implements
60 /// the [Publisher]-trait.
61 fn pub_term(&self) -> &Self {
62 self
63 }
64}
65
66/// Trait declaration that is competing with the [DefaultTerm]-trait and is
67/// *only* implemented for types that implement the [Expr]-trait.
68///
69/// Since the compiler prefers copying before auto-referencing, the
70/// [Self::expr_term]-method will always have precedence if it is in scope.
71pub trait ExprTerm {
72 /// Trait method that is selected if the instance in question implements
73 /// the [Expr]-trait.
74 fn expr_term(&self) -> &Self {
75 self
76 }
77}
78
79/// A wrapper for type-instances that don't implement [Expr] natively.
80#[derive(Copy, Clone)]
81#[repr(transparent)]
82pub struct DefaultExpr<T: ?Sized>(pub T);
83
84// implement Expr by dereferencing to the inner T
85impl<'s, T> Expr for &'s DefaultExpr<T> {
86 type Output = &'s T;
87
88 fn get(&self) -> Self::Output {
89 &self.0
90 }
91}
92
93// delegate to the inner Publisher when appropriate
94impl<T: Publisher> Publisher for DefaultExpr<T> {
95 type Link = T::Link;
96
97 unsafe fn subscribe(&self, link: Pin<&Self::Link>) {
98 unsafe {
99 self.0.subscribe(link);
100 }
101 }
102
103 unsafe fn unsubscribe(&self, link: Pin<&Self::Link>) {
104 unsafe {
105 self.0.unsubscribe(link);
106 }
107 }
108}
109
110/// A wrapper for type-instances that don't implement [Publisher] natively.
111///
112/// This structure provides an implementation of the `Publisher`-trait that
113/// doesn't subscribe to anything and is therefore not alerted when the inner
114/// value changes.
115#[derive(Copy, Clone)]
116#[repr(transparent)]
117pub struct DefaultPub<T: ?Sized>(pub T);
118
119impl<T> DefaultPub<DefaultExpr<T>> {
120 /// Inherent method that is selected if the type-instance didn't implement
121 /// neither [Publisher] nor [Expr].
122 ///
123 /// The returned type doesn't implement `Expr` and cannot be used to access
124 /// the inner instance, leading to compile-time type errors.
125 pub const fn expr_or_publisher(&self) -> DefaultPub<T> {
126 unimplemented!()
127 }
128}
129
130// implement Publisher trivially
131impl<T> Publisher for DefaultPub<T> {
132 type Link = crate::Tuple<()>;
133
134 unsafe fn subscribe(&self, _link: Pin<&Self::Link>) {}
135
136 unsafe fn unsubscribe(&self, _link: Pin<&Self::Link>) {}
137}
138
139// delegate to the inner Expr when appropriate
140impl<T: Expr> Expr for DefaultPub<T> {
141 type Output = T::Output;
142
143 fn get(&self) -> Self::Output {
144 self.0.get()
145 }
146}
147
148/* **************************************************** Trait Implementations */
149
150// blanket implementation for all reference types to fall back to
151impl<T: core::ops::Deref> DefaultTerm for T {
152 fn expr_term(&self) -> &DefaultExpr<T::Target> {
153 // SAFETY: validity of this cast is guaranteed by #[repr(transparent)]
154 unsafe { &*((&**self) as *const T::Target as *const DefaultExpr<T::Target>) }
155 }
156
157 fn pub_term(&self) -> &DefaultPub<T::Target> {
158 // SAFETY: validity of this cast is guaranteed by #[repr(transparent)]
159 unsafe { &*((&**self) as *const T::Target as *const DefaultPub<T::Target>) }
160 }
161}
162
163// blanket implements PubTerm for all Publisher types
164impl<T: Publisher> PubTerm for T {}
165
166// blanket implements ExprTerm for all Expr types
167impl<T: Expr> ExprTerm for T {}