switchyard_protocol/
model_id.rs1use std::borrow::Borrow;
7use std::fmt;
8use std::ops::Deref;
9
10use serde::{Deserialize, Serialize};
11
12#[derive(Clone, Default, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
22#[serde(transparent)]
23pub struct ModelId(String);
24
25impl fmt::Debug for ModelId {
28 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
29 fmt::Debug::fmt(&self.0, formatter)
30 }
31}
32
33impl ModelId {
34 pub fn new(id: impl Into<String>) -> Self {
36 Self(id.into())
37 }
38
39 pub fn as_str(&self) -> &str {
41 &self.0
42 }
43
44 pub fn into_string(self) -> String {
46 self.0
47 }
48}
49
50impl fmt::Display for ModelId {
51 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
52 formatter.write_str(&self.0)
53 }
54}
55
56impl Deref for ModelId {
57 type Target = str;
58
59 fn deref(&self) -> &str {
60 &self.0
61 }
62}
63
64impl AsRef<str> for ModelId {
65 fn as_ref(&self) -> &str {
66 &self.0
67 }
68}
69
70impl Borrow<str> for ModelId {
73 fn borrow(&self) -> &str {
74 &self.0
75 }
76}
77
78impl From<String> for ModelId {
79 fn from(id: String) -> Self {
80 Self(id)
81 }
82}
83
84impl From<&str> for ModelId {
85 fn from(id: &str) -> Self {
86 Self(id.to_string())
87 }
88}
89
90impl From<&ModelId> for ModelId {
93 fn from(id: &ModelId) -> Self {
94 id.clone()
95 }
96}
97
98impl From<ModelId> for String {
99 fn from(id: ModelId) -> Self {
100 id.0
101 }
102}
103
104impl From<&ModelId> for String {
105 fn from(id: &ModelId) -> Self {
106 id.0.clone()
107 }
108}
109
110impl PartialEq<str> for ModelId {
115 fn eq(&self, other: &str) -> bool {
116 self.0 == other
117 }
118}
119
120impl PartialEq<&str> for ModelId {
121 fn eq(&self, other: &&str) -> bool {
122 self.0 == *other
123 }
124}
125
126impl PartialEq<String> for ModelId {
127 fn eq(&self, other: &String) -> bool {
128 &self.0 == other
129 }
130}
131
132impl PartialEq<ModelId> for str {
133 fn eq(&self, other: &ModelId) -> bool {
134 self == other.0
135 }
136}
137
138impl PartialEq<ModelId> for &str {
139 fn eq(&self, other: &ModelId) -> bool {
140 *self == other.0
141 }
142}
143
144impl PartialEq<ModelId> for String {
145 fn eq(&self, other: &ModelId) -> bool {
146 self == &other.0
147 }
148}
149
150#[cfg(test)]
151mod tests {
152 use std::collections::HashMap;
153
154 use super::*;
155
156 #[test]
157 fn it_behaves_like_the_string_it_wraps() {
158 let id = ModelId::new("openai/gpt-oss-20b");
159
160 assert_eq!(id.to_string(), "openai/gpt-oss-20b");
161 assert_eq!(id, "openai/gpt-oss-20b");
162 assert_eq!("openai/gpt-oss-20b", id);
163 assert!(id.starts_with("openai/"));
164 assert_eq!(takes_str(&id), "openai/gpt-oss-20b");
165 }
166
167 fn takes_str(model: &str) -> &str {
169 model
170 }
171
172 #[test]
173 fn a_map_of_ids_is_queryable_by_str() {
174 let by_model = HashMap::from([(ModelId::new("aws/anthropic/claude-opus-4-5"), 1)]);
175
176 assert_eq!(by_model.get("aws/anthropic/claude-opus-4-5"), Some(&1));
177 }
178
179 #[test]
180 fn it_serializes_as_a_bare_string() -> serde_json::Result<()> {
181 let id = ModelId::new("openai/gpt-oss-20b");
182
183 let json = serde_json::to_string(&id)?;
184 assert_eq!(json, "\"openai/gpt-oss-20b\"");
185 assert_eq!(serde_json::from_str::<ModelId>(&json)?, id);
186 Ok(())
187 }
188}