1use std::path::PathBuf;
2
3use chrono::{DateTime, Utc};
4use xshell::{Shell, cmd};
5
6pub fn create_and_push_tag(tag: &str) -> Result<(), xshell::Error> {
7 let sh = Shell::new()?;
8
9 cmd!(sh, "git tag {tag}").run()?;
10 cmd!(sh, "git push origin {tag}").run()?;
11 Ok(())
12}
13
14pub fn has_tag(tag: &str) -> Result<bool, xshell::Error> {
15 let sh = Shell::new()?;
16
17 let tags = cmd!(sh, "git tag")
18 .read()?
19 .split('\n')
20 .map(|x| x.trim().to_string())
21 .collect::<Vec<_>>();
22
23 Ok(tags.iter().any(|x| x == tag))
24}
25
26pub fn unstaged_changes() -> Result<bool, xshell::Error> {
27 let sh = Shell::new()?;
28 Ok(!cmd!(sh, "git status --porcelain").read()?.is_empty())
29}
30
31#[derive(Debug, thiserror::Error)]
32pub enum LastCommitError {
33 #[error(transparent)]
34 XShellError(xshell::Error),
35 #[error("Could not parse timestamp value. Not an integer")]
36 ParseIntError,
37 #[error("Not a timestamp")]
38 NotATimestamp,
39}
40
41impl From<xshell::Error> for LastCommitError {
42 fn from(value: xshell::Error) -> Self {
43 LastCommitError::XShellError(value)
44 }
45}
46
47pub fn last_commit_date() -> Result<DateTime<Utc>, LastCommitError> {
48 let sh = Shell::new()?;
49 DateTime::from_timestamp(
50 cmd!(sh, "git show --no-patch --format=%ct HEAD")
51 .read()?
52 .parse()
53 .map_err(|_| LastCommitError::ParseIntError)?,
54 0,
55 )
56 .ok_or_else(|| LastCommitError::NotATimestamp)
57}
58
59pub fn get_root_path() -> Result<PathBuf, xshell::Error> {
60 let sh = Shell::new()?;
61 Ok(PathBuf::from(cmd!(sh, "git rev-parse --show-toplevel").read()?))
62}
63
64pub struct OriginUrl(pub String);
65
66impl OriginUrl {
67 pub fn new() -> Result<OriginUrl, xshell::Error> {
68 let sh = Shell::new()?;
69 Ok(OriginUrl(cmd!(sh, "git remote get-url origin").read()?))
70 }
71
72 pub fn to_http(mut self) -> Result<Self, ()> {
73 if self.0.starts_with("https://") || self.0.starts_with("http://") {
74 Ok(self)
75 } else if self.0.starts_with("git@") && self.0.contains("github") {
76 self.0 = self.0.replace("git@", "https://").replacen(":", "/", 1);
77 Ok(self)
78 } else {
79 Err(())
80 }
81 }
82
83 pub fn get(&self) -> &str {
84 &self.0
85 }
86}