1use core::fmt;
17use core::ops::Deref;
18
19#[cfg(not(feature = "std"))]
20use alloc::string::String;
21
22use serde::{Deserialize, Deserializer, Serialize, Serializer};
23
24#[derive(Debug, Clone, Default, PartialEq, Eq, Hash)]
44pub enum ProtocolVersion {
45 V2025_06_18,
47 #[default]
49 V2025_11_25,
50 Draft,
52 Unknown(String),
54}
55
56impl ProtocolVersion {
57 pub const LATEST: Self = Self::V2025_11_25;
59
60 pub const STABLE: &[Self] = &[Self::V2025_06_18, Self::V2025_11_25];
64
65 #[must_use]
67 pub fn as_str(&self) -> &str {
68 match self {
69 Self::V2025_06_18 => "2025-06-18",
70 Self::V2025_11_25 => "2025-11-25",
71 Self::Draft => "DRAFT-2026-v1",
72 Self::Unknown(s) => s.as_str(),
73 }
74 }
75
76 #[must_use]
78 pub fn is_known(&self) -> bool {
79 !matches!(self, Self::Unknown(_))
80 }
81
82 #[must_use]
86 pub fn is_stable(&self) -> bool {
87 matches!(self, Self::V2025_06_18 | Self::V2025_11_25)
88 }
89
90 #[must_use]
97 pub fn is_draft(&self) -> bool {
98 matches!(self, Self::Draft)
99 }
100
101 #[must_use]
104 pub fn is_any_draft(&self) -> bool {
105 match self {
106 Self::Draft => true,
107 Self::Unknown(s) => s.starts_with("DRAFT-"),
108 _ => false,
109 }
110 }
111
112 fn ordinal(&self) -> u32 {
113 match self {
114 Self::V2025_06_18 => 1,
115 Self::V2025_11_25 => 2,
116 Self::Draft => 3,
117 Self::Unknown(_) => u32::MAX,
118 }
119 }
120}
121
122impl fmt::Display for ProtocolVersion {
123 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 f.write_str(self.as_str())
125 }
126}
127
128impl PartialOrd for ProtocolVersion {
129 fn partial_cmp(&self, other: &Self) -> Option<core::cmp::Ordering> {
130 Some(self.cmp(other))
131 }
132}
133
134impl Ord for ProtocolVersion {
135 fn cmp(&self, other: &Self) -> core::cmp::Ordering {
136 match (self, other) {
137 (Self::Unknown(a), Self::Unknown(b)) => a.cmp(b),
138 _ => self.ordinal().cmp(&other.ordinal()),
139 }
140 }
141}
142
143impl From<&str> for ProtocolVersion {
144 fn from(s: &str) -> Self {
145 match s {
146 "2025-06-18" => Self::V2025_06_18,
147 "2025-11-25" => Self::V2025_11_25,
148 "DRAFT-2026-v1" => Self::Draft,
149 other => Self::Unknown(other.into()),
150 }
151 }
152}
153
154impl From<String> for ProtocolVersion {
155 fn from(s: String) -> Self {
156 match s.as_str() {
157 "2025-06-18" => Self::V2025_06_18,
158 "2025-11-25" => Self::V2025_11_25,
159 "DRAFT-2026-v1" => Self::Draft,
160 _ => Self::Unknown(s),
161 }
162 }
163}
164
165impl From<ProtocolVersion> for String {
166 fn from(v: ProtocolVersion) -> Self {
167 match v {
168 ProtocolVersion::Unknown(s) => s,
169 other => other.as_str().into(),
170 }
171 }
172}
173
174impl Serialize for ProtocolVersion {
175 fn serialize<S: Serializer>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error> {
176 serializer.serialize_str(self.as_str())
177 }
178}
179
180impl<'de> Deserialize<'de> for ProtocolVersion {
181 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> core::result::Result<Self, D::Error> {
182 let s = String::deserialize(deserializer)?;
183 Ok(ProtocolVersion::from(s))
184 }
185}
186
187impl PartialEq<&str> for ProtocolVersion {
188 fn eq(&self, other: &&str) -> bool {
189 self.as_str() == *other
190 }
191}
192
193impl PartialEq<ProtocolVersion> for &str {
194 fn eq(&self, other: &ProtocolVersion) -> bool {
195 *self == other.as_str()
196 }
197}
198
199#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
209#[serde(transparent)]
210pub struct Uri(String);
211
212impl Uri {
213 #[must_use]
215 pub fn new(uri: impl Into<String>) -> Self {
216 Self(uri.into())
217 }
218
219 #[must_use]
221 pub fn as_str(&self) -> &str {
222 &self.0
223 }
224
225 #[must_use]
227 pub fn into_inner(self) -> String {
228 self.0
229 }
230}
231
232impl fmt::Display for Uri {
233 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
234 self.0.fmt(f)
235 }
236}
237
238impl Deref for Uri {
239 type Target = str;
240
241 fn deref(&self) -> &Self::Target {
242 &self.0
243 }
244}
245
246impl AsRef<str> for Uri {
247 fn as_ref(&self) -> &str {
248 &self.0
249 }
250}
251
252impl From<String> for Uri {
253 fn from(value: String) -> Self {
254 Self(value)
255 }
256}
257
258impl From<&str> for Uri {
259 fn from(value: &str) -> Self {
260 Self(value.into())
261 }
262}
263
264impl From<Uri> for String {
265 fn from(value: Uri) -> Self {
266 value.0
267 }
268}
269
270impl PartialEq<&str> for Uri {
271 fn eq(&self, other: &&str) -> bool {
272 self.as_str() == *other
273 }
274}
275
276impl PartialEq<Uri> for &str {
277 fn eq(&self, other: &Uri) -> bool {
278 *self == other.as_str()
279 }
280}
281
282#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
288#[serde(transparent)]
289pub struct MimeType(String);
290
291impl MimeType {
292 #[must_use]
294 pub fn new(mime_type: impl Into<String>) -> Self {
295 Self(mime_type.into())
296 }
297
298 #[must_use]
300 pub fn as_str(&self) -> &str {
301 &self.0
302 }
303}
304
305impl fmt::Display for MimeType {
306 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
307 self.0.fmt(f)
308 }
309}
310
311impl Deref for MimeType {
312 type Target = str;
313
314 fn deref(&self) -> &Self::Target {
315 &self.0
316 }
317}
318
319impl AsRef<str> for MimeType {
320 fn as_ref(&self) -> &str {
321 &self.0
322 }
323}
324
325impl From<String> for MimeType {
326 fn from(value: String) -> Self {
327 Self(value)
328 }
329}
330
331impl From<&str> for MimeType {
332 fn from(value: &str) -> Self {
333 Self(value.into())
334 }
335}
336
337impl From<MimeType> for String {
338 fn from(value: MimeType) -> Self {
339 value.0
340 }
341}
342
343impl PartialEq<&str> for MimeType {
344 fn eq(&self, other: &&str) -> bool {
345 self.as_str() == *other
346 }
347}
348
349impl PartialEq<MimeType> for &str {
350 fn eq(&self, other: &MimeType) -> bool {
351 *self == other.as_str()
352 }
353}
354
355#[derive(Debug, Clone, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
361#[serde(transparent)]
362pub struct Base64String(String);
363
364impl Base64String {
365 #[must_use]
367 pub fn new(data: impl Into<String>) -> Self {
368 Self(data.into())
369 }
370
371 #[must_use]
373 pub fn as_str(&self) -> &str {
374 &self.0
375 }
376}
377
378impl fmt::Display for Base64String {
379 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
380 self.0.fmt(f)
381 }
382}
383
384impl Deref for Base64String {
385 type Target = str;
386
387 fn deref(&self) -> &Self::Target {
388 &self.0
389 }
390}
391
392impl AsRef<str> for Base64String {
393 fn as_ref(&self) -> &str {
394 &self.0
395 }
396}
397
398impl From<String> for Base64String {
399 fn from(value: String) -> Self {
400 Self(value)
401 }
402}
403
404impl From<&str> for Base64String {
405 fn from(value: &str) -> Self {
406 Self(value.into())
407 }
408}
409
410impl From<Base64String> for String {
411 fn from(value: Base64String) -> Self {
412 value.0
413 }
414}
415
416impl PartialEq<&str> for Base64String {
417 fn eq(&self, other: &&str) -> bool {
418 self.as_str() == *other
419 }
420}
421
422impl PartialEq<Base64String> for &str {
423 fn eq(&self, other: &Base64String) -> bool {
424 *self == other.as_str()
425 }
426}
427
428#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
435pub struct BaseMetadata {
436 pub name: String,
438 #[serde(skip_serializing_if = "Option::is_none")]
440 pub title: Option<String>,
441}
442
443impl BaseMetadata {
444 #[must_use]
446 pub fn new(name: impl Into<String>) -> Self {
447 Self {
448 name: name.into(),
449 title: None,
450 }
451 }
452
453 #[must_use]
455 pub fn with_title(mut self, title: impl Into<String>) -> Self {
456 self.title = Some(title.into());
457 self
458 }
459}
460
461pub type Cursor = String;
467
468#[cfg(test)]
469mod tests {
470 use super::*;
471
472 #[test]
473 fn protocol_version_serde_roundtrip() {
474 let v = ProtocolVersion::V2025_11_25;
475 let s = serde_json::to_string(&v).unwrap();
476 assert_eq!(s, "\"2025-11-25\"");
477 let back: ProtocolVersion = serde_json::from_str(&s).unwrap();
478 assert_eq!(back, v);
479 }
480
481 #[test]
482 fn protocol_version_unknown_preserves_string() {
483 let v: ProtocolVersion = "future-version".into();
484 assert_eq!(v, ProtocolVersion::Unknown("future-version".into()));
485 assert!(!v.is_known());
486 assert!(!v.is_stable());
487 }
488
489 #[test]
490 fn protocol_version_ordering() {
491 assert!(ProtocolVersion::V2025_06_18 < ProtocolVersion::V2025_11_25);
492 assert!(ProtocolVersion::V2025_11_25 < ProtocolVersion::Draft);
493 assert!(ProtocolVersion::Draft < ProtocolVersion::Unknown("x".into()));
494 }
495
496 #[test]
497 fn uri_transparent_serde() {
498 let uri = Uri::new("file:///path/to/file.txt");
499 let s = serde_json::to_string(&uri).unwrap();
500 assert_eq!(s, "\"file:///path/to/file.txt\"");
501 let back: Uri = serde_json::from_str(&s).unwrap();
502 assert_eq!(back, uri);
503 }
504
505 #[test]
506 fn mime_type_and_base64_roundtrips() {
507 let m = MimeType::new("application/json");
508 assert_eq!(m.as_str(), "application/json");
509 let b = Base64String::new("aGVsbG8=");
510 assert_eq!(b.as_str(), "aGVsbG8=");
511 }
512
513 #[test]
514 fn base_metadata_builder() {
515 let meta = BaseMetadata::new("my_tool").with_title("My Tool");
516 assert_eq!(meta.name, "my_tool");
517 assert_eq!(meta.title.as_deref(), Some("My Tool"));
518 let json = serde_json::to_string(&meta).unwrap();
519 assert!(json.contains("\"name\":\"my_tool\""));
520 assert!(json.contains("\"title\":\"My Tool\""));
521 }
522
523 #[test]
524 fn base_metadata_omits_absent_title() {
525 let meta = BaseMetadata::new("x");
526 let json = serde_json::to_string(&meta).unwrap();
527 assert!(!json.contains("title"));
528 }
529}