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