Skip to main content

radicle_git_metadata/commit/
trailers.rs

1use std::{borrow::Cow, fmt, ops::Deref};
2
3pub trait Separator<'a> {
4    fn sep_for(&self, token: &Token) -> &'a str;
5}
6
7impl<'a> Separator<'a> for &'a str {
8    fn sep_for(&self, _: &Token) -> &'a str {
9        self
10    }
11}
12
13impl<'a, F> Separator<'a> for F
14where
15    F: Fn(&Token) -> &'a str,
16{
17    fn sep_for(&self, token: &Token) -> &'a str {
18        self(token)
19    }
20}
21
22#[derive(Debug, Clone, Eq, PartialEq)]
23pub struct Token<'a>(&'a str);
24
25impl Deref for Token<'_> {
26    type Target = str;
27
28    fn deref(&self) -> &Self::Target {
29        self.0
30    }
31}
32
33impl<'a> TryFrom<&'a str> for Token<'a> {
34    type Error = &'static str;
35
36    fn try_from(s: &'a str) -> Result<Self, Self::Error> {
37        let is_token = s.chars().all(|c| c.is_alphanumeric() || c == '-');
38        if is_token {
39            Ok(Token(s))
40        } else {
41            Err("token contains invalid characters")
42        }
43    }
44}
45
46pub struct Display<'a> {
47    trailer: &'a Trailer<'a>,
48    separator: &'a str,
49}
50
51impl fmt::Display for Display<'_> {
52    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
53        write!(
54            f,
55            "{}{}{}",
56            self.trailer.token.deref(),
57            self.separator,
58            self.trailer.value,
59        )
60    }
61}
62
63/// A trailer is a key/value pair found in the last paragraph of a Git
64/// commit message, not including any patches or conflicts that may be
65/// present.
66#[derive(Debug, Clone, Eq, PartialEq)]
67pub struct Trailer<'a> {
68    pub token: Token<'a>,
69    pub value: Cow<'a, str>,
70}
71
72impl<'a> Trailer<'a> {
73    pub fn display(&'a self, separator: &'a str) -> Display<'a> {
74        Display {
75            trailer: self,
76            separator,
77        }
78    }
79
80    pub fn to_owned(&self) -> OwnedTrailer {
81        OwnedTrailer::from(self)
82    }
83}
84
85/// A version of the [`Trailer`] which owns its token and
86/// value. Useful for when you need to carry trailers around in a long
87/// lived data structure.
88#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
89pub struct OwnedTrailer {
90    pub token: OwnedToken,
91    pub value: String,
92}
93
94#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
95pub struct OwnedToken(String);
96
97impl Deref for OwnedToken {
98    type Target = str;
99
100    fn deref(&self) -> &Self::Target {
101        &self.0
102    }
103}
104
105impl<'a> From<&Trailer<'a>> for OwnedTrailer {
106    fn from(t: &Trailer<'a>) -> Self {
107        OwnedTrailer {
108            token: OwnedToken(t.token.0.to_string()),
109            value: t.value.to_string(),
110        }
111    }
112}
113
114impl<'a> From<Trailer<'a>> for OwnedTrailer {
115    fn from(t: Trailer<'a>) -> Self {
116        (&t).into()
117    }
118}
119
120impl<'a> From<&'a OwnedTrailer> for Trailer<'a> {
121    fn from(t: &'a OwnedTrailer) -> Self {
122        Trailer {
123            token: Token(t.token.0.as_str()),
124            value: Cow::from(&t.value),
125        }
126    }
127}