use std::sync::Arc;
use std::time::{Duration, Instant};
use tokio::time::sleep;
use crate::error::{Result, ScreenshotFreeAPIError};
use crate::http::HttpClient;
use crate::types::{
EnqueueResponse, HtmlScreenshotOptions, JobResult, JobStatusResponse, MobileScreenshotOptions,
WebScreenshotOptions,
};
pub struct WaitOptions {
pub interval_ms: u64,
pub timeout_ms: u64,
pub on_progress: Option<Box<dyn Fn(&JobStatusResponse) + Send>>,
}
impl Default for WaitOptions {
fn default() -> Self {
Self { interval_ms: 2_000, timeout_ms: 120_000, on_progress: None }
}
}
#[derive(Clone)]
pub struct ScreenshotsResource {
pub(crate) http: Arc<HttpClient>,
}
impl ScreenshotsResource {
pub async fn capture_web(&self, options: WebScreenshotOptions) -> Result<EnqueueResponse> {
self.http.post("/screenshots/web", &options, None).await
}
pub async fn capture_mobile(
&self,
options: MobileScreenshotOptions,
) -> Result<EnqueueResponse> {
self.http.post("/screenshots/mobile", &options, None).await
}
pub async fn capture_html(&self, options: HtmlScreenshotOptions) -> Result<EnqueueResponse> {
self.http.post("/screenshots/html", &options, None).await
}
pub async fn capture_web_and_wait(
&self,
options: WebScreenshotOptions,
wait_opts: WaitOptions,
) -> Result<JobResult> {
let enq = self.capture_web(options).await?;
wait_for_job(&self.http, &enq.job_id, wait_opts).await
}
pub async fn capture_mobile_and_wait(
&self,
options: MobileScreenshotOptions,
wait_opts: WaitOptions,
) -> Result<JobResult> {
let enq = self.capture_mobile(options).await?;
wait_for_job(&self.http, &enq.job_id, wait_opts).await
}
pub async fn capture_html_and_wait(
&self,
options: HtmlScreenshotOptions,
wait_opts: WaitOptions,
) -> Result<JobResult> {
let enq = self.capture_html(options).await?;
wait_for_job(&self.http, &enq.job_id, wait_opts).await
}
}
pub(crate) async fn wait_for_job(
http: &HttpClient,
job_id: &str,
opts: WaitOptions,
) -> Result<JobResult> {
let deadline = Instant::now() + Duration::from_millis(opts.timeout_ms);
loop {
if Instant::now() >= deadline {
return Err(ScreenshotFreeAPIError::JobTimeout { timeout_ms: opts.timeout_ms });
}
let status: JobStatusResponse =
http.get(&format!("/jobs/{job_id}/status"), None).await?;
if let Some(ref cb) = opts.on_progress {
cb(&status);
}
match status.status.as_str() {
"completed" => {
return http.get(&format!("/jobs/{job_id}/result"), None).await;
}
"failed" => {
return Err(ScreenshotFreeAPIError::JobFailed {
job_id: job_id.to_string(),
reason: status.error.unwrap_or_else(|| "unknown error".into()),
});
}
_ => {}
}
sleep(Duration::from_millis(opts.interval_ms)).await;
}
}