Skip to main content

gix_date/parse/
mod.rs

1use std::io;
2use std::str::FromStr;
3
4use crate::{Error, Time};
5
6/// A container for just enough bytes to hold the largest possible serialization of a [`Time`].
7#[derive(Default, Clone)]
8pub struct TimeBuf {
9    /// One past the last byte written into `buf`.
10    end_idx: usize,
11    /// Fixed storage large enough for the widest raw time serialization.
12    buf: [u8; Time::MIN.size()],
13}
14
15/// Adapter to make [`TimeBuf`] writable only while `Time::to_str()` serializes into it.
16///
17/// This keeps [`TimeBuf`] as plain reusable storage: callers can inspect the finished bytes through
18/// [`TimeBuf::as_str()`], but cannot append arbitrary bytes or leave it in a partially-written state.
19struct TimeBufWriter<'a>(&'a mut TimeBuf);
20
21impl io::Write for TimeBufWriter<'_> {
22    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
23        let idx = self.0.end_idx;
24        let end_idx = idx
25            .checked_add(buf.len())
26            .ok_or_else(|| io::Error::from(io::ErrorKind::OutOfMemory))?;
27        if end_idx > Time::MIN.size() {
28            return Err(io::Error::from(io::ErrorKind::StorageFull));
29        }
30        self.0.buf[idx..end_idx].copy_from_slice(buf);
31        self.0.end_idx = end_idx;
32        Ok(buf.len())
33    }
34
35    fn flush(&mut self) -> io::Result<()> {
36        Ok(())
37    }
38}
39
40impl TimeBuf {
41    /// Represent this instance as standard string, serialized in a format compatible with
42    /// signature fields in Git commits, also known as anything parseable as [raw format](function::parse_header()).
43    pub fn as_str(&self) -> &str {
44        // Time serializes as ASCII, which is a subset of UTF-8.
45        let time_bytes = &self.buf[..self.end_idx];
46        std::str::from_utf8(time_bytes).expect("time serializes as valid UTF-8")
47    }
48
49    /// Clear the previous content.
50    fn clear(&mut self) {
51        self.end_idx = 0;
52    }
53}
54
55impl Time {
56    /// Serialize this instance into `buf`, exactly as it would appear in the header of a Git commit,
57    /// and return `buf` as `&str` for easy consumption.
58    pub fn to_str<'a>(&self, buf: &'a mut TimeBuf) -> &'a str {
59        buf.clear();
60        self.write_to(&mut TimeBufWriter(buf))
61            .expect("write to memory of just the right size cannot fail");
62        buf.as_str()
63    }
64}
65
66impl FromStr for Time {
67    type Err = Error;
68
69    fn from_str(s: &str) -> Result<Self, Self::Err> {
70        crate::parse_header(s).ok_or_else(|| Error::new_with_input("invalid time", s))
71    }
72}
73
74pub(crate) mod function;
75mod git;
76mod raw;
77mod relative;