Skip to main content

soma_provider_core/
id.rs

1use std::{fmt, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
6#[serde(transparent)]
7pub struct ProviderId(String);
8
9impl ProviderId {
10    pub fn new(value: impl Into<String>) -> Result<Self, ProviderIdError> {
11        let value = value.into();
12        if valid_id(&value) {
13            Ok(Self(value))
14        } else {
15            Err(ProviderIdError(value))
16        }
17    }
18
19    pub fn as_str(&self) -> &str {
20        &self.0
21    }
22
23    pub fn into_string(self) -> String {
24        self.0
25    }
26}
27
28impl fmt::Display for ProviderId {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        f.write_str(&self.0)
31    }
32}
33
34impl FromStr for ProviderId {
35    type Err = ProviderIdError;
36
37    fn from_str(value: &str) -> Result<Self, Self::Err> {
38        Self::new(value)
39    }
40}
41
42#[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
43#[error("invalid provider id `{0}`; expected a lowercase provider name")]
44pub struct ProviderIdError(String);
45
46fn valid_id(value: &str) -> bool {
47    let mut chars = value.chars();
48    matches!(chars.next(), Some('a'..='z'))
49        && chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit() || matches!(ch, '-' | '_'))
50        && !value.ends_with(['-', '_'])
51        && !value.contains("--")
52        && !value.contains("__")
53        && !value.contains("-_")
54        && !value.contains("_-")
55}