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