git_commit/
headers.rs

1use std::borrow::Cow;
2
3const BEGIN_SSH: &str = "-----BEGIN SSH SIGNATURE-----\n";
4const BEGIN_PGP: &str = "-----BEGIN PGP SIGNATURE-----\n";
5
6/// A collection of headers stored in a [`super::Commit`].
7///
8/// Note: these do not include `tree`, `parent`, `author`, and `committer`.
9#[derive(Clone, Debug, Default)]
10pub struct Headers(pub(super) Vec<(String, String)>);
11
12/// A `gpgsig` signature stored in a [`super::Commit`].
13pub enum Signature<'a> {
14    /// A PGP signature, i.e. starts with `-----BEGIN PGP SIGNATURE-----`.
15    Pgp(Cow<'a, str>),
16    /// A SSH signature, i.e. starts with `-----BEGIN SSH SIGNATURE-----`.
17    Ssh(Cow<'a, str>),
18}
19
20impl<'a> Signature<'a> {
21    fn from_str(s: &'a str) -> Result<Self, UnknownScheme> {
22        if s.starts_with(BEGIN_SSH) {
23            Ok(Signature::Ssh(Cow::Borrowed(s)))
24        } else if s.starts_with(BEGIN_PGP) {
25            Ok(Signature::Pgp(Cow::Borrowed(s)))
26        } else {
27            Err(UnknownScheme)
28        }
29    }
30}
31
32pub struct UnknownScheme;
33
34impl<'a> ToString for Signature<'a> {
35    fn to_string(&self) -> String {
36        match self {
37            Signature::Pgp(pgp) => pgp.to_string(),
38            Signature::Ssh(ssh) => ssh.to_string(),
39        }
40    }
41}
42
43impl Headers {
44    pub fn new() -> Self {
45        Headers(Vec::new())
46    }
47
48    pub fn iter(&self) -> impl Iterator<Item = (&str, &str)> {
49        self.0.iter().map(|(k, v)| (k.as_str(), v.as_str()))
50    }
51
52    pub fn values<'a>(&'a self, name: &'a str) -> impl Iterator<Item = &'a str> + '_ {
53        self.iter()
54            .filter_map(move |(k, v)| (k == name).then_some(v))
55    }
56
57    pub fn signatures(&self) -> impl Iterator<Item = Signature> + '_ {
58        self.0.iter().filter_map(|(k, v)| {
59            if k == "gpgsig" {
60                Signature::from_str(v).ok()
61            } else {
62                None
63            }
64        })
65    }
66
67    /// Push a header to the end of the headers section.
68    pub fn push(&mut self, name: &str, value: &str) {
69        self.0.push((name.to_owned(), value.trim().to_owned()));
70    }
71}