systemprompt_identifiers/
profile.rs1use crate::error::IdValidationError;
7use crate::{DbValue, ToDbValue};
8use serde::{Deserialize, Serialize};
9use std::fmt;
10
11#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize)]
12#[cfg_attr(feature = "sqlx", derive(sqlx::Type))]
13#[cfg_attr(feature = "sqlx", sqlx(transparent))]
14#[serde(transparent)]
15pub struct ProfileName(String);
16
17impl ProfileName {
18 pub fn try_new(value: impl Into<String>) -> Result<Self, IdValidationError> {
19 let value = value.into();
20 if value.is_empty() {
21 return Err(IdValidationError::empty("ProfileName"));
22 }
23 if value.contains('/') {
24 return Err(IdValidationError::invalid(
25 "ProfileName",
26 "cannot contain path separator '/'",
27 ));
28 }
29 if !value
30 .chars()
31 .all(|c| c.is_alphanumeric() || c == '-' || c == '_')
32 {
33 return Err(IdValidationError::invalid(
34 "ProfileName",
35 "can only contain alphanumeric characters, hyphens, and underscores",
36 ));
37 }
38 Ok(Self(value))
39 }
40
41 #[must_use]
42 #[expect(
43 clippy::expect_used,
44 reason = "infallible constructor reserved for already-validated inputs; untrusted input \
45 must go through try_new"
46 )]
47 pub fn new(value: impl Into<String>) -> Self {
48 Self::try_new(value).expect("ProfileName validation failed")
49 }
50
51 #[must_use]
52 pub fn as_str(&self) -> &str {
53 &self.0
54 }
55
56 #[must_use]
57 pub fn default_profile() -> Self {
58 Self("default".to_owned())
59 }
60}
61
62impl fmt::Display for ProfileName {
63 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
64 write!(f, "{}", self.0)
65 }
66}
67
68impl TryFrom<String> for ProfileName {
69 type Error = IdValidationError;
70
71 fn try_from(s: String) -> Result<Self, Self::Error> {
72 Self::try_new(s)
73 }
74}
75
76impl TryFrom<&str> for ProfileName {
77 type Error = IdValidationError;
78
79 fn try_from(s: &str) -> Result<Self, Self::Error> {
80 Self::try_new(s)
81 }
82}
83
84impl std::str::FromStr for ProfileName {
85 type Err = IdValidationError;
86
87 fn from_str(s: &str) -> Result<Self, Self::Err> {
88 Self::try_new(s)
89 }
90}
91
92impl AsRef<str> for ProfileName {
93 fn as_ref(&self) -> &str {
94 &self.0
95 }
96}
97
98impl<'de> Deserialize<'de> for ProfileName {
99 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
100 where
101 D: serde::Deserializer<'de>,
102 {
103 let s = String::deserialize(deserializer)?;
104 Self::try_new(s).map_err(serde::de::Error::custom)
105 }
106}
107
108impl ToDbValue for ProfileName {
109 fn to_db_value(&self) -> DbValue {
110 DbValue::String(self.0.clone())
111 }
112}
113
114impl ToDbValue for &ProfileName {
115 fn to_db_value(&self) -> DbValue {
116 DbValue::String(self.0.clone())
117 }
118}