use std::future::{Future, IntoFuture};
use std::pin::Pin;
use dvm_api::{
Moment, NewReplay, ReplayStop, ReplayVerdict, Run, RunId, RunState, SandboxCommand,
SandboxSpec, Seek, WatchKind, WatchedEvents,
};
use reqwest::Method;
use crate::client::Client;
use crate::error::{Error, Result};
#[derive(Clone, Debug)]
pub struct Execution {
client: Client,
recorded: dvm_api::Execution,
}
#[must_use = "a replay starts once this is awaited"]
pub struct ReplayBuilder {
client: Client,
path: String,
new: NewReplay,
}
#[derive(Clone, Debug)]
pub struct Replay {
client: Client,
path: String,
run: Run,
}
impl Execution {
pub(crate) fn new(client: Client, recorded: dvm_api::Execution) -> Self {
Self { client, recorded }
}
pub fn spec(&self) -> &SandboxSpec {
&self.recorded.spec
}
pub fn state(&self) -> &RunState {
&self.recorded.run.state
}
pub fn log_bytes(&self) -> Option<u64> {
self.recorded.run.log_bytes
}
pub fn commands(&self) -> &[SandboxCommand] {
&self.recorded.commands
}
pub fn replays(&self) -> Vec<Replay> {
let replays = self.recorded.replays.iter().cloned();
replays
.map(|run| Replay::new(self.client.clone(), run))
.collect()
}
pub fn replay(&self) -> ReplayBuilder {
ReplayBuilder {
client: self.client.clone(),
path: format!("/executions/{}/replays", self.recorded.id),
new: NewReplay::default(),
}
}
pub async fn machine(&self, at: Moment) -> Result<ReplayStop> {
let path = format!("/executions/{}/machine", self.recorded.id);
let query = [("at", at.to_string())];
self.client
.json(Method::GET, &path, &query, None::<&()>)
.await
}
}
impl ReplayBuilder {
pub fn held(mut self) -> Self {
self.new.held = true;
self
}
pub fn watch(mut self, kind: WatchKind) -> Self {
self.new.watch = self.new.watch.with(kind);
self
}
}
impl IntoFuture for ReplayBuilder {
type Output = Result<Replay>;
type IntoFuture = Pin<Box<dyn Future<Output = Result<Replay>> + Send>>;
fn into_future(self) -> Self::IntoFuture {
Box::pin(async move {
let run = self
.client
.json(Method::POST, &self.path, &[], Some(&self.new))
.await?;
Ok(Replay::new(self.client, run))
})
}
}
impl Replay {
fn new(client: Client, run: Run) -> Self {
let path = format!("/replays/{}", run.id);
Self { client, path, run }
}
pub fn id(&self) -> RunId {
self.run.id
}
pub fn state(&self) -> &RunState {
&self.run.state
}
pub fn verdict(&self) -> Option<&ReplayVerdict> {
self.run.verdict.as_ref()
}
pub async fn wait(&mut self) -> Result<ReplayVerdict> {
while self.run.state.is_active() {
let query = [("newer_than", self.run.version.to_string())];
self.run = self
.client
.json(Method::GET, &self.path, &query, None::<&()>)
.await?;
}
self.run.verdict.clone().ok_or_else(|| Error::ReplayEnded {
replay: self.run.id,
state: self.run.state.clone(),
})
}
pub async fn seek(&self, to: Moment) -> Result<ReplayStop> {
let path = format!("{}/seek", self.path);
self.client
.json(Method::POST, &path, &[], Some(&Seek { to }))
.await
}
pub async fn stop(&mut self) -> Result<()> {
let path = format!("{}/stop", self.path);
self.run = self
.client
.json(Method::POST, &path, &[], None::<&()>)
.await?;
Ok(())
}
pub async fn commands(&self) -> Result<Vec<SandboxCommand>> {
let path = format!("{}/commands", self.path);
self.client.json(Method::GET, &path, &[], None::<&()>).await
}
pub async fn events(&self) -> Result<WatchedEvents> {
let path = format!("{}/events", self.path);
self.client.json(Method::GET, &path, &[], None::<&()>).await
}
}