use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[cfg_attr(feature = "openapi", derive(utoipa::ToSchema))]
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case", tag = "kind")]
pub enum RunActor {
User {
user_id: Uuid,
},
ApiKey {
api_key_id: Uuid,
user_id: Uuid,
},
}
impl RunActor {
pub fn user_id(&self) -> Uuid {
match self {
RunActor::User { user_id } | RunActor::ApiKey { user_id, .. } => *user_id,
}
}
pub fn api_key_id(&self) -> Option<Uuid> {
match self {
RunActor::User { .. } => None,
RunActor::ApiKey { api_key_id, .. } => Some(*api_key_id),
}
}
pub fn from_columns(user_id: Option<Uuid>, api_key_id: Option<Uuid>) -> Option<Self> {
match (user_id, api_key_id) {
(Some(user_id), Some(api_key_id)) => Some(RunActor::ApiKey {
api_key_id,
user_id,
}),
(Some(user_id), None) => Some(RunActor::User { user_id }),
(None, _) => None,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn serde_roundtrip() {
let actors = vec![
RunActor::User {
user_id: Uuid::now_v7(),
},
RunActor::ApiKey {
api_key_id: Uuid::now_v7(),
user_id: Uuid::now_v7(),
},
];
for actor in actors {
let json = serde_json::to_string(&actor).expect("serialize");
let back: RunActor = serde_json::from_str(&json).expect("deserialize");
assert_eq!(actor, back);
}
}
#[test]
fn user_variant_is_tagged_user() {
let actor = RunActor::User {
user_id: Uuid::now_v7(),
};
let json = serde_json::to_value(&actor).expect("serialize");
assert_eq!(json["kind"], "user");
}
#[test]
fn api_key_variant_is_tagged_api_key() {
let actor = RunActor::ApiKey {
api_key_id: Uuid::now_v7(),
user_id: Uuid::now_v7(),
};
let json = serde_json::to_value(&actor).expect("serialize");
assert_eq!(json["kind"], "api_key");
}
#[test]
fn user_id_returns_owner_for_both_variants() {
let user_id = Uuid::now_v7();
assert_eq!(RunActor::User { user_id }.user_id(), user_id);
assert_eq!(
RunActor::ApiKey {
api_key_id: Uuid::now_v7(),
user_id,
}
.user_id(),
user_id
);
}
#[test]
fn api_key_id_is_none_for_user_variant() {
let actor = RunActor::User {
user_id: Uuid::now_v7(),
};
assert!(actor.api_key_id().is_none());
}
#[test]
fn api_key_id_is_some_for_api_key_variant() {
let api_key_id = Uuid::now_v7();
let actor = RunActor::ApiKey {
api_key_id,
user_id: Uuid::now_v7(),
};
assert_eq!(actor.api_key_id(), Some(api_key_id));
}
#[test]
fn from_columns_both_none() {
assert!(RunActor::from_columns(None, None).is_none());
}
#[test]
fn from_columns_user_only() {
let user_id = Uuid::now_v7();
assert_eq!(
RunActor::from_columns(Some(user_id), None),
Some(RunActor::User { user_id })
);
}
#[test]
fn from_columns_user_and_key() {
let user_id = Uuid::now_v7();
let api_key_id = Uuid::now_v7();
assert_eq!(
RunActor::from_columns(Some(user_id), Some(api_key_id)),
Some(RunActor::ApiKey {
api_key_id,
user_id
})
);
}
#[test]
fn from_columns_key_without_user_is_discarded() {
assert!(RunActor::from_columns(None, Some(Uuid::now_v7())).is_none());
}
}