diff --git a/crates/spk-build/src/build/binary.rs b/crates/spk-build/src/build/binary.rs index 229e8d871..0a5f652ed 100644 --- a/crates/spk-build/src/build/binary.rs +++ b/crates/spk-build/src/build/binary.rs @@ -776,7 +776,7 @@ where let manifests = split_manifest_by_component( input.package.ident(), &collected_layer, - input.package.components(), + &input.package.components(), )?; let mut components = HashMap::new(); for (component, manifest) in manifests { diff --git a/crates/spk-build/src/validation/inherit_requirements.rs b/crates/spk-build/src/validation/inherit_requirements.rs index dc14532c7..34f52c581 100644 --- a/crates/spk-build/src/validation/inherit_requirements.rs +++ b/crates/spk-build/src/validation/inherit_requirements.rs @@ -56,7 +56,7 @@ impl super::Validator for InheritRequirementsValidator<'_> { for component in all_components { let downstream_build = solved_request .spec - .downstream_build_requirements([component]); + .downstream_build_requirements([&component]); for request in downstream_build.iter() { let compat = build_requirements.contains_request(request); let status = match (self.kind, compat) { @@ -86,7 +86,7 @@ impl super::Validator for InheritRequirementsValidator<'_> { } let downstream_runtime = solved_request .spec - .downstream_runtime_requirements([component]); + .downstream_runtime_requirements([&component]); for request in downstream_runtime.iter() { let status = match (self.kind, runtime_requirements.contains_request(request)) { (RuleKind::Allow, Compatibility::Compatible) diff --git a/crates/spk-schema/crates/foundation/src/spec_ops/versioned.rs b/crates/spk-schema/crates/foundation/src/spec_ops/versioned.rs index 7b3d1ac42..71e05d4b7 100644 --- a/crates/spk-schema/crates/foundation/src/spec_ops/versioned.rs +++ b/crates/spk-schema/crates/foundation/src/spec_ops/versioned.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // https://github.com/spkenv/spk +use std::borrow::Cow; use std::sync::Arc; use crate::version::{Compat, Compatibility, Version}; @@ -25,7 +26,7 @@ pub trait WithVersion { #[enum_dispatch::enum_dispatch] pub trait Versioned: HasVersion { /// The compatibility guaranteed by this items versioning scheme - fn compat(&self) -> &Compat; + fn compat(&self) -> Cow<'_, Compat>; /// Check if this item's version is api-compatible with the provided one fn is_api_compatible(&self, base: &Version) -> Compatibility { @@ -47,7 +48,7 @@ impl HasVersion for Arc { } impl Versioned for Arc { - fn compat(&self) -> &Compat { + fn compat(&self) -> Cow<'_, Compat> { (**self).compat() } @@ -67,7 +68,7 @@ impl HasVersion for Box { } impl Versioned for Box { - fn compat(&self) -> &Compat { + fn compat(&self) -> Cow<'_, Compat> { (**self).compat() } @@ -87,7 +88,7 @@ impl HasVersion for &T { } impl Versioned for &T { - fn compat(&self) -> &Compat { + fn compat(&self) -> Cow<'_, Compat> { (**self).compat() } diff --git a/crates/spk-schema/src/install_spec.rs b/crates/spk-schema/src/install_spec.rs index 26f4b90e7..05860420a 100644 --- a/crates/spk-schema/src/install_spec.rs +++ b/crates/spk-schema/src/install_spec.rs @@ -130,7 +130,8 @@ where } } - let new_components = target_embedded_package.components().names(); + let new_component_specs = target_embedded_package.components(); + let new_components = new_component_specs.names(); if new_components.is_empty() { // Empty components set? The embedded package is not allowed // to have an empty component set. diff --git a/crates/spk-schema/src/package.rs b/crates/spk-schema/src/package.rs index 24ef17c27..ec64ca0c9 100644 --- a/crates/spk-schema/src/package.rs +++ b/crates/spk-schema/src/package.rs @@ -88,16 +88,16 @@ forward_to_impl!(BuildOptions, { /// Access to the components defined by a package. pub trait Components { - type ComponentSpecT; + type ComponentSpecT: Clone; /// The components defined by this package - fn components(&self) -> &super::ComponentSpecList; + fn components(&self) -> Cow<'_, super::ComponentSpecList>; } forward_to_impl!(Components, { type ComponentSpecT = T::ComponentSpecT; - fn components(&self) -> &super::ComponentSpecList { + fn components(&self) -> Cow<'_, super::ComponentSpecList> { (**self).components() } }); @@ -154,7 +154,7 @@ pub trait Package: + Send { type Package; - type EmbeddedPackage; + type EmbeddedPackage: Clone; /// The full identifier for this package /// @@ -172,7 +172,7 @@ pub trait Package: fn sources(&self) -> &Vec; /// The packages that are embedded within this one - fn embedded(&self) -> &super::EmbeddedPackagesList; + fn embedded(&self) -> Cow<'_, super::EmbeddedPackagesList>; /// The packages that are embedded within this one. /// @@ -185,7 +185,7 @@ pub trait Package: ) -> std::result::Result)>, &str>; /// The list of build options for this package - fn get_build_options(&self) -> &Vec; + fn get_build_options(&self) -> Cow<'_, [Opt]>; /// Identify the requirements for a build of this package. fn get_build_requirements(&self) -> crate::Result>>; @@ -266,7 +266,7 @@ forward_to_impl!(Package, { (**self).sources() } - fn embedded(&self) -> &super::EmbeddedPackagesList { + fn embedded(&self) -> Cow<'_, super::EmbeddedPackagesList> { (**self).embedded() } @@ -276,7 +276,7 @@ forward_to_impl!(Package, { (**self).embedded_as_packages() } - fn get_build_options(&self) -> &Vec { + fn get_build_options(&self) -> Cow<'_, [Opt]> { (**self).get_build_options() } diff --git a/crates/spk-schema/src/spec.rs b/crates/spk-schema/src/spec.rs index 330b0e07e..5d78195f7 100644 --- a/crates/spk-schema/src/spec.rs +++ b/crates/spk-schema/src/spec.rs @@ -433,7 +433,7 @@ impl RuntimeEnvironment for SpecRecipe { } impl Versioned for SpecRecipe { - fn compat(&self) -> &Compat { + fn compat(&self) -> Cow<'_, Compat> { each_variant!(self, spec, spec.compat()) } } @@ -638,7 +638,7 @@ pub enum Spec { impl Components for Spec { type ComponentSpecT = ComponentSpec; - fn components(&self) -> &super::ComponentSpecList { + fn components(&self) -> Cow<'_, super::ComponentSpecList> { match self { Spec::V0Package(spec) => spec.components(), } @@ -702,7 +702,7 @@ impl RuntimeEnvironment for Spec { } impl Versioned for Spec { - fn compat(&self) -> &Compat { + fn compat(&self) -> Cow<'_, Compat> { match self { Spec::V0Package(spec) => spec.compat(), } @@ -738,7 +738,7 @@ impl Package for Spec { } } - fn embedded(&self) -> &super::EmbeddedPackagesList { + fn embedded(&self) -> Cow<'_, super::EmbeddedPackagesList> { match self { Spec::V0Package(spec) => spec.embedded(), } @@ -754,7 +754,7 @@ impl Package for Spec { } } - fn get_build_options(&self) -> &Vec { + fn get_build_options(&self) -> Cow<'_, [Opt]> { match self { Spec::V0Package(spec) => spec.get_build_options(), } diff --git a/crates/spk-schema/src/v0/embedded_package_spec.rs b/crates/spk-schema/src/v0/embedded_package_spec.rs index 6051a73cb..bb59ab3ce 100644 --- a/crates/spk-schema/src/v0/embedded_package_spec.rs +++ b/crates/spk-schema/src/v0/embedded_package_spec.rs @@ -243,8 +243,8 @@ impl BuildOptions for EmbeddedPackageSpec { impl Components for EmbeddedPackageSpec { type ComponentSpecT = ComponentSpec; - fn components(&self) -> &ComponentSpecList { - &self.install.components + fn components(&self) -> Cow<'_, ComponentSpecList> { + Cow::Borrowed(&self.install.components) } } @@ -313,8 +313,8 @@ impl OptionValues for EmbeddedPackageSpec { } impl Versioned for EmbeddedPackageSpec { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } diff --git a/crates/spk-schema/src/v0/embedded_recipe_install_spec_test.rs b/crates/spk-schema/src/v0/embedded_recipe_install_spec_test.rs index 133eb615b..e4ae47421 100644 --- a/crates/spk-schema/src/v0/embedded_recipe_install_spec_test.rs +++ b/crates/spk-schema/src/v0/embedded_recipe_install_spec_test.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // https://github.com/spkenv/spk +use std::borrow::Cow; use std::collections::HashMap; use rstest::rstest; @@ -72,8 +73,8 @@ fn test_render_all_pins_renders_requirements_in_components() { } impl Versioned for FakeBuild { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } diff --git a/crates/spk-schema/src/v0/embedded_recipe_spec.rs b/crates/spk-schema/src/v0/embedded_recipe_spec.rs index 4aac7a955..f0a65819e 100644 --- a/crates/spk-schema/src/v0/embedded_recipe_spec.rs +++ b/crates/spk-schema/src/v0/embedded_recipe_spec.rs @@ -2,6 +2,8 @@ // SPDX-License-Identifier: Apache-2.0 // https://github.com/spkenv/spk +use std::borrow::Cow; + use serde::{Deserialize, Serialize}; use spk_schema_foundation::IsDefault; use spk_schema_foundation::ident::{AnyIdent, AsVersionIdent, VersionIdent}; @@ -102,8 +104,8 @@ impl AsVersionIdent for EmbeddedRecipeSpec { impl Components for EmbeddedRecipeSpec { type ComponentSpecT = RecipeComponentSpec; - fn components(&self) -> &ComponentSpecList { - &self.install.components + fn components(&self) -> Cow<'_, ComponentSpecList> { + Cow::Borrowed(&self.install.components) } } @@ -138,8 +140,8 @@ impl Named for EmbeddedRecipeSpec { } impl Versioned for EmbeddedRecipeSpec { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } diff --git a/crates/spk-schema/src/v0/package_spec.rs b/crates/spk-schema/src/v0/package_spec.rs index ec165f852..15f249181 100644 --- a/crates/spk-schema/src/v0/package_spec.rs +++ b/crates/spk-schema/src/v0/package_spec.rs @@ -296,8 +296,8 @@ impl BuildOptions for PackageSpec { impl Components for PackageSpec { type ComponentSpecT = ComponentSpec; - fn components(&self) -> &ComponentSpecList { - &self.install.components + fn components(&self) -> Cow<'_, ComponentSpecList> { + Cow::Borrowed(&self.install.components) } } @@ -350,8 +350,8 @@ impl RuntimeEnvironment for PackageSpec { } impl Versioned for PackageSpec { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } @@ -407,8 +407,8 @@ impl Package for PackageSpec { &self.sources } - fn embedded(&self) -> &EmbeddedPackagesList { - &self.install.embedded + fn embedded(&self) -> Cow<'_, EmbeddedPackagesList> { + Cow::Borrowed(&self.install.embedded) } fn embedded_as_packages( @@ -422,8 +422,8 @@ impl Package for PackageSpec { .collect()) } - fn get_build_options(&self) -> &Vec { - &self.build.options + fn get_build_options(&self) -> Cow<'_, [Opt]> { + Cow::from(&self.build.options) } fn get_build_requirements(&self) -> crate::Result>> { @@ -456,7 +456,7 @@ impl Package for PackageSpec { Ok(Cow::Owned(requests)) } - fn runtime_requirements(&self) -> Cow<'_, crate::RequirementsList> { + fn runtime_requirements(&self) -> Cow<'_, RequirementsList> { Cow::Borrowed(&self.install_requirements_with_options) } diff --git a/crates/spk-schema/src/v0/platform.rs b/crates/spk-schema/src/v0/platform.rs index 84de376e6..5394fc503 100644 --- a/crates/spk-schema/src/v0/platform.rs +++ b/crates/spk-schema/src/v0/platform.rs @@ -175,8 +175,8 @@ impl RuntimeEnvironment for Platform { } impl Versioned for Platform { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } diff --git a/crates/spk-schema/src/v0/recipe_install_spec.rs b/crates/spk-schema/src/v0/recipe_install_spec.rs index 9e3c463a9..f571aed8e 100644 --- a/crates/spk-schema/src/v0/recipe_install_spec.rs +++ b/crates/spk-schema/src/v0/recipe_install_spec.rs @@ -152,7 +152,8 @@ impl From for RecipeInstallSpec { } } - let new_components = target_embedded_package.components().names(); + let new_component_specs = target_embedded_package.components(); + let new_components = new_component_specs.names(); if new_components.is_empty() { // Empty components set? The embedded package is not allowed // to have an empty component set. diff --git a/crates/spk-schema/src/v0/recipe_install_spec_test.rs b/crates/spk-schema/src/v0/recipe_install_spec_test.rs index fb7897a07..96b2119d2 100644 --- a/crates/spk-schema/src/v0/recipe_install_spec_test.rs +++ b/crates/spk-schema/src/v0/recipe_install_spec_test.rs @@ -2,6 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // https://github.com/spkenv/spk +use std::borrow::Cow; use std::collections::{HashMap, HashSet}; use std::str::FromStr; @@ -73,8 +74,8 @@ fn test_render_all_pins_renders_requirements_in_components() { } impl Versioned for FakeBuild { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } diff --git a/crates/spk-schema/src/v0/recipe_spec.rs b/crates/spk-schema/src/v0/recipe_spec.rs index d41fa895a..47cb20cb1 100644 --- a/crates/spk-schema/src/v0/recipe_spec.rs +++ b/crates/spk-schema/src/v0/recipe_spec.rs @@ -151,8 +151,8 @@ impl RuntimeEnvironment for RecipeSpec { } impl Versioned for RecipeSpec { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } diff --git a/crates/spk-schema/src/v1/platform.rs b/crates/spk-schema/src/v1/platform.rs index 23a2d80b3..0946e92af 100644 --- a/crates/spk-schema/src/v1/platform.rs +++ b/crates/spk-schema/src/v1/platform.rs @@ -63,7 +63,7 @@ pub struct Platform { pub platform: VersionIdent, #[serde(default, skip_serializing_if = "IsDefault::is_default")] pub meta: Meta, - #[serde(default, skip_serializing_if = "IsDefault::is_default")] + #[serde(default, skip_serializing_if = "Compat::is_default")] pub compat: Compat, #[serde(default, skip_serializing_if = "is_false")] pub deprecated: bool, @@ -117,8 +117,8 @@ impl RuntimeEnvironment for Platform { } impl Versioned for Platform { - fn compat(&self) -> &Compat { - &self.compat + fn compat(&self) -> Cow<'_, Compat> { + Cow::Borrowed(&self.compat) } } @@ -277,7 +277,8 @@ fn apply_inherit_from_base_component( for requirement in base.runtime_requirements().iter() { cmpt.requirements.insert_or_replace(requirement.clone()); } - let Some(base_cmpt) = base.components().get(inherit) else { + let component_specs = base.components(); + let Some(base_cmpt) = component_specs.get(inherit) else { return; }; for requirement in base_cmpt.requirements().iter() { diff --git a/crates/spk-solve/crates/graph/src/graph.rs b/crates/spk-solve/crates/graph/src/graph.rs index 269241968..55c4c8d87 100644 --- a/crates/spk-solve/crates/graph/src/graph.rs +++ b/crates/spk-solve/crates/graph/src/graph.rs @@ -298,10 +298,10 @@ impl<'state> DecisionBuilder<'state, '_> { let requested_by = RequestedBy::PackageBuild(requester_ident.clone()); changes .extend(self.requirements_to_changes(&spec.runtime_requirements(), &requested_by)); - changes.extend(self.components_to_changes(spec.components(), requester_ident)); + changes.extend(self.components_to_changes(&spec.components(), requester_ident)); changes.extend(self.embedded_to_changes( - spec.embedded(), - spec.components(), + &spec.embedded(), + &spec.components(), requester_ident, )?); changes.push(Self::options_to_change(spec)); @@ -359,10 +359,10 @@ impl<'state> DecisionBuilder<'state, '_> { changes.extend(self.pkg_request_to_changes(&pkg_request_with_options)); } changes.extend(self.requirements_to_changes(&spec.runtime_requirements(), &requested_by)); - changes.extend(self.components_to_changes(spec.components(), requester_ident)); + changes.extend(self.components_to_changes(&spec.components(), requester_ident)); changes.extend(self.embedded_to_changes( - spec.embedded(), - spec.components(), + &spec.embedded(), + &spec.components(), requester_ident, )?); changes.push(Self::options_to_change(&spec)); diff --git a/crates/spk-solve/crates/solution/src/solution.rs b/crates/spk-solve/crates/solution/src/solution.rs index b1d28d7c3..3d42426fd 100644 --- a/crates/spk-solve/crates/solution/src/solution.rs +++ b/crates/spk-solve/crates/solution/src/solution.rs @@ -135,13 +135,22 @@ impl SolvedRequest { } /// The expanded list of components selected for this resolved item - pub fn selected_components(&self) -> BTreeSet<&Component> { - let mut installed_components: BTreeSet<_> = self.request.pkg.components.iter().collect(); + pub fn selected_components(&self) -> BTreeSet { + let mut installed_components = + BTreeSet::from_iter(self.request.pkg.components.iter().cloned()); + if installed_components.is_empty() || installed_components.remove(&Component::All) { if let PackageSource::Repository { components, .. } = &self.source { - installed_components.extend(components.keys()); + installed_components.extend(components.keys().cloned()); } else { - installed_components.extend(self.spec.components().names()); + installed_components.extend( + self.spec + .components() + .names() + .iter() + .map(|c| (*c).clone()) + .collect::>(), + ); } } installed_components @@ -151,11 +160,7 @@ impl SolvedRequest { pub fn format_as_installed_package(&self) -> String { let mut installed = PkgRequest::from_ident(self.spec.ident().to_any_ident(), RequestedBy::DoesNotMatter); - installed.pkg.components = self - .selected_components() - .into_iter() - .map(ToOwned::to_owned) - .collect(); + installed.pkg.components = self.selected_components(); let repo_name = if let PackageSource::Repository { repo, .. } = &self.source { Some(repo.name().to_owned()) diff --git a/crates/spk-solve/crates/validation/src/validators/components.rs b/crates/spk-solve/crates/validation/src/validators/components.rs index ba813fac3..116707873 100644 --- a/crates/spk-solve/crates/validation/src/validators/components.rs +++ b/crates/spk-solve/crates/validation/src/validators/components.rs @@ -105,11 +105,12 @@ impl ComponentsValidator { { // Do the components available in the package match those // required by the request? + let pkg_components = package.components(); let available_components: std::collections::HashSet<_> = match source { PackageSource::Repository { components, .. } => components.keys().collect(), - PackageSource::BuildFromSource { .. } => package.components().names(), + PackageSource::BuildFromSource { .. } => pkg_components.names(), PackageSource::Embedded { components, .. } => components.iter().collect(), - PackageSource::SpkInternalTest => package.components().names(), + PackageSource::SpkInternalTest => pkg_components.names(), }; let required_components = package diff --git a/crates/spk-solve/crates/validation/src/validators/pkg_requirements.rs b/crates/spk-solve/crates/validation/src/validators/pkg_requirements.rs index 7d29af555..53fddd48d 100644 --- a/crates/spk-solve/crates/validation/src/validators/pkg_requirements.rs +++ b/crates/spk-solve/crates/validation/src/validators/pkg_requirements.rs @@ -79,6 +79,7 @@ impl PkgRequirementsValidator { let mut was_embedded = None; + let comp_specs; let (resolved, provided_components) = match state.get_current_resolve(&request.pkg.name) { Ok((spec, source, _)) => match source { PackageSource::Repository { components, .. } => (spec, components.keys().collect()), @@ -87,7 +88,8 @@ impl PkgRequirementsValidator { (spec, components.iter().collect()) } PackageSource::BuildFromSource { .. } | PackageSource::SpkInternalTest => { - (spec, spec.components().names()) + comp_specs = spec.components(); + (spec, comp_specs.names()) } }, Err(spk_solve_graph::GetCurrentResolveError::PackageNotResolved(_)) => { diff --git a/crates/spk-solve/src/solvers/resolvo/mod.rs b/crates/spk-solve/src/solvers/resolvo/mod.rs index cc0526bc4..edea69371 100644 --- a/crates/spk-solve/src/solvers/resolvo/mod.rs +++ b/crates/spk-solve/src/solvers/resolvo/mod.rs @@ -284,7 +284,7 @@ impl Solver { let package = repo.read_package(ident.target()).await?; let rendered_version = package.compat().render(package.version()); solution_options.insert(package.name().as_opt_name().to_owned(), rendered_version); - for option in package.get_build_options() { + for option in package.get_build_options().iter() { match option { spk_schema::Opt::Pkg(pkg_opt) => { if let Some(value) = pkg_opt.get_value(None) { @@ -328,7 +328,7 @@ impl Solver { PackageSource::Repository { repo: Arc::clone(repo), // XXX: Why is this needed? - components: repo.read_components(ident.target()).await?, + components: { repo.read_components(ident.target()).await? }, } } spk_schema::ident_build::Build::Embedded(embedded_source) => { diff --git a/crates/spk-solve/src/solvers/resolvo/pkg_request_version_set.rs b/crates/spk-solve/src/solvers/resolvo/pkg_request_version_set.rs index 733f83f1d..82800eb32 100644 --- a/crates/spk-solve/src/solvers/resolvo/pkg_request_version_set.rs +++ b/crates/spk-solve/src/solvers/resolvo/pkg_request_version_set.rs @@ -170,7 +170,7 @@ impl LocatedBuildIdentWithComponent { // An intra-package request should satisfy any required options. let mut propagated_required_vars = PkgRequestOptions::default(); - for build_option in pkg.get_build_options() { + for build_option in pkg.get_build_options().iter() { let Opt::Var(var_opt) = build_option else { continue; }; diff --git a/crates/spk-solve/src/solvers/resolvo/spk_provider.rs b/crates/spk-solve/src/solvers/resolvo/spk_provider.rs index 2af138b0f..e0a478825 100644 --- a/crates/spk-solve/src/solvers/resolvo/spk_provider.rs +++ b/crates/spk-solve/src/solvers/resolvo/spk_provider.rs @@ -1665,7 +1665,7 @@ impl DependencyProvider for SpkProvider { ); } } - for option in package.get_build_options() { + for option in package.get_build_options().iter() { let Opt::Var(var_opt) = option else { continue; };