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

153 lines
4.1 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 local model server configuration.
LocalModelServerId
);
typed_id!(
/// Identifies a [`crate::skill::Skill`].
SkillId
);
typed_id!(
/// Identifies a [`crate::issue::Issue`].
IssueId
);
typed_id!(
/// Identifies a [`crate::sprint::Sprint`].
SprintId
);
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
);
typed_id!(
/// Identifies one armed one-shot wake-up of a [`crate::ports::Scheduler`]
/// (ARCHITECTURE §21.4). Opaque, cancellable handle returned by
/// [`crate::ports::Scheduler::arm`] and consumed by
/// [`crate::ports::Scheduler::cancel`].
ScheduleId
);
typed_id!(
/// Identifies a first-class background task.
TaskId
);
typed_id!(
/// Identifies one composite inter-agent business rendezvous.
RendezvousId
);
/// Runtime-only key for an agent scoped by its project.
///
/// `AgentId` is persisted inside a project and is not globally unique across
/// simultaneously opened projects. Any app-wide in-memory registry that tracks
/// live runtime state for an agent must use this key instead of `AgentId` alone.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct RuntimeAgentKey {
/// Project that owns the runtime agent.
pub project_id: ProjectId,
/// Agent inside that project.
pub agent_id: AgentId,
}
impl RuntimeAgentKey {
/// Builds a scoped runtime key from its persisted identifiers.
#[must_use]
pub const fn new(project_id: ProjectId, agent_id: AgentId) -> Self {
Self {
project_id,
agent_id,
}
}
}
impl std::fmt::Display for RuntimeAgentKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}:{}", self.project_id, self.agent_id)
}
}