1+ //! Spin internal application interfaces
2+ //!
3+ //! This crate contains interfaces to Spin application configuration to be used
4+ //! by crates that implement Spin execution environments: trigger executors and
5+ //! host components, in particular.
6+
7+ #![ deny( missing_docs) ]
8+
19mod host_component;
210pub mod locked;
311pub mod values;
@@ -13,37 +21,53 @@ pub use async_trait::async_trait;
1321pub use host_component:: DynamicHostComponent ;
1422pub use locked:: Variable ;
1523
24+ /// A trait for implementing the low-level operations needed to load an [`App`].
1625// TODO(lann): Should this migrate to spin-loader?
1726#[ async_trait]
1827pub trait Loader {
28+ /// Called with an implementation-defined `uri` pointing to some
29+ /// representation of a [`LockedApp`], which will be loaded.
1930 async fn load_app ( & self , uri : & str ) -> anyhow:: Result < LockedApp > ;
2031
32+ /// Called with a [`LockedComponentSource`] pointing to a Wasm module
33+ /// binary, which will be loaded.
2134 async fn load_module (
2235 & self ,
2336 engine : & wasmtime:: Engine ,
2437 source : & LockedComponentSource ,
2538 ) -> anyhow:: Result < spin_core:: Module > ;
2639
40+ /// Called with an [`AppComponent`]; any `files` configured with the
41+ /// component should be "mounted" into the `store_builder`, via e.g.
42+ /// [`StoreBuilder::read_only_preopened_dir`].
2743 async fn mount_files (
2844 & self ,
2945 store_builder : & mut StoreBuilder ,
3046 component : & AppComponent ,
3147 ) -> anyhow:: Result < ( ) > ;
3248}
3349
50+ /// An `AppLoader` holds an implementation of [`Loader`] along with
51+ /// [`DynamicHostComponents`] configuration.
3452pub struct AppLoader {
3553 inner : Box < dyn Loader + Send + Sync > ,
3654 dynamic_host_components : DynamicHostComponents ,
3755}
3856
3957impl AppLoader {
58+ /// Creates a new [`AppLoader`].
4059 pub fn new ( loader : impl Loader + Send + Sync + ' static ) -> Self {
4160 Self {
4261 inner : Box :: new ( loader) ,
4362 dynamic_host_components : Default :: default ( ) ,
4463 }
4564 }
4665
66+ /// Adds a [`DynamicHostComponent`] to the given [`EngineBuilder`] and
67+ /// configures this [`AppLoader`] to update it on component instantiation.
68+ ///
69+ /// This calls [`EngineBuilder::add_host_component`] for you; it should not
70+ /// be called separately.
4771 pub fn add_dynamic_host_component < T : Send + Sync , DHC : DynamicHostComponent > (
4872 & mut self ,
4973 engine_builder : & mut EngineBuilder < T > ,
@@ -53,6 +77,7 @@ impl AppLoader {
5377 . add_dynamic_host_component ( engine_builder, host_component)
5478 }
5579
80+ /// Loads an [`App`] from the given `Loader`-implementation-specific `uri`.
5681 pub async fn load_app ( & self , uri : String ) -> Result < App > {
5782 let locked = self
5883 . inner
@@ -66,6 +91,8 @@ impl AppLoader {
6691 } )
6792 }
6893
94+ /// Loads an [`OwnedApp`] from the given `Loader`-implementation-specific
95+ /// `uri`; the [`OwnedApp`] takes ownership of this [`AppLoader`].
6996 pub async fn load_owned_app ( self , uri : String ) -> Result < OwnedApp > {
7097 OwnedApp :: try_new_async ( self , |loader| Box :: pin ( loader. load_app ( uri) ) ) . await
7198 }
@@ -88,11 +115,13 @@ pub struct OwnedApp {
88115}
89116
90117impl OwnedApp {
118+ /// Returns a reference to the owned [`App`].
91119 pub fn borrowed ( & self ) -> & App {
92120 self . borrow_app ( )
93121 }
94122}
95123
124+ /// An `App` holds loaded configuration for a Spin application.
96125#[ derive( Debug ) ]
97126pub struct App < ' a > {
98127 loader : & ' a AppLoader ,
@@ -101,10 +130,16 @@ pub struct App<'a> {
101130}
102131
103132impl < ' a > App < ' a > {
133+ /// Returns a [`Loader`]-implementation-specific URI for this app.
104134 pub fn uri ( & self ) -> & str {
105135 & self . uri
106136 }
107137
138+ /// Deserializes typed metadata for this app.
139+ ///
140+ /// Returns `Ok(None)` if there is no metadata for the given `key` and an
141+ /// `Err` only if there _is_ a value for the `key` but the typed
142+ /// deserialization failed.
108143 pub fn get_metadata < ' this , T : Deserialize < ' this > > ( & ' this self , key : & str ) -> Result < Option < T > > {
109144 self . locked
110145 . metadata
@@ -113,76 +148,100 @@ impl<'a> App<'a> {
113148 . transpose ( )
114149 }
115150
151+ /// Deserializes typed metadata for this app.
152+ ///
153+ /// Like [`App::get_metadata`], but returns an `Err` if there is no metadata
154+ /// for the given `key`.
116155 pub fn require_metadata < ' this , T : Deserialize < ' this > > ( & ' this self , key : & str ) -> Result < T > {
117156 self . get_metadata ( key) ?
118- . ok_or_else ( || Error :: ManifestError ( format ! ( "missing required {key:?}" ) ) )
157+ . ok_or_else ( || Error :: MetadataError ( format ! ( "missing required {key:?}" ) ) )
119158 }
120159
160+ /// Returns an iterator of custom config [`Variable`]s defined for this app.
121161 pub fn variables ( & self ) -> impl Iterator < Item = ( & String , & Variable ) > {
122162 self . locked . variables . iter ( )
123163 }
124164
165+ /// Returns an iterator of [`AppComponent`]s defined for this app.
125166 pub fn components ( & self ) -> impl Iterator < Item = AppComponent > {
126167 self . locked
127168 . components
128169 . iter ( )
129170 . map ( |locked| AppComponent { app : self , locked } )
130171 }
131172
173+ /// Returns the [`AppComponent`] with the given `component_id`, or `None`
174+ /// if it doesn't exist.
132175 pub fn get_component ( & self , component_id : & str ) -> Option < AppComponent > {
133176 self . components ( )
134177 . find ( |component| component. locked . id == component_id)
135178 }
136179
180+ /// Returns an iterator of [`AppTrigger`]s defined for this app.
137181 pub fn triggers ( & self ) -> impl Iterator < Item = AppTrigger > {
138182 self . locked
139183 . triggers
140184 . iter ( )
141185 . map ( |locked| AppTrigger { app : self , locked } )
142186 }
143187
188+ /// Returns an iterator of [`AppTrigger`]s defined for this app with
189+ /// the given `trigger_type`.
144190 pub fn triggers_with_type ( & ' a self , trigger_type : & ' a str ) -> impl Iterator < Item = AppTrigger > {
145191 self . triggers ( )
146192 . filter ( move |trigger| trigger. locked . trigger_type == trigger_type)
147193 }
148194}
149195
196+ /// An `AppComponent` holds configuration for a Spin application component.
150197pub struct AppComponent < ' a > {
198+ /// The app this component belongs to.
151199 pub app : & ' a App < ' a > ,
152200 locked : & ' a LockedComponent ,
153201}
154202
155203impl < ' a > AppComponent < ' a > {
204+ /// Returns this component's app-unique ID.
156205 pub fn id ( & self ) -> & str {
157206 & self . locked . id
158207 }
159208
209+ /// Returns this component's Wasm module source.
160210 pub fn source ( & self ) -> & LockedComponentSource {
161211 & self . locked . source
162212 }
163213
214+ /// Returns an iterator of [`ContentPath`]s for this component's configured
215+ /// "directory mounts".
164216 pub fn files ( & self ) -> std:: slice:: Iter < ContentPath > {
165217 self . locked . files . iter ( )
166218 }
167219
220+ /// Deserializes typed metadata for this component.
221+ ///
222+ /// Returns `Ok(None)` if there is no metadata for the given `key` and an
223+ /// `Err` only if there _is_ a value for the `key` but the typed
224+ /// deserialization failed.
168225 pub fn get_metadata < T : Deserialize < ' a > > ( & self , key : & str ) -> Result < Option < T > > {
169226 self . locked
170227 . metadata
171228 . get ( key)
172229 . map ( |value| {
173230 T :: deserialize ( value) . map_err ( |err| {
174- Error :: ManifestError ( format ! (
231+ Error :: MetadataError ( format ! (
175232 "failed to deserialize {key:?} = {value:?}: {err:?}"
176233 ) )
177234 } )
178235 } )
179236 . transpose ( )
180237 }
181238
239+ /// Returns an iterator of custom config values for this component.
182240 pub fn config ( & self ) -> impl Iterator < Item = ( & String , & String ) > {
183241 self . locked . config . iter ( )
184242 }
185243
244+ /// Loads and returns the [`spin_core::Module`] for this component.
186245 pub async fn load_module < T : Send + Sync > (
187246 & self ,
188247 engine : & Engine < T > ,
@@ -195,6 +254,11 @@ impl<'a> AppComponent<'a> {
195254 . map_err ( Error :: LoaderError )
196255 }
197256
257+ /// Updates the given [`StoreBuilder`] with configuration for this component.
258+ ///
259+ /// In particular, the WASI 'env' and "preloaded dirs" are set up, and any
260+ /// [`DynamicHostComponent`]s associated with the source [`AppLoader`] are
261+ /// configured.
198262 pub async fn apply_store_config ( & self , builder : & mut StoreBuilder ) -> Result < ( ) > {
199263 builder. env ( & self . locked . env ) . map_err ( Error :: CoreError ) ?;
200264
@@ -214,58 +278,74 @@ impl<'a> AppComponent<'a> {
214278 }
215279}
216280
281+ /// An `AppTrigger` holds configuration for a Spin application trigger.
217282pub struct AppTrigger < ' a > {
283+ /// The app this trigger belongs to.
218284 pub app : & ' a App < ' a > ,
219285 locked : & ' a LockedTrigger ,
220286}
221287
222288impl < ' a > AppTrigger < ' a > {
289+ /// Returns this trigger's app-unique ID.
223290 pub fn id ( & self ) -> & str {
224291 & self . locked . id
225292 }
226293
294+ /// Returns the Trigger's type.
227295 pub fn trigger_type ( & self ) -> & str {
228296 & self . locked . trigger_type
229297 }
230298
299+ /// Returns a reference to the [`AppComponent`] configured for this trigger.
300+ ///
301+ /// This is a convenience wrapper that looks up the component based on the
302+ /// 'component' metadata value which is conventionally a component ID.
231303 pub fn component ( & self ) -> Result < AppComponent < ' a > > {
232304 let component_id = self . locked . trigger_config . get ( "component" ) . ok_or_else ( || {
233- Error :: ManifestError ( format ! (
305+ Error :: MetadataError ( format ! (
234306 "trigger {:?} missing 'component' config field" ,
235307 self . locked. id
236308 ) )
237309 } ) ?;
238310 let component_id = component_id. as_str ( ) . ok_or_else ( || {
239- Error :: ManifestError ( format ! (
311+ Error :: MetadataError ( format ! (
240312 "trigger {:?} 'component' field has unexpected value {:?}" ,
241313 self . locked. id, component_id
242314 ) )
243315 } ) ?;
244316 self . app . get_component ( component_id) . ok_or_else ( || {
245- Error :: ManifestError ( format ! (
317+ Error :: MetadataError ( format ! (
246318 "missing component {:?} configured for trigger {:?}" ,
247319 component_id, self . locked. id
248320 ) )
249321 } )
250322 }
251323
324+ /// Deserializes this trigger's configuration into a typed value.
252325 pub fn typed_config < Config : Deserialize < ' a > > ( & self ) -> Result < Config > {
253326 Ok ( Config :: deserialize ( & self . locked . trigger_config ) ?)
254327 }
255328}
256329
330+ /// Type alias for a [`Result`]s with [`Error`].
257331pub type Result < T > = std:: result:: Result < T , Error > ;
258332
333+ /// Errors returned by methods in this crate.
259334#[ derive( Debug , thiserror:: Error ) ]
260335pub enum Error {
336+ /// An error propagated from the [`spin_core`] crate.
261337 #[ error( "spin core error: {0:#}" ) ]
262338 CoreError ( anyhow:: Error ) ,
339+ /// An error from a [`DynamicHostComponent`].
263340 #[ error( "host component error: {0:#}" ) ]
264341 HostComponentError ( anyhow:: Error ) ,
342+ /// An error from a [`Loader`] implementation.
265343 #[ error( "loader error: {0:#}" ) ]
266344 LoaderError ( anyhow:: Error ) ,
267- #[ error( "manifest error: {0}" ) ]
268- ManifestError ( String ) ,
345+ /// An error indicating missing or unexpected metadata.
346+ #[ error( "metadata error: {0}" ) ]
347+ MetadataError ( String ) ,
348+ /// An error indicating failed JSON (de)serialization.
269349 #[ error( "json error: {0}" ) ]
270350 JsonError ( #[ from] serde_json:: Error ) ,
271351}
0 commit comments