zixcel_github/
boundary.rs1use 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}