wasm_pkg_common/
label.rs

1use serde::{Deserialize, Serialize};
2
3/// A Component Model kebab-case label.
4#[derive(Clone, PartialEq, Eq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
5#[serde(into = "String", try_from = "String")]
6pub struct Label(String);
7
8impl AsRef<str> for Label {
9    fn as_ref(&self) -> &str {
10        &self.0
11    }
12}
13
14impl std::fmt::Display for Label {
15    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
16        write!(f, "{}", self.0)
17    }
18}
19
20impl std::fmt::Debug for Label {
21    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22        write!(f, "{:?}", self.0)
23    }
24}
25
26impl From<Label> for String {
27    fn from(value: Label) -> Self {
28        value.0
29    }
30}
31
32impl TryFrom<String> for Label {
33    type Error = InvalidLabel;
34
35    fn try_from(label: String) -> Result<Self, Self::Error> {
36        if label.is_empty() {
37            return Err(InvalidLabel::Empty);
38        }
39        for word in label.split('-') {
40            let mut chars = word.chars();
41            match chars.next() {
42                None => return Err(InvalidLabel::EmptyWord),
43                Some(ch) if !ch.is_ascii_lowercase() => {
44                    return Err(InvalidLabel::InvalidWordFirstChar)
45                }
46                _ => (),
47            }
48            if !chars.all(|ch| ch.is_ascii_lowercase() || ch.is_ascii_digit()) {
49                return Err(InvalidLabel::InvalidChar);
50            }
51        }
52        Ok(Self(label))
53    }
54}
55
56impl std::str::FromStr for Label {
57    type Err = InvalidLabel;
58
59    fn from_str(s: &str) -> Result<Self, Self::Err> {
60        s.to_owned().try_into()
61    }
62}
63
64#[derive(Debug, thiserror::Error)]
65pub enum InvalidLabel {
66    #[error("labels may not be empty")]
67    Empty,
68    #[error("dash-separated words may not be empty")]
69    EmptyWord,
70    #[error("dash-separated words may contain only lowercase alphanumeric ASCII characters")]
71    InvalidChar,
72    #[error("dash-separated words must begin with an ASCII lowercase letter")]
73    InvalidWordFirstChar,
74}