Skip to main content

alux_ext/
operation.rs

1//! What a defunctionalized operation is: its signature, its application, and the handle an
2//! interpretation applies it with.
3
4use core::future::Future;
5use core::marker::PhantomData;
6
7/// Applies a defunctionalized operation to a context and an argument product.
8pub trait ApplyAlg<Context, Args> {
9    /// The value produced by applying the operation.
10    type Output;
11
12    /// Interprets the operation using the supplied context and arguments.
13    fn apply(&self, context: Context, args: Args) -> impl Future<Output = Self::Output> + Send;
14}
15
16/// Selects an owned runtime handle for a semantic context.
17pub trait HandlerContextAlg<Context: ?Sized> {
18    /// The owned carrier cloned into asynchronous operation invocations.
19    type Handle: AsRef<Context> + Clone + Send + Sync + 'static;
20}
21
22/// Describes the semantic signature of a defunctionalized operation.
23pub trait OperationAlg {
24    /// The semantic context interpreted by the operation.
25    type Context: ?Sized;
26    /// The product of arguments accepted by the operation.
27    type Args;
28
29    /// The documentation the operation was declared with, as the author wrote it.
30    ///
31    /// An interpretation that describes a surface to a reader has nowhere else to read what an
32    /// operation is for. The first line is its summary and the rest is its description, which is
33    /// what a doc comment already means.
34    const DOC: &'static str;
35
36    /// The source-level name this operation was declared under.
37    ///
38    /// An interpretation that names operations to a reader, rather than only applying them, has no
39    /// other place to read one from: a type name states where the operation lives, not what it was
40    /// called.
41    const NAME: &'static str;
42
43    /// The source-level argument names, in declaration order.
44    const ARG_NAMES: &'static [&'static str];
45}
46
47/// States an argument's type as the method writes it, known without any context.
48///
49/// A program can check a source against it where the program is declared.
50pub struct StatedArg<Arg>(PhantomData<fn() -> Arg>);
51
52impl<Arg> StatedArg<Arg> {
53    /// States the argument type `Arg`.
54    #[must_use]
55    pub const fn new() -> Self {
56        Self(PhantomData)
57    }
58}
59
60impl<Arg> Default for StatedArg<Arg> {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66/// States an argument whose type the context or a selected type decides, so only an
67/// interpretation that names them can check it.
68pub struct ContextArg;
69
70#[cfg(test)]
71mod tests {
72    use super::{ApplyAlg, OperationAlg};
73    use crate::ext;
74    use std::sync::Arc;
75
76    trait ValueAlg {
77        fn value(&self) -> u32;
78    }
79
80    struct Value(u32);
81
82    impl ValueAlg for Value {
83        fn value(&self) -> u32 {
84            self.0
85        }
86    }
87
88    #[ext(name = ValueExt, defunc)]
89    impl<This> This
90    where
91        This: ValueAlg,
92    {
93        /// Adds `increment` to the value.
94        async fn value_plus(&self, increment: u32) -> u32 {
95            self.value() + increment
96        }
97    }
98
99    #[ext(name = DescribeExt)]
100    impl<This> This
101    where
102        This: ValueAlg,
103    {
104        fn describe(&self) -> String {
105            self.value().to_string()
106        }
107    }
108
109    trait ReadAlg {
110        type Item;
111        type Key;
112
113        fn read(&self, key: Self::Key) -> Self::Item;
114    }
115
116    struct Reader;
117
118    impl ReadAlg for Reader {
119        type Item = String;
120        type Key = u32;
121
122        fn read(&self, key: u32) -> String {
123            key.to_string()
124        }
125    }
126
127    #[ext(name = ReadExt, defunc)]
128    impl<This, Item, Key> This
129    where
130        This: ReadAlg<Item = Item, Key = Key>,
131        Item: Send,
132        Key: Send,
133    {
134        async fn read_suffixed(&self, key: Key, suffix: Item) -> (Item, Item) {
135            (self.read(key), suffix)
136        }
137    }
138
139    #[tokio::test]
140    async fn preserves_named_parameters_bound_through_the_context() {
141        let reader = Arc::new(Reader);
142        let operation = ReadSuffixedOperation::<Reader>::default();
143
144        assert_eq!(
145            reader.read_suffixed(4, "!".to_owned()).await,
146            operation.apply(reader.clone(), (4, "!".to_owned())).await,
147        );
148        assert_eq!(<ReadSuffixedOperation<Reader> as OperationAlg>::ARG_NAMES, &["key", "suffix"]);
149    }
150
151    #[tokio::test]
152    async fn preserves_extension_methods_and_defunctionalizes_their_application() {
153        let value = Arc::new(Value(40));
154
155        assert_eq!(value.value_plus(2).await, 42);
156        assert_eq!(ValuePlusOperation::<Value>::default().apply(value, (2,)).await, 42);
157        assert_eq!(<ValuePlusOperation<Value> as OperationAlg>::NAME, "value_plus");
158        assert_eq!(<ValuePlusOperation<Value> as OperationAlg>::DOC, "Adds `increment` to the value.");
159        assert_eq!(<ValuePlusOperation<Value> as OperationAlg>::ARG_NAMES, &["increment"]);
160    }
161
162    trait BytesAlg {
163        type Error;
164        type Name;
165
166        fn push(&mut self, bytes: &[u8]) -> Result<(), Self::Error>;
167        fn leaf(&self, name: &Self::Name) -> Self::Name;
168    }
169
170    #[derive(Default)]
171    struct Buffer(Vec<u8>);
172
173    impl BytesAlg for Buffer {
174        type Error = ();
175        type Name = String;
176
177        fn push(&mut self, bytes: &[u8]) -> Result<(), ()> {
178            self.0.extend_from_slice(bytes);
179            Ok(())
180        }
181
182        fn leaf(&self, name: &String) -> String {
183            name.rsplit('/').next().unwrap_or_default().to_owned()
184        }
185    }
186
187    impl AsRef<[u8]> for Buffer {
188        fn as_ref(&self) -> &[u8] {
189            &self.0
190        }
191    }
192
193    impl AsMut<Buffer> for Buffer {
194        fn as_mut(&mut self) -> &mut Buffer {
195            self
196        }
197    }
198
199    #[ext(name = BytesExt, defunc)]
200    impl<This> This
201    where
202        This: BytesAlg,
203    {
204        /// Appends borrowed bytes.
205        fn append(&mut self, bytes: &[u8]) -> Result<(), This::Error> {
206            self.push(bytes)
207        }
208
209        /// Answers the last segment of each borrowed name.
210        fn leaves(&self, names: &[This::Name]) -> Vec<This::Name> {
211            names.iter().map(|name| self.leaf(name)).collect()
212        }
213
214        /// Answers the first `count` bytes, borrowed from the buffer.
215        fn head(&self, count: usize) -> &[u8]
216        where
217            This: AsRef<[u8]>,
218        {
219            &self.as_ref()[..count]
220        }
221
222        /// Keeps the bytes a predicate the caller selects accepts.
223        fn select<Predicate>(&self, bytes: Vec<u8>, predicate: Predicate) -> Vec<u8>
224        where
225            Predicate: Fn(u8) -> bool,
226        {
227            bytes.into_iter().filter(|byte| predicate(*byte)).collect()
228        }
229    }
230
231    #[tokio::test]
232    async fn applies_operations_to_borrowed_arguments() {
233        let bytes = [1, 2, 3];
234        let mut buffer = Buffer::default();
235        AppendOperation::<Buffer>::default().apply(&mut buffer, (&bytes[..],)).await.unwrap();
236        assert_eq!(buffer.0, [1, 2, 3]);
237        assert_eq!(<AppendOperation<Buffer> as OperationAlg>::ARG_NAMES, &["bytes"]);
238
239        let names = ["a/b".to_owned(), "c".to_owned()];
240        let leaves = LeavesOperation::<Buffer>::default().apply(Arc::new(Buffer::default()), (&names[..],)).await;
241        assert_eq!(leaves, ["b", "c"]);
242    }
243
244    trait TallyAlg {
245        fn bump(&mut self) -> u32;
246    }
247
248    /// Is `Send` but not `Sync`, as a context holding a `Cell` is.
249    #[derive(Default)]
250    struct Tally(std::cell::Cell<u32>);
251
252    impl TallyAlg for Tally {
253        fn bump(&mut self) -> u32 {
254            self.0.set(self.0.get() + 1);
255            self.0.get()
256        }
257    }
258
259    #[ext(name = TallyExt, defunc)]
260    impl<This> This
261    where
262        This: TallyAlg,
263    {
264        async fn bump_twice(&mut self) -> u32 {
265            self.bump();
266            self.bump()
267        }
268    }
269
270    #[tokio::test]
271    async fn applies_a_mutable_operation_to_an_owned_handle_whose_context_is_not_sync() {
272        fn sent<Running: Send>(running: Running) -> Running {
273            running
274        }
275
276        let operation = BumpTwiceOperation::<Tally>::default();
277        let running = operation.apply(Box::new(Tally::default()), ());
278        assert_eq!(sent(running).await, 2);
279    }
280
281    #[tokio::test]
282    async fn answers_a_borrowed_output_to_a_borrowed_handle() {
283        let buffer = Buffer(vec![1, 2, 3]);
284        let head = HeadOperation::<Buffer>::default().apply(&buffer, (2,)).await;
285        assert_eq!(head, [1, 2]);
286        assert_eq!(head, buffer.head(2));
287    }
288
289    #[ext(name = PrefixExt, defunc)]
290    impl<This> This
291    where
292        This: AsRef<[u8]> + ?Sized,
293    {
294        /// Answers the first `count` bytes of any byte carrier, a slice included.
295        fn prefix(&self, count: usize) -> &[u8] {
296            &self.as_ref()[..count.min(self.as_ref().len())]
297        }
298    }
299
300    #[tokio::test]
301    async fn applies_an_operation_over_an_unsized_carrier() {
302        let bytes: &[u8] = &[1, 2, 3];
303        assert_eq!(PrefixOperation::<[u8]>::default().apply(bytes, (5,)).await, [1, 2, 3]);
304        assert_eq!(PrefixOperation::<[u8]>::default().apply(bytes, (1,)).await, bytes.prefix(1));
305    }
306
307    trait Counter {
308        fn count(&self) -> usize;
309        fn bump(&mut self);
310    }
311
312    impl Counter for Buffer {
313        fn count(&self) -> usize {
314            self.0.len()
315        }
316
317        fn bump(&mut self) {
318            self.0.push(0);
319        }
320    }
321
322    #[ext(name = CounterExt, defunc)]
323    impl<This> This
324    where
325        This: Counter + ?Sized,
326    {
327        /// Answers the count plus `more`.
328        fn counted(&self, more: usize) -> usize {
329            self.count() + more
330        }
331
332        /// Bumps the count and answers it.
333        fn bumped(&mut self) -> usize {
334            self.bump();
335            self.count()
336        }
337    }
338
339    #[tokio::test]
340    async fn applies_an_operation_to_an_unsized_context_through_any_borrow() {
341        let counted = CountedOperation::<dyn Counter + Send + Sync>::default();
342        let shared: Arc<dyn Counter + Send + Sync> = Arc::new(Buffer(vec![1, 2]));
343        assert_eq!(counted.apply(Arc::clone(&shared), (1,)).await, 3);
344        assert_eq!(counted.apply(&*shared, (2,)).await, 4);
345
346        let mut owned: Box<dyn Counter + Send + Sync> = Box::new(Buffer(vec![1]));
347        let bumped = BumpedOperation::<dyn Counter + Send + Sync>::default();
348        assert_eq!(bumped.apply(&mut *owned, ()).await, 2);
349        assert_eq!(bumped.apply(owned, ()).await, 3);
350    }
351
352    #[tokio::test]
353    async fn selects_the_type_parameters_a_method_declares() {
354        fn odd(byte: u8) -> bool {
355            byte % 2 == 1
356        }
357        let operation = SelectOperation::<Buffer, fn(u8) -> bool>::default();
358        let selected = operation.apply(Arc::new(Buffer::default()), (vec![1, 2, 3], odd as fn(u8) -> bool)).await;
359        assert_eq!(selected, [1, 3]);
360        assert_eq!(<SelectOperation<Buffer, fn(u8) -> bool> as OperationAlg>::ARG_NAMES, &["bytes", "predicate"]);
361    }
362
363    #[test]
364    fn remains_compatible_with_ordinary_extensions() {
365        assert_eq!(Value(42).describe(), "42");
366    }
367}