mod asyncop;
mod body;
mod loc;
mod noop;
mod op;
mod utils;
use azure_core::error::ErrorKind;
use azure_core::http::{Method, Request};
use std::time::Duration;
use typespec_client_core::sleep::sleep;
use typespec_client_core::time;
use utils::FinalStateVia;
use azure_core::Error;
use azure_core::{
http::{Context, Pipeline, StatusCode},
Result,
};
use super::response::Response;
trait PollingHandler {
fn applicable(req: &Request, resp: &Response) -> bool;
async fn poll(&mut self, ctx: &Context<'_>) -> Result<Response>;
fn done(&self) -> bool;
async fn result(&self, ctx: &Context<'_>) -> Result<Response>;
}
enum Handler {
AsyncOp(asyncop::Poller),
Loc(loc::Poller),
Op(op::Poller),
Body(body::Poller),
Noop(noop::Poller),
}
#[derive(Debug, Default, Clone)]
pub struct NewPollerOptions {
final_state: Option<FinalStateVia>,
operation_location_result_path: Option<String>,
}
#[derive(Debug, Clone, Default)]
pub struct PollUntilDoneOptions {
frequency: Option<Duration>,
}
pub struct Poller {
handler: Handler,
resp: Response,
}
impl Poller {
pub async fn new(
pl: Pipeline,
req: &Request,
resp: &Response,
opts: Option<NewPollerOptions>,
) -> Result<Self> {
let opts = opts.unwrap_or_default();
if !utils::is_valid_status_code(resp.status_code) {
return Err(Error::message(
ErrorKind::Other,
"the operation failed or was cancelled",
));
}
let handler = if asyncop::Poller::applicable(req, resp) {
Handler::AsyncOp(asyncop::Poller::new(
pl,
req,
resp.clone(),
opts.final_state,
)?)
} else if op::Poller::applicable(req, resp) {
Handler::Op(op::Poller::new(
pl,
req,
resp.clone(),
opts.final_state,
opts.operation_location_result_path,
)?)
} else if loc::Poller::applicable(req, resp) {
Handler::Loc(loc::Poller::new(pl, resp.clone())?)
} else if body::Poller::applicable(req, resp) {
Handler::Body(body::Poller::new(pl, req, resp.clone())?)
} else if resp.status_code == StatusCode::Accepted
&& [Method::Delete, Method::Post]
.iter()
.any(|v| *v == req.method())
{
return Err(Error::message(
resp.clone().into(),
"response is missing polling URL",
));
} else {
Handler::Noop(noop::Poller::new(resp))
};
Ok(Self {
handler,
resp: resp.clone(),
})
}
pub async fn poll(&mut self, ctx: &Context<'_>) -> Result<Response> {
if self.done() {
return Ok(self.resp.clone());
}
let resp = match &mut self.handler {
Handler::AsyncOp(poller) => poller.poll(ctx).await?,
Handler::Loc(poller) => poller.poll(ctx).await?,
Handler::Op(poller) => poller.poll(ctx).await?,
Handler::Body(poller) => poller.poll(ctx).await?,
Handler::Noop(poller) => poller.poll(ctx).await?,
};
self.resp = resp;
Ok(self.resp.clone())
}
pub async fn poll_until_done(
&mut self,
ctx: &Context<'_>,
opts: Option<PollUntilDoneOptions>,
) -> Result<Response> {
let opts = opts.unwrap_or_default();
loop {
let resp = self.poll(ctx).await?;
if self.done() {
return self.result(ctx).await;
}
let duration = time::Duration::try_from(
utils::retry_after(&resp)
.unwrap_or(opts.frequency.unwrap_or(Duration::from_secs(30))),
)
.map_err(|e| Error::new(ErrorKind::Other, e))?;
sleep(duration).await;
}
}
pub fn done(&self) -> bool {
match &self.handler {
Handler::AsyncOp(poller) => poller.done(),
Handler::Loc(poller) => poller.done(),
Handler::Op(poller) => poller.done(),
Handler::Body(poller) => poller.done(),
Handler::Noop(poller) => poller.done(),
}
}
async fn result(&self, ctx: &Context<'_>) -> Result<Response> {
assert!(self.done());
match &self.handler {
Handler::AsyncOp(poller) => poller.result(ctx).await,
Handler::Loc(poller) => poller.result(ctx).await,
Handler::Op(poller) => poller.result(ctx).await,
Handler::Body(poller) => poller.result(ctx).await,
Handler::Noop(poller) => poller.result(ctx).await,
}
}
}