spin_factors/
factor.rs

1use std::any::Any;
2use std::marker::PhantomData;
3
4use wasmtime::component::{Linker, ResourceTable};
5
6use crate::{
7    prepare::FactorInstanceBuilder, App, AsInstanceState, Error, PrepareContext, RuntimeFactors,
8};
9
10/// A contained (i.e., "factored") piece of runtime functionality.
11pub trait Factor: Any + Sized {
12    /// The particular runtime configuration relevant to this factor.
13    ///
14    /// Runtime configuration allows for user-provided customization of the
15    /// factor's behavior on a per-app basis.
16    type RuntimeConfig;
17
18    /// The application state of this factor.
19    ///
20    /// This state *may* be cached by the runtime across multiple requests.
21    type AppState: Sync;
22
23    /// The builder of instance state for this factor.
24    type InstanceBuilder: FactorInstanceBuilder;
25
26    /// Initializes this `Factor` for a runtime once at runtime startup.
27    ///
28    /// This will be called at most once, before any call to
29    /// [`Factor::prepare`]. `InitContext` provides access to a wasmtime
30    /// `Linker`, so this is where any bindgen `add_to_linker` calls go.
31    fn init(&mut self, ctx: &mut impl InitContext<Self>) -> anyhow::Result<()> {
32        let _ = 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
73/// An InitContext is passed to [`Factor::init`], giving access to the global
74/// common [`wasmtime::component::Linker`].
75pub trait InitContext<F: Factor> {
76    /// The `T` in `Store<T>`.
77    type StoreData: Send + 'static;
78
79    /// Returns a mutable reference to the [`wasmtime::component::Linker`].
80    fn linker(&mut self) -> &mut Linker<Self::StoreData>;
81
82    /// Get the instance state for this factor from the store's state.
83    fn get_data(store: &mut Self::StoreData) -> &mut FactorInstanceState<F> {
84        Self::get_data_with_table(store).0
85    }
86
87    /// Get the instance state for this factor from the store's state, with the
88    /// resource table as well.
89    fn get_data_with_table(
90        store: &mut Self::StoreData,
91    ) -> (&mut FactorInstanceState<F>, &mut ResourceTable);
92
93    /// Convenience method to link a binding to the linker.
94    fn link_bindings(
95        &mut self,
96        add_to_linker: impl Fn(
97            &mut Linker<Self::StoreData>,
98            fn(&mut Self::StoreData) -> &mut FactorInstanceState<F>,
99        ) -> anyhow::Result<()>,
100    ) -> anyhow::Result<()> {
101        add_to_linker(self.linker(), Self::get_data)
102    }
103}
104
105// used in #[derive(RuntimeFactor)]
106#[doc(hidden)]
107pub struct FactorInitContext<'a, T, G> {
108    pub linker: &'a mut Linker<T>,
109    pub _marker: PhantomData<G>,
110}
111
112// used in #[derive(RuntimeFactor)]
113#[doc(hidden)]
114pub trait FactorField {
115    type State: crate::RuntimeFactorsInstanceState;
116    type Factor: Factor;
117
118    fn get(field: &mut Self::State)
119        -> (&mut FactorInstanceState<Self::Factor>, &mut ResourceTable);
120}
121
122impl<T, G> InitContext<G::Factor> for FactorInitContext<'_, T, G>
123where
124    G: FactorField,
125    T: AsInstanceState<G::State> + Send + 'static,
126{
127    type StoreData = T;
128
129    fn linker(&mut self) -> &mut Linker<Self::StoreData> {
130        self.linker
131    }
132
133    fn get_data_with_table(
134        store: &mut Self::StoreData,
135    ) -> (&mut FactorInstanceState<G::Factor>, &mut ResourceTable) {
136        G::get(store.as_instance_state())
137    }
138}
139
140pub struct ConfigureAppContext<'a, T: RuntimeFactors, F: Factor> {
141    app: &'a App,
142    app_state: &'a T::AppState,
143    runtime_config: Option<F::RuntimeConfig>,
144}
145
146impl<'a, T: RuntimeFactors, F: Factor> ConfigureAppContext<'a, T, F> {
147    #[doc(hidden)]
148    pub fn new(
149        app: &'a App,
150        app_state: &'a T::AppState,
151        runtime_config: Option<F::RuntimeConfig>,
152    ) -> crate::Result<Self> {
153        Ok(Self {
154            app,
155            app_state,
156            runtime_config,
157        })
158    }
159
160    /// Get the [`App`] being configured.
161    pub fn app(&self) -> &'a App {
162        self.app
163    }
164
165    /// Get the app state related to the given factor.
166    pub fn app_state<U: Factor>(&self) -> crate::Result<&'a U::AppState> {
167        T::app_state::<U>(self.app_state).ok_or(Error::no_such_factor::<U>())
168    }
169
170    /// Get a reference to the runtime configuration for the given factor.
171    pub fn runtime_config(&self) -> Option<&F::RuntimeConfig> {
172        self.runtime_config.as_ref()
173    }
174
175    /// Take ownership of the runtime configuration for the given factor.
176    pub fn take_runtime_config(&mut self) -> Option<F::RuntimeConfig> {
177        self.runtime_config.take()
178    }
179}
180
181#[doc(hidden)]
182pub struct ConfiguredApp<T: RuntimeFactors> {
183    app: App,
184    app_state: T::AppState,
185}
186
187impl<T: RuntimeFactors> ConfiguredApp<T> {
188    #[doc(hidden)]
189    pub fn new(app: App, app_state: T::AppState) -> Self {
190        Self { app, app_state }
191    }
192
193    /// Get the configured [`App`].
194    pub fn app(&self) -> &App {
195        &self.app
196    }
197
198    /// Get the configured app's state related to the given factor.
199    pub fn app_state<U: Factor>(&self) -> crate::Result<&U::AppState> {
200        T::app_state::<U>(&self.app_state).ok_or(Error::no_such_factor::<U>())
201    }
202}