use std::path::Path;
use std::process::Stdio;
use std::time::{Duration, Instant};
use apierror::{error_backend_failure, specialize, ApiError};
use futures::future::BoxFuture;
use tokio::io::AsyncWriteExt;
use tokio::process::Command;
pub mod apierror;
pub mod axum;
pub mod concurrent;
pub mod db;
pub mod hashes;
pub mod shared;
pub mod sigterm;
pub mod token;
pub fn push_if_not_present<T>(v: &mut Vec<T>, item: T) -> bool
where
T: PartialEq<T>,
{
if v.contains(&item) {
false
} else {
v.push(item);
true
}
}
#[must_use]
pub fn stale_instant() -> Instant {
let now = Instant::now();
now.checked_sub(Duration::from_secs(60 * 60 * 24 * 7)).unwrap()
}
pub async fn execute_git(location: &Path, args: &[&str]) -> Result<(), ApiError> {
execute_at_location(location, "git", args, &[]).await.map(|_| ())
}
pub async fn execute_at_location(location: &Path, command: &str, args: &[&str], input: &[u8]) -> Result<Vec<u8>, ApiError> {
let mut child = Command::new(command)
.current_dir(location)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()?;
child.stdin.as_mut().unwrap().write_all(input).await?;
let output = child.wait_with_output().await?;
if output.status.success() {
Ok(output.stdout)
} else {
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let error = format!("-- stdout\n{stdout}\n\n-- stderr\n{stderr}");
Err(specialize(error_backend_failure(), error))
}
}
pub type FaillibleFuture<'a, T> = BoxFuture<'a, Result<T, ApiError>>;
#[must_use]
pub fn comma_sep_to_vec(input: &str) -> Vec<String> {
input
.split(',')
.filter_map(|s| {
let s = s.trim();
if s.is_empty() {
None
} else {
Some(s.to_string())
}
})
.collect::<Vec<_>>()
}