use std::ops::Range;
use std::process::{Command, Stdio};
use std::time::{Duration, Instant};
use crate::rules::{Context, Evidence, Stance};
use crate::shell::Parsed;
pub mod push_landed;
pub mod push_published;
pub struct Claim {
#[allow(dead_code)]
pub what: String,
pub span: Range<usize>,
}
pub enum Verdict {
Held,
Broken { reason: String, remedy: String },
Unknown(&'static str),
Noted(&'static str),
}
pub struct Assertion {
pub id: &'static str,
pub default_stance: Stance,
pub evidence: Evidence,
pub examine: fn(&Parsed) -> Option<Claim>,
pub verify: fn(&Context, &Claim) -> Verdict,
}
pub const ASSERTIONS: &[Assertion] = &[push_landed::ASSERTION, push_published::ASSERTION];
#[allow(dead_code)]
pub fn by_id(id: &str) -> Option<&'static Assertion> {
ASSERTIONS.iter().find(|a| a.id == id)
}
pub fn examine_all(parsed: &Parsed) -> Vec<(&'static Assertion, Claim)> {
let mut out = Vec::new();
for assertion in ASSERTIONS {
let found =
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| (assertion.examine)(parsed)));
match found {
Ok(Some(claim)) => out.push((assertion, claim)),
Ok(None) => {}
Err(_) => eprintln!("amont-agent: assertion {} panicked", assertion.id),
}
}
out
}
const DEADLINE: Duration = Duration::from_secs(5);
pub fn read_briefly(dir: &std::path::Path, args: &[&str]) -> Option<String> {
let mut child = Command::new("git")
.arg("-C")
.arg(dir)
.args(args)
.env("GIT_TERMINAL_PROMPT", "0")
.env("GIT_ASKPASS", "/bin/false")
.env("SSH_ASKPASS", "/bin/false")
.env("SSH_ASKPASS_REQUIRE", "never")
.env(
"GIT_SSH_COMMAND",
"ssh -oBatchMode=yes -oStrictHostKeyChecking=accept-new -oConnectTimeout=4",
)
.stdin(Stdio::null())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()
.ok()?;
let started = Instant::now();
loop {
match child.try_wait() {
Ok(Some(status)) => {
if !status.success() {
return None;
}
break;
}
Ok(None) => {
if started.elapsed() >= DEADLINE {
let _ = child.kill();
let _ = child.wait();
return None;
}
std::thread::sleep(Duration::from_millis(20));
}
Err(_) => return None,
}
}
let mut out = String::new();
if let Some(mut stdout) = child.stdout.take() {
use std::io::Read;
if stdout.read_to_string(&mut out).is_err() {
return None;
}
}
Some(out.trim().to_string())
}