Skip to content

Commit 4870bba

Browse files
committed
Encapsulate the application and loader
Signed-off-by: itowlson <ivan.towlson@fermyon.com>
1 parent 163503d commit 4870bba

File tree

3 files changed

+108
-85
lines changed

3 files changed

+108
-85
lines changed

crates/build/src/lib.rs

+6-4
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,14 @@ pub async fn build(
5959
};
6060

6161
if !skip_target_checks {
62-
let resolution_context =
63-
spin_environments::ResolutionContext::new(manifest_file.parent().unwrap()).await?;
62+
let application = spin_environments::ApplicationToValidate::new(
63+
manifest.clone(),
64+
manifest_file.parent().unwrap(),
65+
)
66+
.await?;
6467
let errors = spin_environments::validate_application_against_environment_ids(
68+
&application,
6569
build_info.deployment_targets(),
66-
manifest,
67-
&resolution_context,
6870
)
6971
.await?;
7072

crates/environments/src/lib.rs

+7-17
Original file line numberDiff line numberDiff line change
@@ -5,30 +5,26 @@ mod loader;
55

66
use environment_definition::{load_environment, TargetEnvironment, TriggerType};
77
use futures::future::try_join_all;
8-
pub use loader::ResolutionContext;
9-
use loader::{load_and_resolve_all, ComponentToValidate};
8+
pub use loader::ApplicationToValidate;
9+
use loader::ComponentToValidate;
1010

1111
pub async fn validate_application_against_environment_ids(
12+
application: &ApplicationToValidate,
1213
env_ids: &[impl AsRef<str>],
13-
app: &spin_manifest::schema::v2::AppManifest,
14-
resolution_context: &ResolutionContext,
1514
) -> anyhow::Result<Vec<anyhow::Error>> {
1615
if env_ids.is_empty() {
1716
return Ok(Default::default());
1817
}
1918

2019
let envs = try_join_all(env_ids.iter().map(load_environment)).await?;
21-
validate_application_against_environments(&envs, app, resolution_context).await
20+
validate_application_against_environments(application, &envs).await
2221
}
2322

2423
async fn validate_application_against_environments(
24+
application: &ApplicationToValidate,
2525
envs: &[TargetEnvironment],
26-
app: &spin_manifest::schema::v2::AppManifest,
27-
resolution_context: &ResolutionContext,
2826
) -> anyhow::Result<Vec<anyhow::Error>> {
29-
use futures::FutureExt;
30-
31-
for trigger_type in app.triggers.keys() {
27+
for trigger_type in application.trigger_types() {
3228
if let Some(env) = envs.iter().find(|e| !e.supports_trigger_type(trigger_type)) {
3329
anyhow::bail!(
3430
"Environment {} does not support trigger type {trigger_type}",
@@ -37,13 +33,7 @@ async fn validate_application_against_environments(
3733
}
3834
}
3935

40-
let components_by_trigger_type_futs = app.triggers.iter().map(|(ty, ts)| {
41-
load_and_resolve_all(app, ts, resolution_context)
42-
.map(|css| css.map(|css| (ty.to_owned(), css)))
43-
});
44-
let components_by_trigger_type = try_join_all(components_by_trigger_type_futs)
45-
.await
46-
.context("Failed to prepare components for target environment checking")?;
36+
let components_by_trigger_type = application.components_by_trigger_type().await?;
4737

4838
let mut errs = vec![];
4939

crates/environments/src/loader.rs

+95-64
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,6 @@ pub(crate) struct ComponentToValidate<'a> {
1010
wasm: Vec<u8>,
1111
}
1212

13-
struct ComponentSource<'a> {
14-
id: &'a str,
15-
source: &'a spin_manifest::schema::v2::ComponentSource,
16-
dependencies: WrappedComponentDependencies,
17-
}
18-
1913
impl<'a> ComponentToValidate<'a> {
2014
pub fn id(&self) -> &str {
2115
self.id
@@ -30,75 +24,112 @@ impl<'a> ComponentToValidate<'a> {
3024
}
3125
}
3226

33-
pub async fn load_and_resolve_all<'a>(
34-
app: &'a spin_manifest::schema::v2::AppManifest,
35-
triggers: &'a [spin_manifest::schema::v2::Trigger],
36-
resolution_context: &'a ResolutionContext,
37-
) -> anyhow::Result<Vec<ComponentToValidate<'a>>> {
38-
let component_futures = triggers
39-
.iter()
40-
.map(|t| load_and_resolve_one(app, t, resolution_context));
41-
try_join_all(component_futures).await
27+
pub struct ApplicationToValidate {
28+
manifest: spin_manifest::schema::v2::AppManifest,
29+
wasm_loader: spin_loader::WasmLoader,
4230
}
4331

44-
async fn load_and_resolve_one<'a>(
45-
app: &'a spin_manifest::schema::v2::AppManifest,
46-
trigger: &'a spin_manifest::schema::v2::Trigger,
47-
resolution_context: &'a ResolutionContext,
48-
) -> anyhow::Result<ComponentToValidate<'a>> {
49-
let component_spec = trigger
50-
.component
51-
.as_ref()
52-
.ok_or_else(|| anyhow!("No component specified for trigger {}", trigger.id))?;
53-
let (id, source, dependencies) = match component_spec {
54-
spin_manifest::schema::v2::ComponentSpec::Inline(c) => {
55-
(trigger.id.as_str(), &c.source, &c.dependencies)
56-
}
57-
spin_manifest::schema::v2::ComponentSpec::Reference(r) => {
58-
let id = r.as_ref();
59-
let Some(component) = app.components.get(r) else {
60-
anyhow::bail!(
61-
"Component {id} specified for trigger {} does not exist",
62-
trigger.id
63-
);
64-
};
65-
(id, &component.source, &component.dependencies)
66-
}
67-
};
68-
69-
let component = ComponentSource {
70-
id,
71-
source,
72-
dependencies: WrappedComponentDependencies::new(dependencies),
73-
};
32+
impl ApplicationToValidate {
33+
pub async fn new(
34+
manifest: spin_manifest::schema::v2::AppManifest,
35+
base_dir: impl AsRef<Path>,
36+
) -> anyhow::Result<Self> {
37+
let wasm_loader =
38+
spin_loader::WasmLoader::new(base_dir.as_ref().to_owned(), None, None).await?;
39+
Ok(Self {
40+
manifest,
41+
wasm_loader,
42+
})
43+
}
7444

75-
let loader = ComponentSourceLoader::new(resolution_context.wasm_loader());
45+
fn component_source<'a>(
46+
&'a self,
47+
trigger: &'a spin_manifest::schema::v2::Trigger,
48+
) -> anyhow::Result<ComponentSource<'a>> {
49+
let component_spec = trigger
50+
.component
51+
.as_ref()
52+
.ok_or_else(|| anyhow!("No component specified for trigger {}", trigger.id))?;
53+
let (id, source, dependencies) = match component_spec {
54+
spin_manifest::schema::v2::ComponentSpec::Inline(c) => {
55+
(trigger.id.as_str(), &c.source, &c.dependencies)
56+
}
57+
spin_manifest::schema::v2::ComponentSpec::Reference(r) => {
58+
let id = r.as_ref();
59+
let Some(component) = self.manifest.components.get(r) else {
60+
anyhow::bail!(
61+
"Component {id} specified for trigger {} does not exist",
62+
trigger.id
63+
);
64+
};
65+
(id, &component.source, &component.dependencies)
66+
}
67+
};
68+
69+
Ok(ComponentSource {
70+
id,
71+
source,
72+
dependencies: WrappedComponentDependencies::new(dependencies),
73+
})
74+
}
7675

77-
let wasm = spin_compose::compose(&loader, &component).await.with_context(|| format!("Spin needed to compose dependencies for {id} as part of target checking, but composition failed"))?;
76+
pub fn trigger_types(&self) -> impl Iterator<Item = &String> {
77+
self.manifest.triggers.keys()
78+
}
7879

79-
Ok(ComponentToValidate {
80-
id,
81-
source_description: source_description(component.source),
82-
wasm,
83-
})
84-
}
80+
pub fn triggers(
81+
&self,
82+
) -> impl Iterator<Item = (&String, &Vec<spin_manifest::schema::v2::Trigger>)> {
83+
self.manifest.triggers.iter()
84+
}
8585

86-
pub struct ResolutionContext {
87-
wasm_loader: spin_loader::WasmLoader,
88-
}
86+
pub(crate) async fn components_by_trigger_type(
87+
&self,
88+
) -> anyhow::Result<Vec<(String, Vec<ComponentToValidate<'_>>)>> {
89+
use futures::FutureExt;
90+
91+
let components_by_trigger_type_futs = self.triggers().map(|(ty, ts)| {
92+
self.components_for_trigger(ts)
93+
.map(|css| css.map(|css| (ty.to_owned(), css)))
94+
});
95+
let components_by_trigger_type = try_join_all(components_by_trigger_type_futs)
96+
.await
97+
.context("Failed to prepare components for target environment checking")?;
98+
Ok(components_by_trigger_type)
99+
}
89100

90-
impl ResolutionContext {
91-
pub async fn new(base_dir: impl AsRef<Path>) -> anyhow::Result<Self> {
92-
let wasm_loader =
93-
spin_loader::WasmLoader::new(base_dir.as_ref().to_owned(), None, None).await?;
94-
Ok(Self { wasm_loader })
101+
async fn components_for_trigger<'a>(
102+
&'a self,
103+
triggers: &'a [spin_manifest::schema::v2::Trigger],
104+
) -> anyhow::Result<Vec<ComponentToValidate<'a>>> {
105+
let component_futures = triggers.iter().map(|t| self.load_and_resolve_trigger(t));
106+
try_join_all(component_futures).await
95107
}
96108

97-
fn wasm_loader(&self) -> &spin_loader::WasmLoader {
98-
&self.wasm_loader
109+
async fn load_and_resolve_trigger<'a>(
110+
&'a self,
111+
trigger: &'a spin_manifest::schema::v2::Trigger,
112+
) -> anyhow::Result<ComponentToValidate<'a>> {
113+
let component = self.component_source(trigger)?;
114+
115+
let loader = ComponentSourceLoader::new(&self.wasm_loader);
116+
117+
let wasm = spin_compose::compose(&loader, &component).await.with_context(|| format!("Spin needed to compose dependencies for {} as part of target checking, but composition failed", component.id))?;
118+
119+
Ok(ComponentToValidate {
120+
id: component.id,
121+
source_description: source_description(component.source),
122+
wasm,
123+
})
99124
}
100125
}
101126

127+
struct ComponentSource<'a> {
128+
id: &'a str,
129+
source: &'a spin_manifest::schema::v2::ComponentSource,
130+
dependencies: WrappedComponentDependencies,
131+
}
132+
102133
struct ComponentSourceLoader<'a> {
103134
wasm_loader: &'a spin_loader::WasmLoader,
104135
}
@@ -135,7 +166,7 @@ impl<'a> spin_compose::ComponentSourceLoader for ComponentSourceLoader<'a> {
135166
}
136167

137168
// This exists only to thwart the orphan rule
138-
pub(crate) struct WrappedComponentDependency {
169+
struct WrappedComponentDependency {
139170
name: spin_serde::DependencyName,
140171
dependency: spin_manifest::schema::v2::ComponentDependency,
141172
}

0 commit comments

Comments
 (0)