use std::path::Path;
use std::process::Command;
use crate::error::{GitError, Result};
use chrono::{DateTime, Utc};
pub fn git(args: &[&str], working_dir: Option<&Path>) -> Result<String> {
let output = git_raw(args, working_dir)?;
if !output.status.success() {
let error_msg = String::from_utf8_lossy(&output.stderr);
return Err(GitError::CommandFailed(format!(
"git {} failed: {}",
args.first().unwrap_or(&"<unknown>"),
error_msg
)));
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub fn git_raw(args: &[&str], working_dir: Option<&Path>) -> Result<std::process::Output> {
let mut cmd = Command::new("git");
cmd.args(args);
if let Some(dir) = working_dir {
cmd.current_dir(dir);
}
cmd.output().map_err(GitError::from)
}
pub fn parse_unix_timestamp(timestamp_str: &str) -> Result<DateTime<Utc>> {
if timestamp_str.is_empty() {
return Ok(Utc::now());
}
let timestamp: i64 = timestamp_str
.parse()
.map_err(|_| GitError::CommandFailed(format!("Invalid timestamp: {}", timestamp_str)))?;
DateTime::from_timestamp(timestamp, 0)
.ok_or_else(|| GitError::CommandFailed(format!("Invalid timestamp value: {}", timestamp)))
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
use std::path::Path;
#[test]
fn test_git_raw_version_command() {
let result = git_raw(&["--version"], None);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.status.success());
assert!(!output.stdout.is_empty());
let stdout_str = String::from_utf8_lossy(&output.stdout);
assert!(stdout_str.contains("git version"));
}
#[test]
fn test_git_version_command() {
let result = git(&["--version"], None);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.contains("git version"));
}
#[test]
fn test_git_raw_invalid_command() {
let result = git_raw(&["invalid-command-that-does-not-exist"], None);
assert!(result.is_ok());
let output = result.unwrap();
assert!(!output.status.success());
assert!(!output.stderr.is_empty());
}
#[test]
fn test_git_invalid_command_returns_error() {
let result = git(&["invalid-command-that-does-not-exist"], None);
assert!(result.is_err());
match result.unwrap_err() {
GitError::CommandFailed(msg) => {
assert!(msg.contains("git invalid-command-that-does-not-exist failed"));
}
_ => panic!("Expected CommandFailed error"),
}
}
#[test]
fn test_git_with_working_directory() {
let temp_dir = env::temp_dir();
let result = git(&["--version"], Some(&temp_dir));
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.contains("git version"));
}
#[test]
fn test_git_raw_with_working_directory() {
let temp_dir = env::temp_dir();
let result = git_raw(&["--version"], Some(&temp_dir));
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.status.success());
}
#[test]
fn test_git_with_nonexistent_working_directory() {
let nonexistent_path = Path::new("/nonexistent/path/that/should/not/exist");
let result = git(&["--version"], Some(nonexistent_path));
assert!(result.is_err());
match result.unwrap_err() {
GitError::IoError(_) => {} _ => panic!("Expected IoError for nonexistent directory"),
}
}
#[test]
fn test_git_raw_with_nonexistent_working_directory() {
let nonexistent_path = Path::new("/nonexistent/path/that/should/not/exist");
let result = git_raw(&["--version"], Some(nonexistent_path));
assert!(result.is_err());
match result.unwrap_err() {
GitError::IoError(_) => {} _ => panic!("Expected IoError for nonexistent directory"),
}
}
#[test]
fn test_git_empty_args() {
let result = git(&[], None);
assert!(result.is_err());
match result.unwrap_err() {
GitError::CommandFailed(msg) => {
assert!(msg.contains("git <unknown> failed") || msg.contains("usage"));
}
_ => panic!("Expected CommandFailed error for empty args"),
}
}
#[test]
fn test_git_raw_empty_args() {
let result = git_raw(&[], None);
assert!(result.is_ok());
let output = result.unwrap();
assert!(!output.status.success());
}
#[test]
fn test_git_help_command() {
let result = git(&["--help"], None);
assert!(result.is_ok());
let output = result.unwrap();
assert!(output.contains("usage:") || output.contains("Git") || output.contains("git"));
}
#[test]
fn test_parse_unix_timestamp() {
let timestamp = "1642694400"; let result = parse_unix_timestamp(timestamp);
assert!(result.is_ok());
let datetime = result.unwrap();
assert_eq!(datetime.timestamp(), 1642694400);
let result = parse_unix_timestamp("");
assert!(result.is_ok());
let result = parse_unix_timestamp("invalid");
assert!(result.is_err());
let result = parse_unix_timestamp("999999999999999999");
assert!(result.is_err());
}
}