use jiff::Timestamp;
use serde::{Deserialize, Serialize};
use ulid::Ulid;
use crate::agent::AgentName;
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct RunId(String);
impl RunId {
#[must_use]
pub fn generate() -> Self {
Self(format!("run_{}", Ulid::new()))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for RunId {
fn from(value: &str) -> Self {
Self(value.to_owned())
}
}
impl std::fmt::Display for RunId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct FlightId(String);
impl FlightId {
#[must_use]
pub fn generate() -> Self {
Self(format!("flt_{}", Ulid::new()))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl From<&str> for FlightId {
fn from(value: &str) -> Self {
Self(value.to_owned())
}
}
impl std::fmt::Display for FlightId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(&self.0)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
#[serde(transparent)]
pub struct ItineraryId(String);
impl ItineraryId {
#[must_use]
pub fn generate() -> Self {
Self(format!("itn_{}", Ulid::new()))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Deserialize, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum Origin {
Human,
Agent(AgentName),
}
impl Origin {
#[must_use]
pub fn agent(&self) -> Option<&AgentName> {
match self {
Self::Human => None,
Self::Agent(name) => Some(name),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
pub struct Flight {
pub id: FlightId,
pub itinerary: ItineraryId,
pub from: Origin,
pub to: AgentName,
pub body: String,
pub hops_remaining: u32,
pub sent_at: Timestamp,
}
impl Flight {
#[must_use]
pub fn new(
itinerary: ItineraryId,
from: Origin,
to: AgentName,
body: impl Into<String>,
hops_remaining: u32,
) -> Self {
Self {
id: FlightId::generate(),
itinerary,
from,
to,
body: body.into(),
hops_remaining,
sent_at: Timestamp::now(),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn identifiers_are_unique_and_prefixed() {
let first = FlightId::generate();
let second = FlightId::generate();
assert_ne!(first, second);
assert!(first.as_str().starts_with("flt_"));
assert!(ItineraryId::generate().as_str().starts_with("itn_"));
}
#[test]
fn human_origin_has_no_agent() {
assert!(Origin::Human.agent().is_none());
assert_eq!(
Origin::Agent("planner".into()).agent(),
Some(&AgentName::from("planner"))
);
}
}