Files
IdeaSDK/crates/domain/src/ids.rs

95 lines
2.4 KiB
Rust

//! Strongly-typed identifiers.
//!
//! Each identifier is a `newtype` around [`uuid::Uuid`]. Using distinct types
//! per concept makes it impossible to pass, say, an [`AgentId`] where a
//! [`ProjectId`] is expected (compile-time safety, SOLID/typing discipline).
use serde::{Deserialize, Serialize};
use uuid::Uuid;
macro_rules! typed_id {
($(#[$meta:meta])* $name:ident) => {
$(#[$meta])*
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct $name(pub Uuid);
impl $name {
/// Wraps an existing [`Uuid`].
#[must_use]
pub const fn from_uuid(id: Uuid) -> Self {
Self(id)
}
/// Generates a fresh random (v4) identifier.
///
/// Prefer injecting an [`crate::ports::IdGenerator`] in application
/// code for determinism; this convenience exists for tests and the
/// composition root.
#[must_use]
pub fn new_random() -> Self {
Self(Uuid::new_v4())
}
/// Returns the inner [`Uuid`].
#[must_use]
pub const fn as_uuid(&self) -> Uuid {
self.0
}
}
impl std::fmt::Display for $name {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<Uuid> for $name {
fn from(id: Uuid) -> Self {
Self(id)
}
}
};
}
typed_id!(
/// Identifies a [`crate::project::Project`].
ProjectId
);
typed_id!(
/// Identifies an [`crate::agent::Agent`].
AgentId
);
typed_id!(
/// Identifies an [`crate::template::AgentTemplate`].
TemplateId
);
typed_id!(
/// Identifies an [`crate::profile::AgentProfile`].
ProfileId
);
typed_id!(
/// Identifies a [`crate::skill::Skill`].
SkillId
);
typed_id!(
/// Identifies a [`crate::terminal::TerminalSession`].
SessionId
);
typed_id!(
/// Identifies a [`crate::layout::WindowId`]-bearing OS window.
WindowId
);
typed_id!(
/// Identifies a [`crate::layout::Tab`].
TabId
);
typed_id!(
/// Identifies one named terminal layout within a project (L10/#4).
LayoutId
);
typed_id!(
/// Identifies a node in a [`crate::layout::LayoutTree`].
NodeId
);