remove SoV from the public API

This was a leaky abstraction; surface everything as `Vec<T>`
and handle the scalar conversion internally.
This commit is contained in:
William Woodruff 2024-02-25 19:57:25 -05:00
parent 1a439ef28b
commit d070a0bf3f
No known key found for this signature in database
4 changed files with 38 additions and 60 deletions

View file

@ -2,7 +2,7 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::Deserialize; use serde::{Deserialize, Deserializer};
/// `permissions` for a workflow, job, or step. /// `permissions` for a workflow, job, or step.
#[derive(Deserialize, Debug, PartialEq)] #[derive(Deserialize, Debug, PartialEq)]
@ -116,48 +116,37 @@ pub type BoE = LoE<bool>;
/// A "scalar or vector" type, for places in GitHub Actions where a /// A "scalar or vector" type, for places in GitHub Actions where a
/// key can have either a scalar value or an array of values. /// 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)] #[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)] #[serde(untagged)]
pub enum SoV<T> { pub(crate) enum SoV<T> {
One(T), One(T),
Many(Vec<T>), Many(Vec<T>),
} }
impl<T> Default for SoV<T> { impl<T> Into<Vec<T>> for SoV<T> {
fn default() -> Self { fn into(self) -> Vec<T> {
SoV::Many(Default::default()) match self {
Self::One(v) => vec![v],
Self::Many(vs) => vs,
}
} }
} }
impl<T> SoV<T> { pub(crate) fn scalar_or_vector<'de, D, T>(de: D) -> Result<Vec<T>, D::Error>
pub fn one(v: T) -> Self { where
SoV::One(v) D: Deserializer<'de>,
} T: Deserialize<'de>,
{
pub fn many(vs: Vec<T>) -> Self { SoV::deserialize(de).map(Into::into)
SoV::Many(vs)
}
}
impl<'a, T> IntoIterator for &'a SoV<T> {
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()
}
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::common::{BasePermission, ExplicitPermissions}; use crate::common::{BasePermission, ExplicitPermissions};
use super::{Permissions, SoV}; use super::Permissions;
#[test] #[test]
fn test_permissions() { fn test_permissions() {
@ -172,17 +161,4 @@ mod tests {
Ok(_) Ok(_)
)); ));
} }
#[test]
fn test_sov_intoiterator() {
let sov_one = SoV::one("test".to_string());
assert_eq!(sov_one.into_iter().collect::<Vec<_>>(), 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<_>>(),
vec!["test-1", "test-2"]
);
}
} }

View file

@ -8,8 +8,6 @@ use std::collections::{HashMap, HashSet};
use serde::Deserialize; use serde::Deserialize;
use crate::common::SoV;
/// A `dependabot.yml` configuration file. /// A `dependabot.yml` configuration file.
#[derive(Deserialize, Debug)] #[derive(Deserialize, Debug)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "kebab-case")]
@ -123,7 +121,8 @@ pub struct Update {
// TODO: pull-request-branch-name // TODO: pull-request-branch-name
#[serde(default)] #[serde(default)]
pub rebase_strategy: RebaseStrategy, pub rebase_strategy: RebaseStrategy,
pub registries: Option<SoV<String>>, #[serde(default, deserialize_with = "crate::common::scalar_or_vector")]
pub registries: Vec<String>,
#[serde(default)] #[serde(default)]
pub reviewers: HashSet<String>, pub reviewers: HashSet<String>,
pub schedule: Schedule, pub schedule: Schedule,

View file

@ -5,7 +5,7 @@ use std::collections::HashMap;
use serde::Deserialize; use serde::Deserialize;
use serde_yaml::Value; use serde_yaml::Value;
use crate::common::{BoE, Env, LoE, Permissions, SoV}; use crate::common::{BoE, Env, LoE, Permissions};
use super::{Concurrency, Defaults}; use super::{Concurrency, Defaults};
@ -17,8 +17,8 @@ pub struct NormalJob {
pub name: Option<String>, pub name: Option<String>,
#[serde(default)] #[serde(default)]
pub permissions: Permissions, pub permissions: Permissions,
#[serde(default)] #[serde(default, deserialize_with = "crate::common::scalar_or_vector")]
pub needs: SoV<String>, pub needs: Vec<String>,
pub r#if: Option<String>, pub r#if: Option<String>,
pub runs_on: RunsOn, pub runs_on: RunsOn,
pub environment: Option<DeploymentEnvironment>, pub environment: Option<DeploymentEnvironment>,
@ -41,9 +41,15 @@ pub struct NormalJob {
#[derive(Debug, Deserialize, PartialEq)] #[derive(Debug, Deserialize, PartialEq)]
#[serde(rename_all = "kebab-case", untagged)] #[serde(rename_all = "kebab-case", untagged)]
pub enum RunsOn { pub enum RunsOn {
Target(SoV<String>), #[serde(deserialize_with = "crate::common::scalar_or_vector")]
Group { group: String }, Target(Vec<String>),
Label { label: SoV<String> }, Group {
group: String,
},
#[serde(deserialize_with = "crate::common::scalar_or_vector")]
Label {
label: Vec<String>,
},
} }
#[derive(Deserialize)] #[derive(Deserialize)]
@ -130,8 +136,8 @@ pub struct ReusableWorkflowCallJob {
pub name: Option<String>, pub name: Option<String>,
#[serde(default)] #[serde(default)]
pub permissions: Permissions, pub permissions: Permissions,
#[serde(default)] #[serde(default, deserialize_with = "crate::common::scalar_or_vector")]
pub needs: SoV<String>, pub needs: Vec<String>,
pub r#if: Option<String>, pub r#if: Option<String>,
pub uses: String, pub uses: String,
#[serde(default)] #[serde(default)]

View file

@ -1,12 +1,9 @@
use std::{env, path::Path}; use std::{env, path::Path};
use github_actions_models::{ use github_actions_models::workflow::{
common::SoV, event::OptionalBody,
workflow::{ job::{RunsOn, StepBody},
event::OptionalBody, Job, Trigger, Workflow,
job::{RunsOn, StepBody},
Job, Trigger, Workflow,
},
}; };
fn load_workflow(name: &str) -> 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.name, None);
assert_eq!( assert_eq!(
test_job.runs_on, 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); assert_eq!(test_job.steps.len(), 3);