xtask-toolkit 0.1.12

A collection of useful tools for xtask building
Documentation
use std::path::PathBuf;

use chrono::{DateTime, Utc};
use xshell::{Shell, cmd};

pub fn create_and_push_tag(tag: &str) -> Result<(), xshell::Error> {
    let sh = Shell::new()?;

    cmd!(sh, "git tag {tag}").run()?;
    cmd!(sh, "git push origin {tag}").run()?;
    Ok(())
}

pub fn has_tag(tag: &str) -> Result<bool, xshell::Error> {
    let sh = Shell::new()?;

    let tags = cmd!(sh, "git tag")
        .read()?
        .split('\n')
        .map(|x| x.trim().to_string())
        .collect::<Vec<_>>();

    Ok(tags.iter().any(|x| x == tag))
}

pub fn unstaged_changes() -> Result<bool, xshell::Error> {
    let sh = Shell::new()?;
    Ok(!cmd!(sh, "git status --porcelain").read()?.is_empty())
}

#[derive(Debug, thiserror::Error)]
pub enum LastCommitError {
    #[error(transparent)]
    XShellError(xshell::Error),
    #[error("Could not parse timestamp value. Not an integer")]
    ParseIntError,
    #[error("Not a timestamp")]
    NotATimestamp,
}

impl From<xshell::Error> for LastCommitError {
    fn from(value: xshell::Error) -> Self {
        LastCommitError::XShellError(value)
    }
}

pub fn last_commit_date() -> Result<DateTime<Utc>, LastCommitError> {
    let sh = Shell::new()?;
    DateTime::from_timestamp(
        cmd!(sh, "git show --no-patch --format=%ct HEAD")
            .read()?
            .parse()
            .map_err(|_| LastCommitError::ParseIntError)?,
        0,
    )
    .ok_or_else(|| LastCommitError::NotATimestamp)
}

pub fn get_root_path() -> Result<PathBuf, xshell::Error> {
    let sh = Shell::new()?;
    Ok(PathBuf::from(cmd!(sh, "git rev-parse --show-toplevel").read()?))
}

pub struct OriginUrl(pub String);

impl OriginUrl {
    pub fn new() -> Result<OriginUrl, xshell::Error> {
        let sh = Shell::new()?;
        Ok(OriginUrl(cmd!(sh, "git remote get-url origin").read()?))
    }

    pub fn to_http(mut self) -> Result<Self, ()> {
        if self.0.starts_with("https://") || self.0.starts_with("http://") {
            Ok(self)
        } else if self.0.starts_with("git@") && self.0.contains("github") {
            self.0 = self.0.replace("git@", "https://").replacen(":", "/", 1);
            Ok(self)
        } else {
            Err(())
        }
    }

    pub fn get(&self) -> &str {
        &self.0
    }
}