pub use stack_ids::{EntityId, Scope, ScopeKey};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
pub struct ProjectionId {
pub kind: ProjectionKind,
pub key: String,
pub scope: ScopeKey,
}
impl ProjectionId {
pub fn new(kind: ProjectionKind, key: impl Into<String>, scope: ScopeKey) -> Self {
Self {
kind,
key: key.into(),
scope,
}
}
}
impl std::fmt::Display for ProjectionId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}@{}", self.kind.as_str(), self.key, self.scope)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ProjectionKind {
Entity,
Temporal,
RouteStats,
Custom(String),
}
impl ProjectionKind {
pub fn as_str(&self) -> &str {
match self {
Self::Entity => "entity",
Self::Temporal => "temporal",
Self::RouteStats => "route_stats",
Self::Custom(s) => s.as_str(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn scope_key_equality() {
let s1 = Scope::new("ns").with_repo("repo-a");
let s2 = Scope::new("ns").with_repo("repo-a");
assert_eq!(s1.key(), s2.key());
}
#[test]
fn scope_key_inequality_different_repo() {
let s1 = Scope::new("ns").with_repo("repo-a");
let s2 = Scope::new("ns").with_repo("repo-b");
assert_ne!(s1.key(), s2.key());
}
#[test]
fn scope_key_display() {
let s = Scope::new("prod")
.with_domain("code")
.with_workspace("ws1")
.with_repo("myrepo");
assert_eq!(s.key().to_string(), "prod/code@ws1#myrepo");
}
#[test]
fn projection_id_includes_scope() {
let sk = ScopeKey::namespace_only("ns");
let pid = ProjectionId::new(ProjectionKind::Entity, "test", sk);
let display = pid.to_string();
assert!(display.contains("entity"));
assert!(display.contains("test"));
assert!(display.contains("ns"));
}
}