@@ -4,37 +4,119 @@ use std::{collections::BTreeMap, path::Path};
4
4
5
5
use spin_manifest:: { schema:: v2, ManifestVersion } ;
6
6
7
+ pub enum ManifestBuildInfo {
8
+ Loadable {
9
+ components : Vec < ComponentBuildInfo > ,
10
+ deployment_targets : Vec < spin_manifest:: schema:: v2:: TargetEnvironmentRef > ,
11
+ manifest : spin_manifest:: schema:: v2:: AppManifest ,
12
+ } ,
13
+ Unloadable {
14
+ components : Vec < ComponentBuildInfo > ,
15
+ has_deployment_targets : bool ,
16
+ load_error : spin_manifest:: Error ,
17
+ } ,
18
+ }
19
+
20
+ impl ManifestBuildInfo {
21
+ pub fn components ( & self ) -> Vec < ComponentBuildInfo > {
22
+ match self {
23
+ Self :: Loadable { components, .. } => components. clone ( ) ,
24
+ Self :: Unloadable { components, .. } => components. clone ( ) ,
25
+ }
26
+ }
27
+
28
+ pub fn load_error ( & self ) -> Option < & spin_manifest:: Error > {
29
+ match self {
30
+ Self :: Loadable { .. } => None ,
31
+ Self :: Unloadable { load_error, .. } => Some ( load_error) ,
32
+ }
33
+ }
34
+
35
+ pub fn deployment_targets ( & self ) -> & [ spin_manifest:: schema:: v2:: TargetEnvironmentRef ] {
36
+ match self {
37
+ Self :: Loadable {
38
+ deployment_targets, ..
39
+ } => deployment_targets,
40
+ Self :: Unloadable { .. } => & [ ] ,
41
+ }
42
+ }
43
+
44
+ pub fn has_deployment_targets ( & self ) -> bool {
45
+ match self {
46
+ Self :: Loadable {
47
+ deployment_targets, ..
48
+ } => !deployment_targets. is_empty ( ) ,
49
+ Self :: Unloadable {
50
+ has_deployment_targets,
51
+ ..
52
+ } => * has_deployment_targets,
53
+ }
54
+ }
55
+
56
+ pub fn manifest ( & self ) -> Option < & spin_manifest:: schema:: v2:: AppManifest > {
57
+ match self {
58
+ Self :: Loadable { manifest, .. } => Some ( manifest) ,
59
+ Self :: Unloadable { .. } => None ,
60
+ }
61
+ }
62
+ }
63
+
7
64
/// Returns a map of component IDs to [`v2::ComponentBuildConfig`]s for the
8
65
/// given (v1 or v2) manifest path. If the manifest cannot be loaded, the
9
66
/// function attempts fallback: if fallback succeeds, result is Ok but the load error
10
67
/// is also returned via the second part of the return value tuple.
11
- pub async fn component_build_configs (
12
- manifest_file : impl AsRef < Path > ,
13
- ) -> Result < ( Vec < ComponentBuildInfo > , Option < spin_manifest:: Error > ) > {
68
+ pub async fn component_build_configs ( manifest_file : impl AsRef < Path > ) -> Result < ManifestBuildInfo > {
14
69
let manifest = spin_manifest:: manifest_from_file ( & manifest_file) ;
15
70
match manifest {
16
- Ok ( manifest) => Ok ( ( build_configs_from_manifest ( manifest) , None ) ) ,
17
- Err ( e) => fallback_load_build_configs ( & manifest_file)
18
- . await
19
- . map ( |bc| ( bc, Some ( e) ) ) ,
71
+ Ok ( mut manifest) => {
72
+ spin_manifest:: normalize:: normalize_manifest ( & mut manifest) ;
73
+ let components = build_configs_from_manifest ( & manifest) ;
74
+ let deployment_targets = deployment_targets_from_manifest ( & manifest) ;
75
+ Ok ( ManifestBuildInfo :: Loadable {
76
+ components,
77
+ deployment_targets,
78
+ manifest,
79
+ } )
80
+ }
81
+ Err ( load_error) => {
82
+ // The manifest didn't load, but the problem might not be build-affecting.
83
+ // Try to fall back by parsing out only the bits we need. And if something
84
+ // goes wrong with the fallback, give up and return the original manifest load
85
+ // error.
86
+ let Ok ( components) = fallback_load_build_configs ( & manifest_file) . await else {
87
+ return Err ( load_error. into ( ) ) ;
88
+ } ;
89
+ let Ok ( has_deployment_targets) = has_deployment_targets ( & manifest_file) . await else {
90
+ return Err ( load_error. into ( ) ) ;
91
+ } ;
92
+ Ok ( ManifestBuildInfo :: Unloadable {
93
+ components,
94
+ has_deployment_targets,
95
+ load_error,
96
+ } )
97
+ }
20
98
}
21
99
}
22
100
23
101
fn build_configs_from_manifest (
24
- mut manifest : spin_manifest:: schema:: v2:: AppManifest ,
102
+ manifest : & spin_manifest:: schema:: v2:: AppManifest ,
25
103
) -> Vec < ComponentBuildInfo > {
26
- spin_manifest:: normalize:: normalize_manifest ( & mut manifest) ;
27
-
28
104
manifest
29
105
. components
30
- . into_iter ( )
106
+ . iter ( )
31
107
. map ( |( id, c) | ComponentBuildInfo {
32
108
id : id. to_string ( ) ,
33
- build : c. build ,
109
+ build : c. build . clone ( ) ,
34
110
} )
35
111
. collect ( )
36
112
}
37
113
114
+ fn deployment_targets_from_manifest (
115
+ manifest : & spin_manifest:: schema:: v2:: AppManifest ,
116
+ ) -> Vec < spin_manifest:: schema:: v2:: TargetEnvironmentRef > {
117
+ manifest. application . targets . clone ( )
118
+ }
119
+
38
120
async fn fallback_load_build_configs (
39
121
manifest_file : impl AsRef < Path > ,
40
122
) -> Result < Vec < ComponentBuildInfo > > {
@@ -57,7 +139,23 @@ async fn fallback_load_build_configs(
57
139
} )
58
140
}
59
141
60
- #[ derive( Deserialize ) ]
142
+ async fn has_deployment_targets ( manifest_file : impl AsRef < Path > ) -> Result < bool > {
143
+ let manifest_text = tokio:: fs:: read_to_string ( manifest_file) . await ?;
144
+ Ok ( match ManifestVersion :: detect ( & manifest_text) ? {
145
+ ManifestVersion :: V1 => false ,
146
+ ManifestVersion :: V2 => {
147
+ let table: toml:: value:: Table = toml:: from_str ( & manifest_text) ?;
148
+ table
149
+ . get ( "application" )
150
+ . and_then ( |a| a. as_table ( ) )
151
+ . and_then ( |t| t. get ( "targets" ) )
152
+ . and_then ( |arr| arr. as_array ( ) )
153
+ . is_some_and ( |arr| !arr. is_empty ( ) )
154
+ }
155
+ } )
156
+ }
157
+
158
+ #[ derive( Clone , Deserialize ) ]
61
159
pub struct ComponentBuildInfo {
62
160
#[ serde( default ) ]
63
161
pub id : String ,
0 commit comments