Skip to content

Commit

Permalink
Support string or mapping in recipes list
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 fb8710c commit c46d0f3
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 41 deletions.
2 changes: 1 addition & 1 deletion crates/spk-workspace/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl WorkspaceBuilder {
let mut glob_results = file
.recipes
.iter()
.map(|pattern| glob::glob(pattern.as_str()))
.map(|pattern| glob::glob(pattern.path.as_str()))
.flatten_ok()
.flatten_ok();
while let Some(path) = glob_results.next().transpose()? {
Expand Down
61 changes: 33 additions & 28 deletions crates/spk-workspace/src/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

use std::path::Path;

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

use crate::error::LoadWorkspaceFileError;
Expand All @@ -19,10 +19,10 @@ mod file_test;
/// and where to find data, usually loaded from a file on disk.
/// It must still be fully validated and loaded into a
/// [`super::Workspace`] to be operated on.
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Deserialize, Serialize)]
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd, Deserialize)]
pub struct WorkspaceFile {
#[serde(default, skip_serializing_if = "Vec::is_empty", with = "glob_from_str")]
pub recipes: Vec<glob::Pattern>,
#[serde(default)]
pub recipes: Vec<RecipesItem>,
}

impl WorkspaceFile {
Expand Down Expand Up @@ -72,43 +72,48 @@ impl WorkspaceFile {
}
}

mod glob_from_str {
use serde::{Deserializer, Serialize, Serializer};

pub fn serialize<S>(patterns: &Vec<glob::Pattern>, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let patterns: Vec<_> = patterns.iter().map(|p| p.as_str()).collect();
patterns.serialize(serializer)
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, Ord, PartialOrd)]
pub struct RecipesItem {
pub path: glob::Pattern,
}

pub fn deserialize<'de, D>(deserializer: D) -> Result<Vec<glob::Pattern>, D::Error>
impl<'de> serde::de::Deserialize<'de> for RecipesItem {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
D: serde::de::Deserializer<'de>,
{
/// Visits a serialized string, decoding it as a digest
struct PatternVisitor;
struct RecipeCollectorVisitor;

impl<'de> serde::de::Visitor<'de> for PatternVisitor {
type Value = Vec<glob::Pattern>;
impl<'de> serde::de::Visitor<'de> for RecipeCollectorVisitor {
type Value = RecipesItem;

fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
formatter.write_str("a glob pattern")
}

fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
fn visit_str<E>(self, v: &str) -> Result<RecipesItem, E>
where
A: serde::de::SeqAccess<'de>,
E: serde::de::Error,
{
let mut patterns = Vec::with_capacity(seq.size_hint().unwrap_or(0));
while let Some(pattern) = seq.next_element()? {
let pattern = glob::Pattern::new(pattern).map_err(serde::de::Error::custom)?;
patterns.push(pattern);
let path = glob::Pattern::new(v).map_err(serde::de::Error::custom)?;
Ok(RecipesItem { path })
}

fn visit_map<A>(self, map: A) -> Result<Self::Value, A::Error>
where
A: serde::de::MapAccess<'de>,
{
#[derive(Deserialize)]
struct RawRecipeItem {
path: String,
}
Ok(patterns)

let raw_recipe =
RawRecipeItem::deserialize(serde::de::value::MapAccessDeserializer::new(map))?;
self.visit_str(&raw_recipe.path)
}
}
deserializer.deserialize_seq(PatternVisitor)

deserializer.deserialize_any(RecipeCollectorVisitor)
}
}
28 changes: 16 additions & 12 deletions crates/spk-workspace/src/file_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,22 @@ recipes: []
"#;

#[rstest]
fn test_workspace_roundtrip() {
let workspace = WorkspaceFile {
recipes: vec![
glob::Pattern::new("packages/*/*.spk.yml").unwrap(),
glob::Pattern::new("platforms/*/*.spk.yml").unwrap(),
],
};

let serialized = serde_json::to_string(&workspace).unwrap();
let deserialized: WorkspaceFile = serde_json::from_str(&serialized).unwrap();

assert_eq!(workspace, deserialized);
#[case(
r#"
api: v0/workspace
recipes:
- packages/**/*.spk.yaml
- path: packages/python/python2.spk.yaml
versions: [2.7.18]
- path: packages/python/python3.spk.yaml
versions:
- '3.7.{0..17}'
- '3.8.{0..20}'
- '3.9.{0..21}'
"#
)]
fn test_workspace_from_yaml(#[case] yaml: &str) {
let _deserialized: WorkspaceFile = serde_yaml::from_str(yaml).unwrap();
}

#[rstest]
Expand Down

0 comments on commit c46d0f3

Please sign in to comment.