use std::future::Future;
use std::path::{Path, PathBuf};
use anyhow::{Context, Result, bail};
use crate::model::Server;
pub fn project_dir(path: &Path) -> Result<PathBuf> {
let path = std::path::absolute(path).with_context(|| format!("cannot resolve {}", path.display()))?;
if !path.is_dir() {
bail!("directory {} does not exist", path.display());
}
dunce::canonicalize(&path).with_context(|| format!("cannot canonicalize {}", path.display()))
}
pub fn run_in_dir(command_line: &str, dir: &Path) -> Result<std::process::ExitStatus> {
#[cfg(windows)]
let mut command = {
let mut command = std::process::Command::new("cmd");
command.args(["/C", command_line]);
command
};
#[cfg(not(windows))]
let mut command = {
let mut command = std::process::Command::new("sh");
command.args(["-c", command_line]);
command
};
command.current_dir(dir);
let mut child = command.spawn().with_context(|| format!("cannot run '{command_line}'"))?;
crate::term::confine(&child);
crate::term::child_begin();
let status = child.wait();
crate::term::child_end();
status.with_context(|| format!("cannot run '{command_line}'"))
}
pub fn civil_from_days(days: i64) -> (i64, u32, u32) {
let z = days + 719_468;
let era = z.div_euclid(146_097);
let doe = z.rem_euclid(146_097);
let yoe = (doe - doe / 1_460 + doe / 36_524 - doe / 146_096) / 365;
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
let mp = (5 * doy + 2) / 153;
let day = (doy - (153 * mp + 2) / 5 + 1) as u32;
let month = if mp < 10 { mp + 3 } else { mp - 9 } as u32;
let year = yoe + era * 400 + i64::from(month <= 2);
(year, month, day)
}
pub fn run_blocking<T>(future: impl Future<Output = Result<T>>) -> Result<T> {
let runtime = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()
.context("cannot start a runtime")?;
runtime.block_on(future)
}
pub fn check_reachable(server: &Server) -> Result<reqwest::StatusCode> {
run_blocking(async {
let client = reqwest::Client::builder()
.redirect(reqwest::redirect::Policy::none())
.danger_accept_invalid_certs(server.accept_invalid_certs)
.timeout(std::time::Duration::from_secs(4))
.build()?;
let response = client.get(&server.url).send().await.with_context(|| format!("{} is unreachable", server.url))?;
Ok(response.status())
})
}
#[cfg(test)]
mod tests {
use super::civil_from_days;
#[test]
fn days_since_the_epoch_become_calendar_dates() {
assert_eq!(civil_from_days(0), (1970, 1, 1));
assert_eq!(civil_from_days(11_016), (2000, 2, 29));
assert_eq!(civil_from_days(11_017), (2000, 3, 1));
assert_eq!(civil_from_days(19_782), (2024, 2, 29));
assert_eq!(civil_from_days(19_723), (2024, 1, 1));
assert_eq!(civil_from_days(20_313), (2025, 8, 13));
}
}