systemprompt_models/bridge/
ids.rs1use std::fmt;
18
19use serde::{Deserialize, Serialize};
20
21#[derive(Debug, thiserror::Error)]
22pub enum IdValidationError {
23 #[error("{type_name} cannot be empty")]
24 Empty { type_name: &'static str },
25 #[error("{type_name} is invalid: {reason}")]
26 Invalid {
27 type_name: &'static str,
28 reason: String,
29 },
30}
31
32impl IdValidationError {
33 #[must_use]
34 pub const fn empty(type_name: &'static str) -> Self {
35 Self::Empty { type_name }
36 }
37
38 pub fn invalid(type_name: &'static str, reason: impl Into<String>) -> Self {
39 Self::Invalid {
40 type_name,
41 reason: reason.into(),
42 }
43 }
44}
45
46macro_rules! shared_non_empty_id {
47 ($name:ident) => {
48 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, serde::Serialize)]
49 #[serde(transparent)]
50 pub struct $name(String);
51
52 impl $name {
53 pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
54 let value = value.into();
55 if value.is_empty() {
56 return Err(IdValidationError::empty(stringify!($name)));
57 }
58 Ok(Self(value))
59 }
60
61 pub fn as_str(&self) -> &str {
62 &self.0
63 }
64
65 pub fn into_inner(self) -> String {
66 self.0
67 }
68 }
69
70 impl fmt::Display for $name {
71 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72 write!(f, "{}", self.0)
73 }
74 }
75
76 impl AsRef<str> for $name {
77 fn as_ref(&self) -> &str {
78 &self.0
79 }
80 }
81
82 impl From<$name> for String {
83 fn from(id: $name) -> Self {
84 id.0
85 }
86 }
87
88 impl TryFrom<String> for $name {
89 type Error = IdValidationError;
90 fn try_from(s: String) -> Result<Self, Self::Error> {
91 Self::try_new(s)
92 }
93 }
94
95 impl TryFrom<&str> for $name {
96 type Error = IdValidationError;
97 fn try_from(s: &str) -> Result<Self, Self::Error> {
98 Self::try_new(s)
99 }
100 }
101
102 impl std::str::FromStr for $name {
103 type Err = IdValidationError;
104 fn from_str(s: &str) -> Result<Self, Self::Err> {
105 Self::try_new(s)
106 }
107 }
108
109 impl<'de> serde::Deserialize<'de> for $name {
110 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
111 where
112 D: serde::Deserializer<'de>,
113 {
114 let s = String::deserialize(deserializer)?;
115 Self::try_new(s).map_err(serde::de::Error::custom)
116 }
117 }
118 };
119}
120
121shared_non_empty_id!(PluginId);
122shared_non_empty_id!(SkillId);
123shared_non_empty_id!(SkillName);
124shared_non_empty_id!(ManagedMcpServerName);
125shared_non_empty_id!(ToolName);
126
127#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
134#[serde(transparent)]
135pub struct ManifestSignature(String);
136
137impl ManifestSignature {
138 pub fn new(value: impl Into<String>) -> Self {
139 Self(value.into())
140 }
141
142 pub fn as_str(&self) -> &str {
143 &self.0
144 }
145
146 pub fn into_inner(self) -> String {
147 self.0
148 }
149}
150
151impl fmt::Display for ManifestSignature {
152 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153 write!(f, "{}", self.0)
154 }
155}
156
157impl AsRef<str> for ManifestSignature {
158 fn as_ref(&self) -> &str {
159 &self.0
160 }
161}
162
163impl From<String> for ManifestSignature {
164 fn from(s: String) -> Self {
165 Self(s)
166 }
167}
168
169impl From<&str> for ManifestSignature {
170 fn from(s: &str) -> Self {
171 Self(s.to_string())
172 }
173}
174
175#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
179#[serde(transparent)]
180pub struct Sha256Digest(String);
181
182impl Sha256Digest {
183 pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
184 let value = value.into();
185 if value.len() != 64 {
186 return Err(IdValidationError::invalid(
187 "Sha256Digest",
188 format!("expected 64 hex chars, got {}", value.len()),
189 ));
190 }
191 if !value
192 .bytes()
193 .all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
194 {
195 return Err(IdValidationError::invalid(
196 "Sha256Digest",
197 "expected lowercase hex characters",
198 ));
199 }
200 Ok(Self(value))
201 }
202
203 pub fn as_str(&self) -> &str {
204 &self.0
205 }
206
207 pub fn into_inner(self) -> String {
208 self.0
209 }
210}
211
212impl fmt::Display for Sha256Digest {
213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
214 write!(f, "{}", self.0)
215 }
216}
217
218impl AsRef<str> for Sha256Digest {
219 fn as_ref(&self) -> &str {
220 &self.0
221 }
222}
223
224impl From<Sha256Digest> for String {
225 fn from(id: Sha256Digest) -> Self {
226 id.0
227 }
228}
229
230impl TryFrom<String> for Sha256Digest {
231 type Error = IdValidationError;
232 fn try_from(s: String) -> Result<Self, Self::Error> {
233 Self::try_new(s)
234 }
235}
236
237impl TryFrom<&str> for Sha256Digest {
238 type Error = IdValidationError;
239 fn try_from(s: &str) -> Result<Self, Self::Error> {
240 Self::try_new(s)
241 }
242}
243
244impl std::str::FromStr for Sha256Digest {
245 type Err = IdValidationError;
246 fn from_str(s: &str) -> Result<Self, Self::Err> {
247 Self::try_new(s)
248 }
249}
250
251impl<'de> Deserialize<'de> for Sha256Digest {
252 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
253 where
254 D: serde::Deserializer<'de>,
255 {
256 let s = String::deserialize(deserializer)?;
257 Self::try_new(s).map_err(serde::de::Error::custom)
258 }
259}
260
261#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
262#[serde(rename_all = "lowercase")]
263pub enum ToolPolicy {
264 Allow,
265 Deny,
266 Prompt,
267}
268
269impl fmt::Display for ToolPolicy {
270 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
271 match self {
272 Self::Allow => f.write_str("allow"),
273 Self::Deny => f.write_str("deny"),
274 Self::Prompt => f.write_str("prompt"),
275 }
276 }
277}