use crate::*;
use alux_ext::ext;
use anyhow::{Result, bail};
use std::fmt::Display;
use std::path::PathBuf;
pub trait DownloadLawFixture {
type Source;
fn law_source(&self) -> Self::Source;
fn law_destination(&self) -> PathBuf;
fn law_bytes(&self) -> Vec<u8>;
fn forget_law_resource(&mut self);
fn refuse_law_publication(&mut self);
fn law_progress(&self) -> Vec<u64>;
fn law_terminal_events(&self) -> usize;
fn law_published(&self) -> Option<Vec<u8>>;
fn law_abandoned(&self) -> usize;
}
#[ext(name = DownloadLaws)]
pub impl<This, Source, Event, Progress> This
where
This: DownloadLawFixture<Source = Source>
+ FetchStreamAlg<Source, Error: Display>
+ AtomicPublishAlg<Error: Display>
+ DownloadObservationAlg<Event = Event, Progress = Progress>
+ DownloadProgressAlg<Progress = Progress>
+ DownloadReportAlg<Event = Event>,
{
fn download_laws(&mut self) -> Result<()> {
let expected = self.law_bytes();
let reported = match self.download_resource(&self.law_source(), &self.law_destination()) {
Ok(bytes) => bytes,
Err(error) => bail!("download failed: {error}"),
};
let stated = u64::try_from(expected.len()).unwrap_or(u64::MAX);
if reported != stated {
bail!("download reported {reported} bytes, expected {stated}");
}
self.check_progress_laws()?;
if self.law_terminal_events() != 1 {
bail!("expected exactly one terminal event, observed {}", self.law_terminal_events());
}
match self.law_published() {
Some(bytes) if bytes == expected => Ok(()),
Some(_) => bail!("publication did not preserve the fetched bytes exactly"),
None => bail!("a successful download published nothing"),
}
}
fn download_fetch_failure_laws(&mut self) -> Result<()> {
self.forget_law_resource();
if self.download_resource(&self.law_source(), &self.law_destination()).is_ok() {
bail!("an absent resource downloaded successfully");
}
self.check_failure_laws(0)
}
fn download_publication_failure_laws(&mut self) -> Result<()> {
self.refuse_law_publication();
if self.download_resource(&self.law_source(), &self.law_destination()).is_ok() {
bail!("a refused publication downloaded successfully");
}
self.check_failure_laws(1)
}
fn check_progress_laws(&self) -> Result<()> {
let progress = self.law_progress();
if let Some(first) = progress.first()
&& *first != 0
{
bail!("progress began at {first} rather than zero");
}
if progress.windows(2).any(|pair| pair[1] < pair[0]) {
bail!("retrieved byte counts are not monotonic: {progress:?}");
}
Ok(())
}
fn check_failure_laws(&self, abandoned: usize) -> Result<()> {
if self.law_terminal_events() != 1 {
bail!("expected exactly one terminal event, observed {}", self.law_terminal_events());
}
if self.law_published().is_some() {
bail!("a failed execution published bytes at its final path");
}
if self.law_abandoned() != abandoned {
bail!("a failed publication was abandoned {} times, expected {abandoned}", self.law_abandoned());
}
self.check_progress_laws()
}
}