evobench_tools/serde_types/
git_reference.rs

1use std::{fmt::Display, str::FromStr};
2
3use serde::de::Visitor;
4
5#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, serde::Serialize)]
6pub struct GitReference(String);
7
8impl GitReference {
9    pub fn as_str(&self) -> &str {
10        &self.0
11    }
12}
13
14impl AsRef<str> for GitReference {
15    fn as_ref(&self) -> &str {
16        self.as_str()
17    }
18}
19
20impl AsRef<GitReference> for GitReference {
21    fn as_ref(&self) -> &GitReference {
22        self
23    }
24}
25
26impl<'t> From<&'t GitReference> for &'t str {
27    fn from(value: &'t GitReference) -> Self {
28        value.as_str()
29    }
30}
31
32impl From<GitReference> for String {
33    fn from(value: GitReference) -> Self {
34        value.0
35    }
36}
37
38impl Display for GitReference {
39    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40        write!(f, "{}", self.0)
41    }
42}
43
44#[derive(Debug, thiserror::Error)]
45pub enum GitReferenceError {
46    #[error("a git reference string must be non-empty")]
47    IsEmpty,
48    #[error(
49        "a git reference string must not contain whitespace, '/', '.'--\
50         given reference contains {0:?}"
51    )]
52    InvalidCharacter(char),
53}
54
55/// Returns Ok if `s` is a valid Git reference string
56pub fn check_git_reference_string(s: &str) -> Result<(), GitReferenceError> {
57    if s.is_empty() {
58        Err(GitReferenceError::IsEmpty)?
59    }
60    // XX other characters, too
61    if let Some(c) = s
62        .chars()
63        .filter(|c| c.is_whitespace() || *c == '/' || *c == '.')
64        .next()
65    {
66        Err(GitReferenceError::InvalidCharacter(c))?
67    }
68    // Assume it's OK
69    Ok(())
70}
71
72impl FromStr for GitReference {
73    type Err = GitReferenceError;
74
75    fn from_str(s: &str) -> Result<Self, Self::Err> {
76        check_git_reference_string(s)?;
77        Ok(Self(s.into()))
78    }
79}
80
81struct GitReferenceVisitor;
82impl<'de> Visitor<'de> for GitReferenceVisitor {
83    type Value = GitReference;
84
85    fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
86        formatter.write_str("a Git reference string")
87    }
88
89    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
90    where
91        E: serde::de::Error,
92    {
93        v.parse().map_err(E::custom)
94    }
95}
96
97impl<'de> serde::Deserialize<'de> for GitReference {
98    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
99        deserializer.deserialize_str(GitReferenceVisitor)
100    }
101}