Skip to main content

ferrin_spec/shared/
ids.rs

1//! Newtype identifiers.
2//!
3//! Each identifier wraps a `String` and serializes transparently as a JSON
4//! string. Distinct types prevent, for example, passing a tool name where a
5//! tool call id is expected.
6
7macro_rules! string_id {
8    ($(#[$meta:meta])* $name:ident) => {
9        $(#[$meta])*
10        #[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Default)]
11        #[derive(serde::Serialize, serde::Deserialize)]
12        #[serde(transparent)]
13        pub struct $name(String);
14
15        impl $name {
16            /// Creates a new identifier from any string-like value.
17            #[must_use]
18            pub fn new(value: impl Into<String>) -> Self {
19                Self(value.into())
20            }
21
22            /// Returns the identifier as a string slice.
23            #[must_use]
24            pub fn as_str(&self) -> &str {
25                &self.0
26            }
27
28            /// Consumes the identifier and returns the inner `String`.
29            #[must_use]
30            pub fn into_string(self) -> String {
31                self.0
32            }
33        }
34
35        impl std::fmt::Display for $name {
36            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
37                f.write_str(&self.0)
38            }
39        }
40
41        impl From<String> for $name {
42            fn from(value: String) -> Self {
43                Self(value)
44            }
45        }
46
47        impl From<&str> for $name {
48            fn from(value: &str) -> Self {
49                Self(value.to_owned())
50            }
51        }
52
53        impl From<$name> for String {
54            fn from(value: $name) -> Self {
55                value.0
56            }
57        }
58
59        impl AsRef<str> for $name {
60            fn as_ref(&self) -> &str {
61                &self.0
62            }
63        }
64
65        impl std::borrow::Borrow<str> for $name {
66            fn borrow(&self) -> &str {
67                &self.0
68            }
69        }
70
71        impl PartialEq<str> for $name {
72            fn eq(&self, other: &str) -> bool {
73                self.0 == other
74            }
75        }
76
77        impl PartialEq<&str> for $name {
78            fn eq(&self, other: &&str) -> bool {
79                self.0 == *other
80            }
81        }
82    };
83}
84
85string_id! {
86    /// Provider identifier, for example `openai.responses` or `anthropic.messages`.
87    ProviderId
88}
89
90string_id! {
91    /// Model identifier as understood by the provider, for example `gpt-5`.
92    ModelId
93}
94
95string_id! {
96    /// Name of a tool as exposed to the model.
97    ToolName
98}
99
100string_id! {
101    /// Identifier of a tool call, assigned by the provider.
102    ToolCallId
103}
104
105string_id! {
106    /// Identifier of a tool approval request.
107    ApprovalId
108}
109
110string_id! {
111    /// Identifier of a provider batch job.
112    BatchId
113}
114
115string_id! {
116    /// Identifier of a text, reasoning or tool-input part within a stream.
117    ///
118    /// Provider-assigned part ids are unique within a single call only; the
119    /// core remaps colliding ids across steps.
120    PartId
121}