@@ -52,6 +52,25 @@ impl AppManifest {
5252 }
5353 Ok ( ( ) )
5454 }
55+
56+ /// Whether any component in the application defines the given profile.
57+ /// Not every component defines every profile, and components intentionally
58+ /// fall back to the anonymouse profile if they are asked for a profile
59+ /// they don't define. So this can be used to detect that a user might have
60+ /// mistyped a profile (e.g. `spin up --profile deugb`).
61+ pub fn ensure_profile ( & self , profile : Option < & str > ) -> anyhow:: Result < ( ) > {
62+ let Some ( p) = profile else {
63+ return Ok ( ( ) ) ;
64+ } ;
65+
66+ let is_defined = self . components . values ( ) . any ( |c| c. profile . contains_key ( p) ) ;
67+
68+ if is_defined {
69+ Ok ( ( ) )
70+ } else {
71+ Err ( anyhow ! ( "Profile {p} is not defined in this application" ) )
72+ }
73+ }
5574}
5675
5776/// App details
@@ -397,31 +416,89 @@ pub struct Component {
397416 pub dependencies : ComponentDependencies ,
398417 /// TODO: profile docs
399418 #[ serde( default , skip_serializing_if = "Map::is_empty" ) ]
400- pub profile : Map < String , ComponentProfileUp > ,
419+ pub profile : Map < String , ComponentProfileOverride > ,
401420}
402421
403- /// Customisations for running a Spin component in a non-default profile.
422+ /// Customisations for a Spin component in a non-default profile.
404423#[ derive( Clone , Debug , Serialize , Deserialize , JsonSchema ) ]
405424#[ serde( deny_unknown_fields) ]
406- pub struct ComponentProfileUp {
425+ pub struct ComponentProfileOverride {
407426 /// The file, package, or URL containing the component Wasm binary.
408427 ///
409- /// Example: `source = "bin/cart.wasm"`
428+ /// Example: `source = "bin/debug/ cart.wasm"`
410429 ///
411430 /// Learn more: https://spinframework.dev/writing-apps#the-component-source
412- pub source : ComponentSource ,
431+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
432+ pub source : Option < ComponentSource > ,
433+
434+ /// The command or commands for building the component in non-default profiles.
435+ /// If a component has no special build instructions for a profile, the
436+ /// default build command is used.
437+ #[ serde( default , skip_serializing_if = "Option::is_none" ) ]
438+ pub build : Option < ComponentProfileBuildOverride > ,
439+ }
440+
441+ /// Customisations for a Spin component build in a non-default profile.
442+ #[ derive( Clone , Debug , Serialize , Deserialize , JsonSchema ) ]
443+ #[ serde( deny_unknown_fields) ]
444+ pub struct ComponentProfileBuildOverride {
445+ /// The command or commands to build the component in a named profile. If multiple commands
446+ /// are specified, they are run sequentially from left to right.
447+ ///
448+ /// Example: `build.command = "cargo build"`
449+ ///
450+ /// Learn more: https://spinframework.dev/build#setting-up-for-spin-build
451+ pub command : super :: common:: Commands ,
413452}
414453
415454impl Component {
416- /// TODO: docs! docs! docs!
417- pub fn source ( & self , profile : Option < impl AsRef < str > > ) -> ComponentSource {
418- let Some ( profile) = profile. as_ref ( ) else {
419- return self . source . clone ( ) ;
455+ fn profile ( & self , profile : Option < impl AsRef < str > > ) -> Option < & ComponentProfileOverride > {
456+ profile. and_then ( |p| self . profile . get ( p. as_ref ( ) ) )
457+ }
458+
459+ /// The commands to execute for the build
460+ pub fn build_commands ( & self , profile : Option < & str > ) -> Vec < & String > {
461+ let profile_build = self . profile ( profile) . and_then ( |o| o. build . as_ref ( ) ) ;
462+
463+ match profile_build {
464+ None => match & self . build {
465+ Some ( b) => b. command . as_vec ( ) ,
466+ None => vec ! [ ] ,
467+ } ,
468+ Some ( b) => b. command . as_vec ( ) ,
469+ }
470+ }
471+
472+ /// The build configuration for the component
473+ pub fn build_config ( & self , profile : Option < impl AsRef < str > > ) -> Option < ComponentBuildConfig > {
474+ let build_config = self . build . clone ( ) ;
475+ let build_profile = self . profile ( profile) . and_then ( |o| o. build . as_ref ( ) ) ;
476+
477+ let Some ( build_profile) = build_profile else {
478+ return build_config;
420479 } ;
421- let Some ( source) = self . profile . get ( profile. as_ref ( ) ) else {
422- return self . source . clone ( ) ;
480+
481+ let Some ( mut build_config) = build_config else {
482+ return Some ( ComponentBuildConfig {
483+ command : build_profile. command . clone ( ) ,
484+ workdir : None ,
485+ watch : vec ! [ ] ,
486+ } ) ;
423487 } ;
424- source. source . clone ( )
488+
489+ build_config. command = build_profile. command . clone ( ) ;
490+
491+ Some ( build_config)
492+ }
493+
494+ /// TODO: docs! docs! docs!
495+ pub fn source ( & self , profile : Option < impl AsRef < str > > ) -> & ComponentSource {
496+ let profile_source = self . profile ( profile) . and_then ( |o| o. source . as_ref ( ) ) ;
497+
498+ match profile_source {
499+ Some ( s) => s,
500+ None => & self . source ,
501+ }
425502 }
426503}
427504
0 commit comments