turnframe_provider/
ids.rs1use std::fmt;
25
26use serde::{Deserialize, Serialize};
27use uuid::Uuid;
28
29pub use turnframe_core::ids::{ModelKey, ProviderKey};
30
31#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
38#[serde(transparent)]
39pub struct CallId(pub String);
40
41impl CallId {
42 #[must_use]
44 pub fn new(value: impl Into<String>) -> Self {
45 Self(value.into())
46 }
47
48 #[must_use]
50 pub fn as_str(&self) -> &str {
51 &self.0
52 }
53
54 #[must_use]
56 pub fn into_string(self) -> String {
57 self.0
58 }
59
60 #[must_use]
62 pub fn is_empty(&self) -> bool {
63 self.0.is_empty()
64 }
65}
66
67impl From<&str> for CallId {
68 fn from(value: &str) -> Self {
69 Self(value.to_owned())
70 }
71}
72
73impl From<String> for CallId {
74 fn from(value: String) -> Self {
75 Self(value)
76 }
77}
78
79impl From<CallId> for String {
80 fn from(value: CallId) -> Self {
81 value.0
82 }
83}
84
85impl AsRef<str> for CallId {
86 fn as_ref(&self) -> &str {
87 &self.0
88 }
89}
90
91impl std::borrow::Borrow<str> for CallId {
92 fn borrow(&self) -> &str {
93 &self.0
94 }
95}
96
97impl fmt::Display for CallId {
98 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99 f.write_str(&self.0)
100 }
101}
102
103#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
105pub struct ModelRef {
106 pub provider: ProviderKey,
108 pub model: ModelKey,
110}
111
112impl ModelRef {
113 #[must_use]
121 pub fn new(provider: impl Into<ProviderKey>, model: impl Into<ModelKey>) -> Self {
122 Self {
123 provider: provider.into(),
124 model: model.into(),
125 }
126 }
127}
128
129impl fmt::Display for ModelRef {
130 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
131 write!(f, "{}/{}", self.provider, self.model)
132 }
133}
134
135#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
141#[serde(transparent)]
142pub struct RequestId(pub Uuid);
143
144impl RequestId {
145 #[must_use]
147 pub fn new() -> Self {
148 Self(Uuid::now_v7())
149 }
150
151 #[must_use]
153 pub const fn nil() -> Self {
154 Self(Uuid::nil())
155 }
156
157 #[must_use]
159 pub const fn as_uuid(&self) -> &Uuid {
160 &self.0
161 }
162}
163
164impl Default for RequestId {
165 fn default() -> Self {
166 Self::new()
167 }
168}
169
170impl From<Uuid> for RequestId {
171 fn from(value: Uuid) -> Self {
172 Self(value)
173 }
174}
175
176impl fmt::Display for RequestId {
177 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
178 fmt::Display::fmt(&self.0.hyphenated(), f)
179 }
180}
181
182impl std::str::FromStr for RequestId {
183 type Err = uuid::Error;
184
185 fn from_str(s: &str) -> Result<Self, Self::Err> {
186 Uuid::parse_str(s).map(Self)
187 }
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
192#[serde(transparent)]
193pub struct AttemptNumber(pub u32);
194
195impl AttemptNumber {
196 pub const FIRST: Self = Self(1);
198
199 #[must_use]
201 pub const fn next(self) -> Self {
202 Self(self.0.saturating_add(1))
203 }
204
205 #[must_use]
207 pub const fn get(self) -> u32 {
208 self.0
209 }
210}
211
212impl Default for AttemptNumber {
213 fn default() -> Self {
214 Self::FIRST
215 }
216}
217
218impl fmt::Display for AttemptNumber {
219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
220 write!(f, "{}", self.0)
221 }
222}
223
224#[cfg(test)]
225mod tests {
226 use super::*;
227
228 #[test]
229 fn request_ids_are_time_ordered_and_round_trip() {
230 let a = RequestId::new();
231 let b = RequestId::new();
232 assert!(a <= b);
233 let json = serde_json::to_string(&a).unwrap();
234 let back: RequestId = serde_json::from_str(&json).unwrap();
235 assert_eq!(a, back);
236 assert_eq!(a.to_string().parse::<RequestId>().unwrap(), a);
237 assert_eq!(RequestId::nil().as_uuid(), &Uuid::nil());
238 }
239
240 #[test]
241 fn keys_are_cores_own_types() {
242 let core_key = turnframe_core::ids::ProviderKey::from("openai");
245 let model_ref = ModelRef::new(core_key.clone(), ModelKey::from("gpt-4o"));
246 assert_eq!(model_ref.provider, core_key);
247 assert_eq!(model_ref.to_string(), "openai/gpt-4o");
248 assert_eq!(serde_json::to_string(&core_key).unwrap(), "\"openai\"");
249 }
250
251 #[test]
252 fn call_ids_are_transparent_strings() {
253 let id = CallId::from("call_123");
254 assert_eq!(serde_json::to_string(&id).unwrap(), "\"call_123\"");
255 assert_eq!(id.as_str(), "call_123");
256 assert_eq!(id.to_string(), "call_123");
257 assert!(!id.is_empty());
258 assert!(CallId::new(String::new()).is_empty());
259 }
260
261 #[test]
262 fn attempts_start_at_one() {
263 assert_eq!(AttemptNumber::default(), AttemptNumber::FIRST);
264 assert_eq!(AttemptNumber::FIRST.next().get(), 2);
265 assert_eq!(AttemptNumber(u32::MAX).next().get(), u32::MAX);
266 assert_eq!(AttemptNumber::FIRST.to_string(), "1");
267 }
268}