spin_factors/
factor.rs

1use std::any::Any;
2
3use wasmtime::component::{Linker, ResourceTable};
4
5use crate::{prepare::FactorInstanceBuilder, App, Error, PrepareContext, RuntimeFactors};
6
7/// A contained (i.e., "factored") piece of runtime functionality.
8pub trait Factor: Any + Sized {
9    /// The particular runtime configuration relevant to this factor.
10    ///
11    /// Runtime configuration allows for user-provided customization of the
12    /// factor's behavior on a per-app basis.
13    type RuntimeConfig;
14
15    /// The application state of this factor.
16    ///
17    /// This state *may* be cached by the runtime across multiple requests.
18    type AppState: Sync;
19
20    /// The builder of instance state for this factor.
21    type InstanceBuilder: FactorInstanceBuilder;
22
23    /// Initializes this `Factor` for a runtime once at runtime startup.
24    ///
25    /// This will be called at most once, before any call to
26    /// [`Factor::prepare`]. `InitContext` provides access to a wasmtime
27    /// `Linker`, so this is where any bindgen `add_to_linker` calls go.
28    ///
29    /// The type parameter `T` here is the same as the [`wasmtime::Store`] type
30    /// parameter `T`, which will contain the [`RuntimeFactors::InstanceState`].
31    fn init<T: Send + 'static>(&mut self, mut ctx: InitContext<T, Self>) -> anyhow::Result<()> {
32        _ = &mut ctx;
33        Ok(())
34    }
35
36    /// Performs factor-specific validation and configuration for the given
37    /// [`App`].
38    ///
39    /// `ConfigureAppContext` gives access to:
40    /// - The `spin_app::App`
41    /// - This factors's `RuntimeConfig`
42    /// - The `AppState` for any factors configured before this one
43    ///
44    /// A runtime may - but is not required to - reuse the returned config
45    /// across multiple instances. Because this method may be called
46    /// per-instantiation, it should avoid any blocking operations that could
47    /// unnecessarily delay execution.
48    ///
49    /// This method may be called without any call to `init` or `prepare` in
50    /// cases where only validation is needed (e.g., `spin doctor`).
51    fn configure_app<T: RuntimeFactors>(
52        &self,
53        ctx: ConfigureAppContext<T, Self>,
54    ) -> anyhow::Result<Self::AppState>;
55
56    /// Creates a new `FactorInstanceBuilder`, which will later build
57    /// per-instance state for this factor.
58    ///
59    /// This method is given access to the app component being instantiated and
60    /// to any other factors' instance builders that have already been prepared.
61    /// As such, this is the primary place for inter-factor dependencies to be
62    /// used.
63    fn prepare<T: RuntimeFactors>(
64        &self,
65        ctx: PrepareContext<T, Self>,
66    ) -> anyhow::Result<Self::InstanceBuilder>;
67}
68
69/// The instance state of the given [`Factor`] `F`.
70pub type FactorInstanceState<F> =
71    <<F as Factor>::InstanceBuilder as FactorInstanceBuilder>::InstanceState;
72
73pub(crate) type GetDataFn<T, U> = fn(&mut T) -> &mut FactorInstanceState<U>;
74
75pub(crate) type GetDataWithTableFn<T, U> =
76    fn(&mut T) -> (&mut FactorInstanceState<U>, &mut ResourceTable);
77
78/// An InitContext is passed to [`Factor::init`], giving access to the global
79/// common [`wasmtime::component::Linker`].
80pub struct InitContext<'a, T, U: Factor> {
81    pub(crate) linker: &'a mut Linker<T>,
82    pub(crate) get_data: GetDataFn<T, U>,
83    pub(crate) get_data_with_table: GetDataWithTableFn<T, U>,
84}
85
86impl<'a, T, U: Factor> InitContext<'a, T, U> {
87    #[doc(hidden)]
88    pub fn new(
89        linker: &'a mut Linker<T>,
90        get_data: GetDataFn<T, U>,
91        get_data_with_table: GetDataWithTableFn<T, U>,
92    ) -> Self {
93        Self {
94            linker,
95            get_data,
96            get_data_with_table,
97        }
98    }
99
100    /// Returns a mutable reference to the [`wasmtime::component::Linker`].
101    pub fn linker(&mut self) -> &mut Linker<T> {
102        self.linker
103    }
104
105    /// Returns a function that can be used to get the instance state for this factor.
106    pub fn get_data_fn(&self) -> GetDataFn<T, U> {
107        self.get_data
108    }
109
110    /// Returns a function that can be used to get the instance state for this
111    /// factor along with the instance's [`ResourceTable`].
112    pub fn get_data_with_table_fn(&self) -> GetDataWithTableFn<T, U> {
113        self.get_data_with_table
114    }
115
116    /// Convenience method to link a binding to the linker.
117    pub fn link_bindings(
118        &mut self,
119        add_to_linker: impl Fn(
120            &mut Linker<T>,
121            fn(&mut T) -> &mut FactorInstanceState<U>,
122        ) -> anyhow::Result<()>,
123    ) -> anyhow::Result<()> {
124        add_to_linker(self.linker, self.get_data)
125    }
126}
127
128pub struct ConfigureAppContext<'a, T: RuntimeFactors, F: Factor> {
129    app: &'a App,
130    app_state: &'a T::AppState,
131    runtime_config: Option<F::RuntimeConfig>,
132}
133
134impl<'a, T: RuntimeFactors, F: Factor> ConfigureAppContext<'a, T, F> {
135    #[doc(hidden)]
136    pub fn new(
137        app: &'a App,
138        app_state: &'a T::AppState,
139        runtime_config: Option<F::RuntimeConfig>,
140    ) -> crate::Result<Self> {
141        Ok(Self {
142            app,
143            app_state,
144            runtime_config,
145        })
146    }
147
148    /// Get the [`App`] being configured.
149    pub fn app(&self) -> &'a App {
150        self.app
151    }
152
153    /// Get the app state related to the given factor.
154    pub fn app_state<U: Factor>(&self) -> crate::Result<&'a U::AppState> {
155        T::app_state::<U>(self.app_state).ok_or(Error::no_such_factor::<U>())
156    }
157
158    /// Get a reference to the runtime configuration for the given factor.
159    pub fn runtime_config(&self) -> Option<&F::RuntimeConfig> {
160        self.runtime_config.as_ref()
161    }
162
163    /// Take ownership of the runtime configuration for the given factor.
164    pub fn take_runtime_config(&mut self) -> Option<F::RuntimeConfig> {
165        self.runtime_config.take()
166    }
167}
168
169#[doc(hidden)]
170pub struct ConfiguredApp<T: RuntimeFactors> {
171    app: App,
172    app_state: T::AppState,
173}
174
175impl<T: RuntimeFactors> ConfiguredApp<T> {
176    #[doc(hidden)]
177    pub fn new(app: App, app_state: T::AppState) -> Self {
178        Self { app, app_state }
179    }
180
181    /// Get the configured [`App`].
182    pub fn app(&self) -> &App {
183        &self.app
184    }
185
186    /// Get the configured app's state related to the given factor.
187    pub fn app_state<U: Factor>(&self) -> crate::Result<&U::AppState> {
188        T::app_state::<U>(&self.app_state).ok_or(Error::no_such_factor::<U>())
189    }
190}