Skip to main content

bobby_browser_client/
ids.rs

1//! Typed identifiers used across the `/v1` wire contract.
2
3use serde::{Deserialize, Serialize};
4use std::{fmt, str::FromStr};
5use thiserror::Error;
6use uuid::Uuid;
7
8macro_rules! uuid_id {
9    ($name:ident) => {
10        #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)]
11        #[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
12        #[serde(transparent)]
13        pub struct $name(pub Uuid);
14
15        impl $name {
16            pub fn new() -> Self {
17                Self(Uuid::new_v4())
18            }
19        }
20
21        impl Default for $name {
22            fn default() -> Self {
23                Self::new()
24            }
25        }
26    };
27}
28
29uuid_id!(SessionId);
30uuid_id!(PageId);
31uuid_id!(CommandId);
32uuid_id!(WorkflowId);
33uuid_id!(AttemptId);
34uuid_id!(EvidenceId);
35uuid_id!(WorkerId);
36uuid_id!(ArtifactId);
37uuid_id!(CheckpointId);
38uuid_id!(CompanionId);
39uuid_id!(ProfileId);
40uuid_id!(AttachmentId);
41
42/// Runtime job identifier (`job_<uuid>`).
43#[derive(Debug, Clone, Serialize, PartialEq, Eq, Hash)]
44#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
45#[serde(transparent)]
46pub struct JobId(String);
47
48impl JobId {
49    pub fn new() -> Self {
50        Self(format!("job_{}", Uuid::new_v4()))
51    }
52
53    pub fn parse(value: impl Into<String>) -> Result<Self, JobIdError> {
54        let value = value.into();
55        let suffix = value.strip_prefix("job_").ok_or(JobIdError)?;
56        Uuid::parse_str(suffix).map_err(|_| JobIdError)?;
57        Ok(Self(value))
58    }
59
60    pub fn as_str(&self) -> &str {
61        &self.0
62    }
63
64    pub fn into_string(self) -> String {
65        self.0
66    }
67}
68
69impl Default for JobId {
70    fn default() -> Self {
71        Self::new()
72    }
73}
74
75impl fmt::Display for JobId {
76    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
77        formatter.write_str(&self.0)
78    }
79}
80
81impl FromStr for JobId {
82    type Err = JobIdError;
83
84    fn from_str(value: &str) -> Result<Self, Self::Err> {
85        Self::parse(value)
86    }
87}
88
89impl<'de> Deserialize<'de> for JobId {
90    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
91    where
92        D: serde::Deserializer<'de>,
93    {
94        let value = String::deserialize(deserializer)?;
95        Self::parse(value).map_err(serde::de::Error::custom)
96    }
97}
98
99#[derive(Debug, Clone, Copy, Error)]
100#[error("job id must use the job_<uuid> runtime format")]
101pub struct JobIdError;