Skip to main content

systemprompt_identifiers/
profile.rs

1//! Profile name identifier type with validation.
2//!
3//! Copyright (c) systemprompt.io — Business Source License 1.1.
4//! See <https://systemprompt.io> for licensing details.
5
6use 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    pub fn as_str(&self) -> &str {
43        &self.0
44    }
45
46    #[must_use]
47    pub fn default_profile() -> Self {
48        Self("default".to_owned())
49    }
50}
51
52impl fmt::Display for ProfileName {
53    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
54        write!(f, "{}", self.0)
55    }
56}
57
58impl TryFrom<String> for ProfileName {
59    type Error = IdValidationError;
60
61    fn try_from(s: String) -> Result<Self, Self::Error> {
62        Self::try_new(s)
63    }
64}
65
66impl TryFrom<&str> for ProfileName {
67    type Error = IdValidationError;
68
69    fn try_from(s: &str) -> Result<Self, Self::Error> {
70        Self::try_new(s)
71    }
72}
73
74impl std::str::FromStr for ProfileName {
75    type Err = IdValidationError;
76
77    fn from_str(s: &str) -> Result<Self, Self::Err> {
78        Self::try_new(s)
79    }
80}
81
82impl AsRef<str> for ProfileName {
83    fn as_ref(&self) -> &str {
84        &self.0
85    }
86}
87
88impl<'de> Deserialize<'de> for ProfileName {
89    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
90    where
91        D: serde::Deserializer<'de>,
92    {
93        let s = String::deserialize(deserializer)?;
94        Self::try_new(s).map_err(serde::de::Error::custom)
95    }
96}
97
98impl ToDbValue for ProfileName {
99    fn to_db_value(&self) -> DbValue {
100        DbValue::String(self.0.clone())
101    }
102}
103
104impl ToDbValue for &ProfileName {
105    fn to_db_value(&self) -> DbValue {
106        DbValue::String(self.0.clone())
107    }
108}