use bytes::Bytes;
use http::{HeaderMap, Method};
use parlov_analysis::existence::ExistenceAnalyzer;
use parlov_analysis::{Analyzer, SampleDecision};
use parlov_core::{Error, OracleResult, ProbeDefinition, ProbeSet};
use parlov_output::render_table;
use parlov_probe::http::HttpProbe;
use parlov_probe::Probe;
use uuid::Uuid;
use crate::cli::ExistenceArgs;
use crate::util::parse_headers;
pub async fn run(args: ExistenceArgs) -> Result<(), Error> {
let probe_id = args
.probe_id
.unwrap_or_else(|| Uuid::new_v4().to_string());
let method = parse_method(&args.method)?;
let headers = parse_headers(&args.headers)?;
let body_template = args.body.as_deref();
let baseline_def =
build_probe_def(&args.target, &args.baseline_id, &method, &headers, body_template);
let probe_def = build_probe_def(&args.target, &probe_id, &method, &headers, body_template);
let result = collect_until_verdict(&baseline_def, &probe_def).await?;
println!("{}", render_table(&result));
Ok(())
}
fn build_probe_def(
target: &str,
id: &str,
method: &Method,
headers: &HeaderMap,
body_template: Option<&str>,
) -> ProbeDefinition {
ProbeDefinition {
url: target.replace("{id}", id),
method: method.clone(),
headers: headers.clone(),
body: substitute_body(body_template, id),
}
}
pub(crate) async fn collect_until_verdict(
baseline_def: &ProbeDefinition,
probe_def: &ProbeDefinition,
) -> Result<OracleResult, Error> {
let client = HttpProbe::new();
let analyzer = ExistenceAnalyzer;
let mut probe_set = ProbeSet {
baseline: Vec::new(),
probe: Vec::new(),
};
loop {
let (b_surface, p_surface) = tokio::try_join!(
client.execute(baseline_def),
client.execute(probe_def),
)?;
probe_set.baseline.push(b_surface);
probe_set.probe.push(p_surface);
if let SampleDecision::Complete(result) = analyzer.evaluate(&probe_set) {
return Ok(result);
}
}
}
fn parse_method(method_str: &str) -> Result<Method, Error> {
method_str
.parse::<Method>()
.map_err(|e| Error::Http(format!("invalid HTTP method '{method_str}': {e}")))
}
fn substitute_body(template: Option<&str>, id: &str) -> Option<Bytes> {
template.map(|t| Bytes::from(t.replace("{id}", id)))
}