diff --git a/src/common.rs b/src/common.rs index ca992cc..8bf767f 100644 --- a/src/common.rs +++ b/src/common.rs @@ -2,7 +2,7 @@ use std::collections::HashMap; -use serde::Deserialize; +use serde::{Deserialize, Deserializer}; /// `permissions` for a workflow, job, or step. #[derive(Deserialize, Debug, PartialEq)] @@ -116,48 +116,37 @@ pub type BoE = LoE; /// A "scalar or vector" type, for places in GitHub Actions where a /// key can have either a scalar value or an array of values. +/// +/// This only appears internally, as an intermediate type for `scalar_or_vector`. #[derive(Debug, Deserialize, PartialEq)] #[serde(untagged)] -pub enum SoV { +pub(crate) enum SoV { One(T), Many(Vec), } -impl Default for SoV { - fn default() -> Self { - SoV::Many(Default::default()) +impl Into> for SoV { + fn into(self) -> Vec { + match self { + Self::One(v) => vec![v], + Self::Many(vs) => vs, + } } } -impl SoV { - pub fn one(v: T) -> Self { - SoV::One(v) - } - - pub fn many(vs: Vec) -> Self { - SoV::Many(vs) - } -} - -impl<'a, T> IntoIterator for &'a SoV { - type Item = &'a T; - type IntoIter = std::slice::Iter<'a, T>; - - fn into_iter(self) -> Self::IntoIter { - let slice = match self { - SoV::One(v) => std::slice::from_ref(v), - SoV::Many(vs) => vs.as_slice(), - }; - - slice.iter() - } +pub(crate) fn scalar_or_vector<'de, D, T>(de: D) -> Result, D::Error> +where + D: Deserializer<'de>, + T: Deserialize<'de>, +{ + SoV::deserialize(de).map(Into::into) } #[cfg(test)] mod tests { use crate::common::{BasePermission, ExplicitPermissions}; - use super::{Permissions, SoV}; + use super::Permissions; #[test] fn test_permissions() { @@ -172,17 +161,4 @@ mod tests { Ok(_) )); } - - #[test] - fn test_sov_intoiterator() { - let sov_one = SoV::one("test".to_string()); - assert_eq!(sov_one.into_iter().collect::>(), vec!["test"]); - - let sov_many = SoV::many(vec!["test-1".to_string(), "test-2".to_string()]); - assert!(matches!(sov_many, SoV::Many(_))); - assert_eq!( - sov_many.into_iter().collect::>(), - vec!["test-1", "test-2"] - ); - } } diff --git a/src/dependabot/v2.rs b/src/dependabot/v2.rs index 9a3b3a3..d643f5a 100644 --- a/src/dependabot/v2.rs +++ b/src/dependabot/v2.rs @@ -8,8 +8,6 @@ use std::collections::{HashMap, HashSet}; use serde::Deserialize; -use crate::common::SoV; - /// A `dependabot.yml` configuration file. #[derive(Deserialize, Debug)] #[serde(rename_all = "kebab-case")] @@ -123,7 +121,8 @@ pub struct Update { // TODO: pull-request-branch-name #[serde(default)] pub rebase_strategy: RebaseStrategy, - pub registries: Option>, + #[serde(default, deserialize_with = "crate::common::scalar_or_vector")] + pub registries: Vec, #[serde(default)] pub reviewers: HashSet, pub schedule: Schedule, diff --git a/src/workflow/job.rs b/src/workflow/job.rs index e8b0bc7..4537157 100644 --- a/src/workflow/job.rs +++ b/src/workflow/job.rs @@ -5,7 +5,7 @@ use std::collections::HashMap; use serde::Deserialize; use serde_yaml::Value; -use crate::common::{BoE, Env, LoE, Permissions, SoV}; +use crate::common::{BoE, Env, LoE, Permissions}; use super::{Concurrency, Defaults}; @@ -17,8 +17,8 @@ pub struct NormalJob { pub name: Option, #[serde(default)] pub permissions: Permissions, - #[serde(default)] - pub needs: SoV, + #[serde(default, deserialize_with = "crate::common::scalar_or_vector")] + pub needs: Vec, pub r#if: Option, pub runs_on: RunsOn, pub environment: Option, @@ -41,9 +41,15 @@ pub struct NormalJob { #[derive(Debug, Deserialize, PartialEq)] #[serde(rename_all = "kebab-case", untagged)] pub enum RunsOn { - Target(SoV), - Group { group: String }, - Label { label: SoV }, + #[serde(deserialize_with = "crate::common::scalar_or_vector")] + Target(Vec), + Group { + group: String, + }, + #[serde(deserialize_with = "crate::common::scalar_or_vector")] + Label { + label: Vec, + }, } #[derive(Deserialize)] @@ -130,8 +136,8 @@ pub struct ReusableWorkflowCallJob { pub name: Option, #[serde(default)] pub permissions: Permissions, - #[serde(default)] - pub needs: SoV, + #[serde(default, deserialize_with = "crate::common::scalar_or_vector")] + pub needs: Vec, pub r#if: Option, pub uses: String, #[serde(default)] diff --git a/tests/test_workflow.rs b/tests/test_workflow.rs index 5a2e2ad..edcebf6 100644 --- a/tests/test_workflow.rs +++ b/tests/test_workflow.rs @@ -1,12 +1,9 @@ use std::{env, path::Path}; -use github_actions_models::{ - common::SoV, - workflow::{ - event::OptionalBody, - job::{RunsOn, StepBody}, - Job, Trigger, Workflow, - }, +use github_actions_models::workflow::{ + event::OptionalBody, + job::{RunsOn, StepBody}, + Job, Trigger, Workflow, }; fn load_workflow(name: &str) -> Workflow { @@ -44,7 +41,7 @@ fn test_pip_audit_ci() { assert_eq!(test_job.name, None); assert_eq!( test_job.runs_on, - RunsOn::Target(SoV::one("ubuntu-latest".to_string())) + RunsOn::Target(vec!["ubuntu-latest".to_string()]) ); assert_eq!(test_job.steps.len(), 3);