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);
126shared_non_empty_id!(LibraryArtifactId);
127
128#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
135#[serde(transparent)]
136pub struct ManifestSignature(String);
137
138impl ManifestSignature {
139 pub fn new(value: impl Into<String>) -> Self {
140 Self(value.into())
141 }
142
143 pub fn as_str(&self) -> &str {
144 &self.0
145 }
146
147 pub fn into_inner(self) -> String {
148 self.0
149 }
150}
151
152impl fmt::Display for ManifestSignature {
153 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
154 write!(f, "{}", self.0)
155 }
156}
157
158impl AsRef<str> for ManifestSignature {
159 fn as_ref(&self) -> &str {
160 &self.0
161 }
162}
163
164impl From<String> for ManifestSignature {
165 fn from(s: String) -> Self {
166 Self(s)
167 }
168}
169
170impl From<&str> for ManifestSignature {
171 fn from(s: &str) -> Self {
172 Self(s.to_owned())
173 }
174}
175
176#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize)]
180#[serde(transparent)]
181pub struct Sha256Digest(String);
182
183impl Sha256Digest {
184 pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
185 let value = value.into();
186 if value.len() != 64 {
187 return Err(IdValidationError::invalid(
188 "Sha256Digest",
189 format!("expected 64 hex chars, got {}", value.len()),
190 ));
191 }
192 if !value
193 .bytes()
194 .all(|b| matches!(b, b'0'..=b'9' | b'a'..=b'f'))
195 {
196 return Err(IdValidationError::invalid(
197 "Sha256Digest",
198 "expected lowercase hex characters",
199 ));
200 }
201 Ok(Self(value))
202 }
203
204 pub fn as_str(&self) -> &str {
205 &self.0
206 }
207
208 pub fn into_inner(self) -> String {
209 self.0
210 }
211}
212
213impl fmt::Display for Sha256Digest {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 write!(f, "{}", self.0)
216 }
217}
218
219impl AsRef<str> for Sha256Digest {
220 fn as_ref(&self) -> &str {
221 &self.0
222 }
223}
224
225impl From<Sha256Digest> for String {
226 fn from(id: Sha256Digest) -> Self {
227 id.0
228 }
229}
230
231impl TryFrom<String> for Sha256Digest {
232 type Error = IdValidationError;
233 fn try_from(s: String) -> Result<Self, Self::Error> {
234 Self::try_new(s)
235 }
236}
237
238impl TryFrom<&str> for Sha256Digest {
239 type Error = IdValidationError;
240 fn try_from(s: &str) -> Result<Self, Self::Error> {
241 Self::try_new(s)
242 }
243}
244
245impl std::str::FromStr for Sha256Digest {
246 type Err = IdValidationError;
247 fn from_str(s: &str) -> Result<Self, Self::Err> {
248 Self::try_new(s)
249 }
250}
251
252impl<'de> Deserialize<'de> for Sha256Digest {
253 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
254 where
255 D: serde::Deserializer<'de>,
256 {
257 let s = String::deserialize(deserializer)?;
258 Self::try_new(s).map_err(serde::de::Error::custom)
259 }
260}
261
262#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
263#[serde(rename_all = "lowercase")]
264pub enum ToolPolicy {
265 Allow,
266 Deny,
267 Prompt,
268}
269
270impl fmt::Display for ToolPolicy {
271 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
272 match self {
273 Self::Allow => f.write_str("allow"),
274 Self::Deny => f.write_str("deny"),
275 Self::Prompt => f.write_str("prompt"),
276 }
277 }
278}