radicle_git_metadata/
author.rs1use std::{
2 fmt,
3 num::ParseIntError,
4 str::{self, FromStr},
5};
6
7use thiserror::Error;
8
9#[derive(Clone, Debug, PartialEq, Eq, Hash)]
12pub struct Author {
13 pub name: String,
17 pub email: String,
21 pub time: Time,
23}
24
25#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
27pub struct Time {
28 seconds: i64,
29 offset: i32,
30}
31
32impl Time {
33 pub fn new(seconds: i64, offset: i32) -> Self {
34 Self { seconds, offset }
35 }
36
37 pub fn seconds(&self) -> i64 {
39 self.seconds
40 }
41
42 pub fn offset(&self) -> i32 {
44 self.offset
45 }
46
47 fn from_components<'a>(cs: &mut impl Iterator<Item = &'a str>) -> Result<Self, ParseError> {
48 let offset = match cs.next() {
49 None => Err(ParseError::Missing("offset")),
50 Some(offset) => Self::parse_offset(offset).map_err(ParseError::Offset),
51 }?;
52 let time = match cs.next() {
53 None => return Err(ParseError::Missing("time")),
54 Some(time) => time.parse::<i64>().map_err(ParseError::Time)?,
55 };
56 Ok(Self::new(time, offset))
57 }
58
59 fn parse_offset(offset: &str) -> Result<i32, ParseIntError> {
60 let tz_offset = offset.parse::<i32>()?;
66 let hours = tz_offset / 100;
67 let minutes = tz_offset % 100;
68 Ok(hours * 60 + minutes)
69 }
70}
71
72impl FromStr for Time {
73 type Err = ParseError;
74
75 fn from_str(s: &str) -> Result<Self, Self::Err> {
76 Self::from_components(&mut s.split(' ').rev())
77 }
78}
79
80impl fmt::Display for Time {
81 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
82 let sign = if self.offset.is_negative() { '-' } else { '+' };
83 let hours = self.offset.abs() / 60;
84 let minutes = self.offset.abs() % 60;
85 write!(f, "{} {}{:0>2}{:0>2}", self.seconds, sign, hours, minutes)
86 }
87}
88
89impl fmt::Display for Author {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 write!(f, "{} <{}> {}", self.name, self.email, self.time,)
92 }
93}
94
95#[derive(Debug, Error)]
96pub enum ParseError {
97 #[error("missing '{0}' while parsing person signature")]
98 Missing(&'static str),
99 #[error("offset was incorrect format while parsing person signature")]
100 Offset(#[source] ParseIntError),
101 #[error("time was incorrect format while parsing person signature")]
102 Time(#[source] ParseIntError),
103}
104
105impl FromStr for Author {
106 type Err = ParseError;
107
108 fn from_str(s: &str) -> Result<Self, Self::Err> {
109 let mut components = s.rsplitn(4, ' ');
112 let time = Time::from_components(&mut components)?;
113 let email = components
114 .next()
115 .ok_or(ParseError::Missing("email"))?
116 .trim_matches(|c| c == '<' || c == '>')
117 .to_owned();
118 let name = components.next().ok_or(ParseError::Missing("name"))?;
119 Ok(Self {
120 name: name.to_owned(),
121 email: email.to_owned(),
122 time,
123 })
124 }
125}