Skip to main content

alux_ext/
lib.rs

1//! Reifies extension methods as first-order operations with explicit application meaning.
2
3#![allow(async_fn_in_trait)]
4
5extern crate self as alux_ext;
6
7/// Re-exports the procedural-macro implementation shared by ALUX program crates.
8///
9/// Program crates re-export their own backend attributes from this path so that authored code and
10/// generated code refer to the same macro implementation without depending on it directly.
11pub use alux_ext_macros as macros;
12pub use alux_ext_macros::ext;
13/// Re-exports the extension-method implementation referenced by generated code.
14///
15/// Expansion of [`ext`] names this path, so a crate using the attribute needs no separate
16/// `extend` dependency.
17pub use extend;
18
19use core::future::Future;
20
21/// Applies a defunctionalized operation to a context and an argument product.
22pub trait ApplyAlg<Context, Args> {
23    /// The value produced by applying the operation.
24    type Output;
25
26    /// Interprets the operation using the supplied context and arguments.
27    fn apply(&self, context: Context, args: Args) -> impl Future<Output = Self::Output> + Send;
28}
29
30/// Selects an owned runtime handle for a semantic context.
31pub trait HandlerContextAlg<Context> {
32    /// The owned carrier cloned into asynchronous operation invocations.
33    type Handle: AsRef<Context> + Clone + Send + Sync + 'static;
34}
35
36/// Describes the semantic signature of a defunctionalized operation.
37pub trait OperationAlg {
38    /// The semantic context interpreted by the operation.
39    type Context;
40    /// The product of arguments accepted by the operation.
41    type Args;
42
43    /// The source-level argument names, in declaration order.
44    const ARG_NAMES: &'static [&'static str];
45}
46
47#[cfg(test)]
48mod tests {
49    use super::{ApplyAlg, OperationAlg, ext};
50    use std::sync::Arc;
51
52    trait ValueAlg {
53        fn value(&self) -> u32;
54    }
55
56    struct Value(u32);
57
58    impl ValueAlg for Value {
59        fn value(&self) -> u32 {
60            self.0
61        }
62    }
63
64    #[ext(name = ValueExt, defunc)]
65    impl<This> This
66    where
67        This: ValueAlg,
68    {
69        async fn value_plus(&self, increment: u32) -> u32 {
70            self.value() + increment
71        }
72    }
73
74    #[ext(name = DescribeExt, supertraits = ValueAlg + Sized)]
75    impl<This> This
76    where
77        This: ValueAlg,
78    {
79        fn describe(&self) -> String {
80            self.value().to_string()
81        }
82    }
83
84    #[tokio::test]
85    async fn preserves_extension_methods_and_defunctionalizes_their_application() {
86        let value = Arc::new(Value(40));
87
88        assert_eq!(value.value_plus(2).await, 42);
89        assert_eq!(ValuePlusOperation::<Value>::default().apply(value, (2,)).await, 42);
90        assert_eq!(<ValuePlusOperation<Value> as OperationAlg>::ARG_NAMES, &["increment"]);
91    }
92
93    #[test]
94    fn remains_compatible_with_ordinary_extensions() {
95        assert_eq!(Value(42).describe(), "42");
96    }
97}