use crate::accounting::CostBreakdown;
use crate::container::Descriptor;
use crate::encode::candidates::{Candidate, CandidateKind};
use crate::error::{Error, Result};
use crate::limits::Limits;
use crate::materialize::materialize;
#[derive(Debug, Clone)]
pub struct CourtResult {
pub kind: CandidateKind,
pub bytes: Vec<u8>,
pub cost: CostBreakdown,
pub candidates_evaluated: u32,
pub graph_ops: usize,
}
pub fn run(input: &[u8], candidates: Vec<Candidate>, limits: Limits) -> Result<CourtResult> {
let mut best: Option<(u64, usize, CandidateKind, Vec<u8>, CostBreakdown)> = None;
let mut evaluated: u32 = 0;
for c in candidates {
evaluated += 1;
let (bytes, cost) = c.descriptor.serialize()?;
let parsed = Descriptor::parse(&bytes, limits)?;
let out = materialize(&parsed, limits)?;
if out != input {
return Err(Error::reconstruction_mismatch(format!(
"candidate {} did not reproduce the source exactly",
c.kind.name()
)));
}
let total = cost.total();
debug_assert_eq!(total, bytes.len() as u64);
let work = c.descriptor.program.ops.len();
let key = (total, work, c.kind);
let replace = match &best {
None => true,
Some((bt, bw, bk, ..)) => key < (*bt, *bw, *bk),
};
if replace {
best = Some((total, work, c.kind, bytes, cost));
}
}
let (_, work, kind, bytes, cost) =
best.ok_or_else(|| Error::internal_invariant("no candidates were evaluated"))?;
Ok(CourtResult {
kind,
bytes,
cost,
candidates_evaluated: evaluated,
graph_ops: work,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::encode::candidates;
#[test]
fn raw_candidate_wins_and_is_exact() {
let input = b"some bytes that must round trip".to_vec();
let cands = candidates::propose(&input, Limits::DEFAULT).unwrap();
let r = run(&input, cands, Limits::DEFAULT).unwrap();
assert_eq!(r.kind, CandidateKind::Raw);
assert_eq!(r.candidates_evaluated, 1);
assert_eq!(r.cost.total(), r.bytes.len() as u64);
let (out, _) = crate::materialize::decode_to_bytes(&r.bytes, Limits::DEFAULT).unwrap();
assert_eq!(out, input);
}
#[test]
fn empty_input_is_exact() {
let input: Vec<u8> = Vec::new();
let cands = candidates::propose(&input, Limits::DEFAULT).unwrap();
let r = run(&input, cands, Limits::DEFAULT).unwrap();
let (out, _) = crate::materialize::decode_to_bytes(&r.bytes, Limits::DEFAULT).unwrap();
assert!(out.is_empty());
}
}