zrx_module/module.rs
1// Copyright (c) 2025-2026 Zensical and contributors
2
3// SPDX-License-Identifier: MIT
4// Third-party contributions licensed under DCO
5
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to
8// deal in the Software without restriction, including without limitation the
9// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10// sell copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22// IN THE SOFTWARE.
23
24// ----------------------------------------------------------------------------
25
26//! Module.
27
28pub mod context;
29pub mod error;
30
31use context::Context;
32use error::Result;
33
34// ----------------------------------------------------------------------------
35// Traits
36// ----------------------------------------------------------------------------
37
38/// Module.
39///
40/// It's modules all the way down – the fundamental idea of this library is that
41/// all specific functionality can and should be provided as modules, resulting
42/// in an inherently flexible and composable system, where users can pick and
43/// choose each module according to their requirements.
44///
45/// # Architecture
46///
47/// This crate is carefully integrated with other crates of this library, most
48/// notably the [`zrx-id`] and [`zrx-stream`] crates.
49///
50/// - Each module is built from a dedicated [`Workflow`][], which represents a
51/// set of [`Stream`][] transformations. In workflows, streams can be combined
52/// and transformed using operators like [`Stream::join`][], [`Stream::map`][]
53/// and many others, a concept borrowed from reactive programming.
54///
55/// - Modules can be attached and detached at runtime, allowing users to easily
56/// add and remove functionality as required. The scheduler, which takes care
57/// of orchestration, automatically recomputes the execution plan whenever
58/// modules are added or removed.
59///
60/// - Modules can co-operate through typed subscriptions. Within a module, typed
61/// subscriptions can be created through [`Context::add`][], adding a source
62/// [`Stream`][] to the module's workflow that automatically subscribes to
63/// matching streams from other modules.
64///
65/// # Considerations
66///
67/// Creating a module system that stands the test of time is a non-trivial task.
68/// Deliberate care must be taken to ensure that the system isn't only flexible
69/// and extensible enough, but also that it can evolve without major breaking
70/// changes. The following design decisions ensure just that:
71///
72/// - The API surface of the module system is intentionally minimal, made of a
73/// single entrypoint, [`Module::setup`]. As cooperation and synchronization
74/// of modules is implemented with graphs, a single entrypoint is sufficient
75/// for all use cases, and allows to keep the API simple and extensible.
76///
77/// - Upon initialization, modules are passed a [`Context`] for all interactions
78/// with the system, which allows us to add new functionality without breaking
79/// changes, as long as the signature of existing functions stays the same. It
80/// also means experimental features can be gated behind feature flags.
81///
82/// - Modules are not generic over identifier types, like large parts of this
83/// library, but tied to [`Id`][]. The reason is that the [`zrx-id`] crate is
84/// the canonical way of working with identifiers, and the module system is
85/// designed to be tightly integrated with it.
86///
87/// Our design is fundamentally different from some plugin and module systems
88/// that expose a limited number of extension points (often called hooks), each
89/// of which requires a static signature. This makes it much harder to evolve
90/// without breaking the entire ecosystem. By contrast, our design doesn't come
91/// with a fixed number of extension points, but allows users to create their
92/// own extension points through subscriptions.
93///
94/// [`Error`]: crate::module::error::Error
95/// [`Id`]: zrx_id::Id
96/// [`Stream`]: zrx_stream::Stream
97/// [`Stream::join`]: zrx_stream::Stream::join
98/// [`Stream::map`]: zrx_stream::Stream::map
99/// [`Workflow`]: zrx_stream::Workflow
100/// [`zrx-id`]: zrx_id
101/// [`zrx-stream`]: zrx_stream
102pub trait Module {
103 /// Initializes the module.
104 ///
105 /// # Errors
106 ///
107 /// This method should return an error when a problem is encountered trying
108 /// to initialize the module.
109 fn setup(&self, ctx: &mut Context) -> Result;
110}