use std::fmt::Display;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum Azp {
RootProvider,
Provider(Uuid),
AccessKey(Uuid),
}
impl Azp {
pub fn is_root_provider(&self) -> bool {
matches!(self, Self::RootProvider)
}
pub fn is_provider(&self) -> bool {
matches!(self, Self::Provider(_))
}
pub fn is_access_key(&self) -> bool {
matches!(self, Self::AccessKey(_))
}
}
impl Display for Azp {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::RootProvider => write!(f, "OIDC"),
Self::Provider(provider_id) => write!(f, "OIDC|{provider_id}"),
Self::AccessKey(access_key_id) => write!(f, "CSAK|{access_key_id}"),
}
}
}
impl Serialize for Azp {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
serializer.serialize_str(self.to_string().as_str())
}
}
impl<'de> Deserialize<'de> for Azp {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
if s == "OIDC" {
Ok(Self::RootProvider)
} else if let Some((prefix, id)) = s.split_once('|') {
match prefix {
"OIDC" => Ok(Self::Provider(
Uuid::parse_str(id).map_err(serde::de::Error::custom)?,
)),
"CSAK" => Ok(Self::AccessKey(
Uuid::parse_str(id).map_err(serde::de::Error::custom)?,
)),
_ => Err(serde::de::Error::custom(format!("Invalid azp value: {s}"))),
}
} else {
Err(serde::de::Error::custom(format!("Invalid azp value: {s}")))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
mod root {
use super::*;
#[test]
fn serializes_as_oidc() {
let azp = Azp::RootProvider;
let serialized = serde_json::to_value(azp).unwrap();
assert_eq!(serialized, json!("OIDC"));
}
#[test]
fn deserializes_from_oidc() {
let json = json!("OIDC");
let azp: Azp = serde_json::from_value(json).unwrap();
assert_eq!(azp, Azp::RootProvider);
}
}
mod provider {
use super::*;
#[test]
fn serializes_with_provider_id() {
let uuid = Uuid::new_v4();
let azp = Azp::Provider(uuid);
let serialized = serde_json::to_value(azp).unwrap();
assert_eq!(serialized, json!(format!("OIDC|{uuid}")));
}
#[test]
fn deserializes_with_provider_id() {
let uuid = Uuid::new_v4();
let json = json!(format!("OIDC|{uuid}"));
let azp: Azp = serde_json::from_value(json).unwrap();
assert_eq!(azp, Azp::Provider(uuid));
}
}
mod access_key {
use super::*;
#[test]
fn serializes_with_access_key_id() {
let uuid = Uuid::new_v4();
let azp = Azp::AccessKey(uuid);
let serialized = serde_json::to_value(azp).unwrap();
assert_eq!(serialized, json!(format!("CSAK|{uuid}")));
}
#[test]
fn deserializes_with_access_key_id() {
let uuid = Uuid::new_v4();
let json = json!(format!("CSAK|{uuid}"));
let azp: Azp = serde_json::from_value(json).unwrap();
assert_eq!(azp, Azp::AccessKey(uuid));
}
}
mod invalid {
use super::*;
#[test]
fn deserializes_invalid_azp() {
let json = json!("INVALID|123e4567-e89b-12d3-a456-426614174000");
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
#[test]
fn deserializes_invalid_format() {
let json = json!("OIDC|INVALID_UUID");
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
}
mod empty {
use super::*;
#[test]
fn fails_on_empty_string() {
let json = json!("");
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
#[test]
fn fails_on_null() {
let json = json!(null);
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
}
mod malformed {
use super::*;
#[test]
fn fails_on_malformed_string() {
let json = json!("OIDC|123e4567-e89b-12d3-a456-426614174000|extra");
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
#[test]
fn fails_on_missing_prefix() {
let json = json!("123e4567-e89b-12d3-a456-426614174000");
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
#[test]
fn fails_on_invalid_uuid() {
let json = json!("OIDC|invalid-uuid");
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
#[test]
fn fails_on_invalid_access_key_uuid() {
let json = json!("CSAK|invalid-uuid");
let result: Result<Azp, _> = serde_json::from_value(json);
assert!(result.is_err());
}
}
}