1use std::future::{Future, IntoFuture};
6use std::pin::Pin;
7
8use dvm_api::{
9 Moment, NewReplay, ReplayStop, ReplayVerdict, Run, RunId, RunState, SandboxCommand,
10 SandboxSpec, Seek, WatchKind, WatchedEvents,
11};
12use reqwest::Method;
13
14use crate::client::Client;
15use crate::error::{Error, Result};
16
17#[derive(Clone, Debug)]
20pub struct Execution {
21 client: Client,
22 recorded: dvm_api::Execution,
23}
24
25#[must_use = "a replay starts once this is awaited"]
27pub struct ReplayBuilder {
28 client: Client,
29 path: String,
30 new: NewReplay,
31}
32
33#[derive(Clone, Debug)]
35pub struct Replay {
36 client: Client,
37 path: String,
38 run: Run,
39}
40
41impl Execution {
42 pub(crate) fn new(client: Client, recorded: dvm_api::Execution) -> Self {
43 Self { client, recorded }
44 }
45
46 pub fn spec(&self) -> &SandboxSpec {
47 &self.recorded.spec
48 }
49
50 pub fn state(&self) -> &RunState {
51 &self.recorded.run.state
52 }
53
54 pub fn log_bytes(&self) -> Option<u64> {
56 self.recorded.run.log_bytes
57 }
58
59 pub fn commands(&self) -> &[SandboxCommand] {
61 &self.recorded.commands
62 }
63
64 pub fn replays(&self) -> Vec<Replay> {
65 let replays = self.recorded.replays.iter().cloned();
66 replays
67 .map(|run| Replay::new(self.client.clone(), run))
68 .collect()
69 }
70
71 pub fn replay(&self) -> ReplayBuilder {
74 ReplayBuilder {
75 client: self.client.clone(),
76 path: format!("/executions/{}/replays", self.recorded.id),
77 new: NewReplay::default(),
78 }
79 }
80
81 pub async fn machine(&self, at: Moment) -> Result<ReplayStop> {
83 let path = format!("/executions/{}/machine", self.recorded.id);
84 let query = [("at", at.to_string())];
85 self.client
86 .json(Method::GET, &path, &query, None::<&()>)
87 .await
88 }
89}
90
91impl ReplayBuilder {
92 pub fn held(mut self) -> Self {
94 self.new.held = true;
95 self
96 }
97
98 pub fn watch(mut self, kind: WatchKind) -> Self {
100 self.new.watch = self.new.watch.with(kind);
101 self
102 }
103}
104
105impl IntoFuture for ReplayBuilder {
106 type Output = Result<Replay>;
107 type IntoFuture = Pin<Box<dyn Future<Output = Result<Replay>> + Send>>;
108
109 fn into_future(self) -> Self::IntoFuture {
110 Box::pin(async move {
111 let run = self
112 .client
113 .json(Method::POST, &self.path, &[], Some(&self.new))
114 .await?;
115 Ok(Replay::new(self.client, run))
116 })
117 }
118}
119
120impl Replay {
121 fn new(client: Client, run: Run) -> Self {
122 let path = format!("/replays/{}", run.id);
123 Self { client, path, run }
124 }
125
126 pub fn id(&self) -> RunId {
127 self.run.id
128 }
129
130 pub fn state(&self) -> &RunState {
132 &self.run.state
133 }
134
135 pub fn verdict(&self) -> Option<&ReplayVerdict> {
137 self.run.verdict.as_ref()
138 }
139
140 pub async fn wait(&mut self) -> Result<ReplayVerdict> {
143 while self.run.state.is_active() {
144 let query = [("newer_than", self.run.version.to_string())];
145 self.run = self
146 .client
147 .json(Method::GET, &self.path, &query, None::<&()>)
148 .await?;
149 }
150 self.run.verdict.clone().ok_or_else(|| Error::ReplayEnded {
151 replay: self.run.id,
152 state: self.run.state.clone(),
153 })
154 }
155
156 pub async fn seek(&self, to: Moment) -> Result<ReplayStop> {
159 let path = format!("{}/seek", self.path);
160 self.client
161 .json(Method::POST, &path, &[], Some(&Seek { to }))
162 .await
163 }
164
165 pub async fn stop(&mut self) -> Result<()> {
166 let path = format!("{}/stop", self.path);
167 self.run = self
168 .client
169 .json(Method::POST, &path, &[], None::<&()>)
170 .await?;
171 Ok(())
172 }
173
174 pub async fn commands(&self) -> Result<Vec<SandboxCommand>> {
176 let path = format!("{}/commands", self.path);
177 self.client.json(Method::GET, &path, &[], None::<&()>).await
178 }
179
180 pub async fn events(&self) -> Result<WatchedEvents> {
182 let path = format!("{}/events", self.path);
183 self.client.json(Method::GET, &path, &[], None::<&()>).await
184 }
185}