semver_common/models/
commit.rs1use crate::models::Alert;
2use chrono::{DateTime, FixedOffset};
3use derive_getters::Getters;
4use serde::{Deserialize, Deserializer, Serialize, Serializer};
5use std::fmt::{self, Display, Formatter};
6
7const COMMIT_TIME_FORMAT: &str = "%a %b %d %H:%M:%S %Y %z";
8
9mod datetime_ser {
10 use serde::de;
11
12 use super::*;
13
14 pub fn serialize<S>(ext: &DateTime<FixedOffset>, serializer: S) -> Result<S::Ok, S::Error>
15 where
16 S: Serializer,
17 {
18 serializer.serialize_str(&ext.to_string())
19 }
20
21 pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<FixedOffset>, D::Error>
22 where
23 D: Deserializer<'de>,
24 {
25 println!("Begin deserialize");
26 let s = String::deserialize(deserializer)?;
27 println!("{}", s);
28 let dt = match DateTime::parse_from_str(&s, COMMIT_TIME_FORMAT) {
29 Ok(v) => v,
30 Err(e) => return Err(de::Error::custom(e.to_string())),
31 };
32 println!("After datetime");
33 Ok(dt)
34 }
35}
36
37#[derive(Serialize, Deserialize, Clone, Debug, Getters)]
38pub struct Commit {
39 id: String,
40 author: String,
41
42 #[serde(with = "datetime_ser")]
43 timestamp: DateTime<FixedOffset>,
44
45 message: String,
46}
47
48impl Ord for Commit {
49 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
50 if self.message > other.message {
51 return std::cmp::Ordering::Greater;
52 } else if self.message < other.message {
53 return std::cmp::Ordering::Less;
54 }
55 std::cmp::Ordering::Equal
56 }
57}
58
59impl Eq for Commit {}
60
61impl PartialOrd for Commit {
62 fn ge(&self, other: &Self) -> bool {
63 self.message >= other.message
64 }
65
66 fn gt(&self, other: &Self) -> bool {
67 self.message > other.message
68 }
69
70 fn le(&self, other: &Self) -> bool {
71 self.message <= other.message
72 }
73
74 fn lt(&self, other: &Self) -> bool {
75 self.message < other.message
76 }
77
78 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
79 Some(self.cmp(other))
80 }
81}
82
83impl PartialEq for Commit {
84 fn eq(&self, other: &Self) -> bool {
85 self.message == other.message
86 }
87}
88
89impl Display for Commit {
90 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
91 writeln!(f, "{}", self.message)
92 }
93}
94
95impl Commit {
96 pub fn new(id: &str, author: &str, timestamp: DateTime<FixedOffset>, message: &str) -> Self {
97 Commit {
98 id: id.to_string(),
99 author: author.to_string(),
100 timestamp,
101 message: message.to_string(),
102 }
103 }
104
105 pub fn new_from_str(
107 id: &str,
108 author: &str,
109 timestamp: &str,
110 message: &str,
111 ) -> Result<Self, Alert> {
112 let parsed_timestamp = DateTime::parse_from_str(timestamp, COMMIT_TIME_FORMAT)?;
113 Ok(Commit::new(id, author, parsed_timestamp, message))
114 }
115
116 pub fn new_from_commit(commit: String) -> Result<Self, Alert> {
143 let lines: Vec<&str> = commit.split("\n").collect();
144 if lines.len() > 3 {
145 let id_line: (&str, &str) = lines[0].split_once(" ").unwrap_or((lines[0], ""));
146 let commit_id = id_line.0.trim();
147 let author_line: (&str, &str) = lines[1]
148 .split_once(":")
149 .ok_or("Could not parse author line of commit.")?;
150 let author = author_line.1.trim();
151 let date_line: (&str, &str) = lines[2]
152 .split_once(":")
153 .ok_or("Could not parse date line of commit.")?;
154 let date = date_line.1.trim();
155 let commit_end_line: usize = lines.len() - 1;
156 let commit_message_untrimmed = lines[4..commit_end_line].join("\n");
157 let commit_message = commit_message_untrimmed.trim();
158 let object = Commit::new_from_str(commit_id, author, date, commit_message)?;
159 return Ok(object);
160 }
161 Err(Alert::from("Commit is not valid"))
162 }
163
164 pub fn msg(&self) -> &str {
165 &self.message
166 }
167}
168#[cfg(test)]
169mod test {
170 use super::*;
171
172 #[test]
173 fn test_commit_new_top_commit() {
174 let c = String::from(
175 "490049bf36b19b30d23b4be5a4u94f71b5c6475c (HEAD -> master)
176Author: Some Author <myemail@email.com>
177Date: Tue Apr 14 17:35:15 2026 -0400
178
179 feat: added feature to get commit list
180",
181 );
182 let commit =
183 Commit::new_from_commit(c).expect("Commit could not be instantiated during test.");
184 assert_eq!(commit.id, "490049bf36b19b30d23b4be5a4u94f71b5c6475c");
185 assert_eq!(commit.author, "Some Author <myemail@email.com>");
186 assert_eq!(
187 commit.timestamp,
188 DateTime::parse_from_str("Tue Apr 14 17:35:15 2026 -0400", COMMIT_TIME_FORMAT).unwrap()
189 );
190 assert_eq!(commit.message, "feat: added feature to get commit list");
191 }
192
193 #[test]
194 fn test_commit_new_commit() {
195 let c = String::from(
196 "490049bf36b19b30d23b4be5a4u94f71b5c6475c
197Author: Some Author <myemail@email.com>
198Date: Tue Apr 14 17:35:15 2026 -0400
199
200 feat: added feature to get commit list
201",
202 );
203 let commit =
204 Commit::new_from_commit(c).expect("Commit could not be instantiated during test.");
205 assert_eq!(commit.id, "490049bf36b19b30d23b4be5a4u94f71b5c6475c");
206 assert_eq!(commit.author, "Some Author <myemail@email.com>");
207 assert_eq!(
208 commit.timestamp,
209 DateTime::parse_from_str("Tue Apr 14 17:35:15 2026 -0400", COMMIT_TIME_FORMAT).unwrap()
210 );
211 assert_eq!(commit.message, "feat: added feature to get commit list");
212 }
213}