Skip to content

Commit

Permalink
Add logic to load spec templates in a workspace
Browse files Browse the repository at this point in the history
Signed-off-by: Ryan Bottriell <[email protected]>
  • Loading branch information
rydrman committed Jan 14, 2025
1 parent f9fb300 commit fb8710c
Show file tree
Hide file tree
Showing 6 changed files with 134 additions and 3 deletions.
3 changes: 2 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion crates/spk-workspace/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,12 @@ sentry = ["spk-solve/sentry"]
[dependencies]
serde = { workspace = true, features = ["derive"] }
glob = { workspace = true }
itertools = { workspace = true }
spk-solve = { workspace = true }
thiserror = { workspace = true }
miette = { workspace = true }
serde_yaml = { workspace = true }
spk-schema-foundation = { workspace = true }
spk-schema = { workspace = true }
format_serde_error = { workspace = true }

[dev-dependencies]
Expand Down
98 changes: 98 additions & 0 deletions crates/spk-workspace/src/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Contributors to the SPK project.
// SPDX-License-Identifier: Apache-2.0
// https://github.com/spkenv/spk

use std::collections::HashMap;

use itertools::Itertools;
use spk_schema::TemplateExt;

mod error {
pub use crate::error::LoadWorkspaceFileError;

#[derive(thiserror::Error, miette::Diagnostic, Debug)]
pub enum FromPathError {
#[error(transparent)]
#[diagnostic(forward(0))]
LoadWorkspaceFileError(#[from] LoadWorkspaceFileError),
#[error(transparent)]
#[diagnostic(forward(0))]
FromFileError(#[from] FromFileError),
}

#[derive(thiserror::Error, miette::Diagnostic, Debug)]
pub enum FromFileError {
#[error("Invalid glob pattern")]
PatternError(#[from] glob::PatternError),
#[error("Failed to process glob pattern")]
GlobError(#[from] glob::GlobError),
}

#[derive(thiserror::Error, miette::Diagnostic, Debug)]
pub enum BuildError {
#[error("Failed to load spec from workspace: {file:?}")]
TemplateLoadError {
file: std::path::PathBuf,
source: spk_schema::Error,
},
}
}

#[derive(Default)]
pub struct WorkspaceBuilder {
spec_files: Vec<std::path::PathBuf>,
}

impl WorkspaceBuilder {
/// Load all data from a workspace file discovered using the current directory.
pub fn load_from_current_dir(self) -> Result<Self, error::FromPathError> {
self.load_from_dir(".")
}

/// Load all data from a workspace file in the given directory.
pub fn load_from_dir(
self,
dir: impl AsRef<std::path::Path>,
) -> Result<Self, error::FromPathError> {
let file = crate::file::WorkspaceFile::discover(dir)?;
self.load_from_file(file)
.map_err(error::FromPathError::from)
}

/// Load all data from a workspace specification.
pub fn load_from_file(
mut self,
file: crate::file::WorkspaceFile,
) -> Result<Self, error::FromFileError> {
let mut glob_results = file
.recipes
.iter()
.map(|pattern| glob::glob(pattern.as_str()))
.flatten_ok()
.flatten_ok();
while let Some(path) = glob_results.next().transpose()? {
self = self.with_recipe_file(path);
}

Ok(self)
}

/// Add a recipe file to the workspace.
pub fn with_recipe_file(mut self, path: impl Into<std::path::PathBuf>) -> Self {
self.spec_files.push(path.into());
self
}

pub fn build(self) -> Result<super::Workspace, error::BuildError> {
let mut templates = HashMap::<_, Vec<_>>::new();
for file in self.spec_files {
let template = spk_schema::SpecTemplate::from_file(&file)
.map_err(|source| error::BuildError::TemplateLoadError { file, source })?;
templates
.entry(template.name().cloned())
.or_default()
.push(template);
}
Ok(crate::Workspace { templates })
}
}
2 changes: 1 addition & 1 deletion crates/spk-workspace/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use std::path::Path;

use serde::{Deserialize, Serialize};
use spk_schema_foundation::FromYaml;
use spk_schema::foundation::FromYaml;

use crate::error::LoadWorkspaceFileError;

Expand Down
2 changes: 2 additions & 0 deletions crates/spk-workspace/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// SPDX-License-Identifier: Apache-2.0
// https://github.com/spkenv/spk

pub mod builder;
pub mod error;
mod file;
mod workspace;

pub use file::WorkspaceFile;
pub use workspace::Workspace;
29 changes: 29 additions & 0 deletions crates/spk-workspace/src/workspace.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Contributors to the SPK project.
// SPDX-License-Identifier: Apache-2.0
// https://github.com/spkenv/spk

use std::collections::HashMap;

/// A collection of recipes and build targets.
///
/// Workspaces are used to define and build many recipes
/// together, helping to produce complete environments
/// with shared compatibility requirements. Workspaces
/// can be used to determine the number and order of
/// packages to be built in order to efficiently satisfy
/// and entire set of requirements for an environment.
pub struct Workspace {
/// Spec templates available in this workspace.
///
/// A workspace may contain multiple recipes for a single
/// package, and templates may also not have a package name
/// defined inside.
pub(crate) templates:
HashMap<Option<spk_schema::name::PkgNameBuf>, Vec<spk_schema::SpecTemplate>>,
}

impl Workspace {
pub fn builder() -> crate::builder::WorkspaceBuilder {
crate::builder::WorkspaceBuilder::default()
}
}

0 comments on commit fb8710c

Please sign in to comment.