Skip to main content

zixcel_github/
boundary.rs

1use std::fmt;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4pub struct ConnectorError {
5    pub field: &'static str,
6    pub message: &'static str,
7}
8
9impl ConnectorError {
10    pub(crate) const fn new(field: &'static str, message: &'static str) -> Self {
11        Self { field, message }
12    }
13}
14
15impl fmt::Display for ConnectorError {
16    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
17        write!(formatter, "{}: {}", self.field, self.message)
18    }
19}
20
21impl std::error::Error for ConnectorError {}
22
23pub(crate) fn identifier(field: &'static str, value: &str) -> Result<(), ConnectorError> {
24    if value.is_empty()
25        || value.len() > 96
26        || !value.bytes().all(|byte| {
27            byte.is_ascii_lowercase() || byte.is_ascii_digit() || matches!(byte, b'-' | b'_' | b'.')
28        })
29    {
30        return Err(ConnectorError::new(
31            field,
32            "must be a lowercase ASCII identifier",
33        ));
34    }
35    Ok(())
36}
37
38pub(crate) fn github_name(field: &'static str, value: &str) -> Result<(), ConnectorError> {
39    if value.is_empty()
40        || value.len() > 100
41        || value.starts_with('.')
42        || value.ends_with('.')
43        || !value
44            .bytes()
45            .all(|byte| byte.is_ascii_alphanumeric() || matches!(byte, b'-' | b'_' | b'.'))
46    {
47        return Err(ConnectorError::new(
48            field,
49            "contains an invalid GitHub name",
50        ));
51    }
52    Ok(())
53}
54
55pub(crate) fn digest(field: &'static str, value: &str) -> Result<(), ConnectorError> {
56    if value.len() != 64
57        || !value
58            .bytes()
59            .all(|byte| byte.is_ascii_digit() || matches!(byte, b'a'..=b'f'))
60    {
61        return Err(ConnectorError::new(
62            field,
63            "must be 64 lowercase hex characters",
64        ));
65    }
66    Ok(())
67}
68
69pub(crate) fn reference(field: &'static str, value: &str) -> Result<(), ConnectorError> {
70    if value.is_empty()
71        || value.len() > 256
72        || value.trim() != value
73        || value.bytes().any(|byte| byte.is_ascii_control())
74    {
75        return Err(ConnectorError::new(
76            field,
77            "must be a bounded opaque reference",
78        ));
79    }
80    Ok(())
81}
82
83pub(crate) fn bounded_text(
84    field: &'static str,
85    value: &str,
86    maximum: usize,
87) -> Result<(), ConnectorError> {
88    if value.is_empty()
89        || value.len() > maximum
90        || value.trim() != value
91        || value.bytes().any(|byte| byte.is_ascii_control())
92    {
93        return Err(ConnectorError::new(field, "contains invalid bounded text"));
94    }
95    Ok(())
96}