github_actions_models/
common.rsuse std::fmt::Display;
use indexmap::IndexMap;
use serde::{Deserialize, Deserializer, Serialize};
pub mod expr;
#[derive(Deserialize, Debug, PartialEq)]
#[serde(rename_all = "kebab-case", untagged)]
pub enum Permissions {
Base(BasePermission),
Explicit(IndexMap<String, Permission>),
}
impl Default for Permissions {
fn default() -> Self {
Self::Base(BasePermission::Default)
}
}
#[derive(Deserialize, Default, Debug, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum BasePermission {
#[default]
Default,
ReadAll,
WriteAll,
}
#[derive(Deserialize, Default, Debug, PartialEq)]
#[serde(rename_all = "kebab-case")]
pub enum Permission {
Read,
Write,
#[default]
None,
}
pub type Env = IndexMap<String, EnvValue>;
#[derive(Serialize, Deserialize, Debug, PartialEq)]
#[serde(untagged)]
pub enum EnvValue {
#[serde(deserialize_with = "null_to_default")]
String(String),
Number(f64),
Boolean(bool),
}
impl Display for EnvValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::String(s) => write!(f, "{s}"),
Self::Number(n) => write!(f, "{n}"),
Self::Boolean(b) => write!(f, "{b}"),
}
}
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum SoV<T> {
One(T),
Many(Vec<T>),
}
impl<T> From<SoV<T>> for Vec<T> {
fn from(val: SoV<T>) -> Vec<T> {
match val {
SoV::One(v) => vec![v],
SoV::Many(vs) => vs,
}
}
}
pub(crate) fn scalar_or_vector<'de, D, T>(de: D) -> Result<Vec<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
SoV::deserialize(de).map(Into::into)
}
#[derive(Debug, Deserialize, PartialEq)]
#[serde(untagged)]
enum BoS {
Bool(bool),
String(String),
}
impl From<BoS> for String {
fn from(value: BoS) -> Self {
match value {
BoS::Bool(b) => b.to_string(),
BoS::String(s) => s,
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(untagged)]
pub enum If {
Bool(bool),
Expr(String),
}
pub(crate) fn bool_is_string<'de, D>(de: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
BoS::deserialize(de).map(Into::into)
}
fn null_to_default<'de, D, T>(de: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
T: Default + Deserialize<'de>,
{
let key = Option::<T>::deserialize(de)?;
Ok(key.unwrap_or_default())
}
#[cfg(test)]
mod tests {
use indexmap::IndexMap;
use crate::common::{BasePermission, Env, EnvValue, Permission};
use super::Permissions;
#[test]
fn test_permissions() {
assert_eq!(
serde_yaml::from_str::<Permissions>("read-all").unwrap(),
Permissions::Base(BasePermission::ReadAll)
);
let perm = "security-events: write";
assert_eq!(
serde_yaml::from_str::<Permissions>(perm).unwrap(),
Permissions::Explicit(IndexMap::from([(
"security-events".into(),
Permission::Write
)]))
);
}
#[test]
fn test_env_empty_value() {
let env = "foo:";
assert_eq!(
serde_yaml::from_str::<Env>(env).unwrap()["foo"],
EnvValue::String("".into())
);
}
}