1use crate::{
7 error::{ErrorKind, ErrorResponse},
8 http::{
9 headers::{HeaderName, Headers},
10 policies::create_public_api_span,
11 Context, Format, JsonFormat, Response, StatusCode, Url,
12 },
13 sleep,
14 time::{Duration, OffsetDateTime},
15 tracing::{Span, SpanStatus},
16};
17use futures::{channel::oneshot, stream::unfold, Stream, StreamExt};
18use serde::Deserialize;
19use std::{
20 convert::Infallible,
21 fmt,
22 future::{Future, IntoFuture},
23 pin::Pin,
24 str::FromStr,
25 sync::Arc,
26 task::{Context as TaskContext, Poll},
27};
28
29const DEFAULT_RETRY_TIME: Duration = Duration::seconds(30);
34
35const MIN_RETRY_TIME: Duration = Duration::seconds(1);
37
38#[derive(Debug, Clone, Default, PartialEq, Eq)]
40pub enum PollerState {
41 #[default]
43 Initial,
44 More(PollerContinuation),
46}
47
48#[derive(Debug, Default, Clone, PartialEq, Eq)]
50pub enum PollerStatus {
51 #[default]
53 InProgress,
54
55 Succeeded,
57
58 Failed,
60
61 Canceled,
63
64 UnknownValue(String),
66}
67
68impl From<&str> for PollerStatus {
69 fn from(value: &str) -> Self {
70 if "inprogress".eq_ignore_ascii_case(value) {
75 return PollerStatus::InProgress;
76 }
77
78 if "succeeded".eq_ignore_ascii_case(value) {
79 return PollerStatus::Succeeded;
80 }
81
82 if "failed".eq_ignore_ascii_case(value) {
83 return PollerStatus::Failed;
84 }
85
86 if "canceled".eq_ignore_ascii_case(value) || "cancelled".eq_ignore_ascii_case(value) {
89 return PollerStatus::Canceled;
90 }
91
92 PollerStatus::UnknownValue(value.to_owned())
93 }
94}
95
96impl FromStr for PollerStatus {
97 type Err = Infallible;
98 fn from_str(value: &str) -> Result<Self, Self::Err> {
99 Ok(value.into())
100 }
101}
102
103impl<'de> Deserialize<'de> for PollerStatus {
104 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
105 where
106 D: serde::Deserializer<'de>,
107 {
108 struct PollerStatusVisitor;
109 impl serde::de::Visitor<'_> for PollerStatusVisitor {
110 type Value = PollerStatus;
111
112 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
113 formatter.write_str("a string representing a PollerStatus")
114 }
115
116 fn visit_str<E>(self, s: &str) -> Result<Self::Value, E>
117 where
118 E: serde::de::Error,
119 {
120 FromStr::from_str(s).map_err(serde::de::Error::custom)
121 }
122 }
123
124 deserializer.deserialize_str(PollerStatusVisitor)
125 }
126}
127
128#[derive(Debug, Clone)]
130pub struct PollerOptions<'a> {
131 pub context: Context<'a>,
133 pub frequency: Duration,
137}
138
139impl Default for PollerOptions<'_> {
140 fn default() -> Self {
141 Self {
142 frequency: DEFAULT_RETRY_TIME,
143 context: Context::new(),
144 }
145 }
146}
147
148impl<'a> PollerOptions<'a> {
149 #[must_use]
151 pub fn into_owned(self) -> PollerOptions<'static> {
152 PollerOptions {
153 context: self.context.into_owned(),
154 frequency: self.frequency,
155 }
156 }
157}
158
159pub enum PollerResult<M, F = JsonFormat>
161where
162 M: StatusMonitor,
163 F: Format,
164{
165 InProgress {
173 response: Response<M, F>,
175 retry_after: Duration,
177 continuation: PollerContinuation,
179 },
180
181 Done {
187 response: Response<M, F>,
189 },
190
191 Succeeded {
198 response: Response<M, F>,
200 target: BoxedCallback<M>,
202 },
203}
204
205impl<M, F> fmt::Debug for PollerResult<M, F>
206where
207 M: StatusMonitor,
208 F: Format,
209{
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 match self {
212 Self::InProgress {
213 retry_after,
214 continuation,
215 ..
216 } => f
217 .debug_struct("InProgress")
218 .field("retry_after", &retry_after)
219 .field("continuation", &continuation)
220 .finish_non_exhaustive(),
221 Self::Done { .. } => f.debug_struct("Done").finish_non_exhaustive(),
222 Self::Succeeded { .. } => f.debug_struct("Succeeded").finish_non_exhaustive(),
223 }
224 }
225}
226
227#[derive(Clone, Debug, PartialEq, Eq)]
229#[non_exhaustive]
230pub enum PollerContinuation {
231 Links {
233 next_link: Url,
235
236 final_link: Option<Url>,
240 },
241}
242
243impl fmt::Display for PollerContinuation {
244 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
245 match self {
246 PollerContinuation::Links { next_link, .. } => f.write_str(next_link.as_str()),
247 }
248 }
249}
250
251pub trait StatusMonitor {
253 type Output;
257
258 type Format: Format + Send;
262
263 fn status(&self) -> PollerStatus;
265}
266
267mod types {
268 use super::{PollerResult, Response, StatusMonitor, Stream};
269 use std::{future::Future, pin::Pin};
270
271 pub type BoxedStream<M, F> = Box<dyn Stream<Item = crate::Result<Response<M, F>>> + Send>;
272 pub type BoxedFuture<M> = Box<
273 dyn Future<
274 Output = crate::Result<
275 Response<<M as StatusMonitor>::Output, <M as StatusMonitor>::Format>,
276 >,
277 > + Send,
278 >;
279 pub type BoxedCallback<M> = Box<dyn FnOnce() -> Pin<BoxedFuture<M>> + Send>;
280
281 pub type PollerResultFuture<M, F> =
283 Pin<Box<dyn Future<Output = crate::Result<PollerResult<M, F>>> + Send + 'static>>;
284}
285
286pub use types::PollerResultFuture;
287use types::{BoxedCallback, BoxedFuture, BoxedStream};
288
289#[must_use = "streams do nothing unless you `.await` or poll them"]
350#[pin_project::pin_project]
351pub struct Poller<M, F = JsonFormat>
352where
353 M: StatusMonitor,
354 F: Format,
355{
356 #[pin]
357 stream: Pin<BoxedStream<M, F>>,
358 target: Option<BoxedFuture<M>>,
359}
360
361impl<M, F> Poller<M, F>
362where
363 M: StatusMonitor,
364 F: Format + Send,
365{
366 pub fn new<Fun>(make_request: Fun, options: Option<PollerOptions<'static>>) -> Self
467 where
468 M: Send + 'static,
469 M::Output: Send + 'static,
470 M::Format: Send + 'static,
471 Fun: Fn(PollerState, PollerOptions<'static>) -> PollerResultFuture<M, F> + Send + 'static,
472 {
473 let options = options.unwrap_or_default();
474 let (stream, target) = create_poller_stream(make_request, options);
475 Self {
476 stream: Box::pin(stream),
477 target: Some(target),
478 }
479 }
480}
481
482impl<M, F> Stream for Poller<M, F>
483where
484 M: StatusMonitor,
485 F: Format,
486{
487 type Item = crate::Result<Response<M, F>>;
488
489 fn poll_next(self: Pin<&mut Self>, cx: &mut TaskContext<'_>) -> Poll<Option<Self::Item>> {
490 let state = self.project().stream.poll_next(cx);
491 if let Poll::Ready(Some(Ok(ref response))) = state {
492 check_status_code(response)?;
493 }
494
495 state
496 }
497}
498
499impl<M, F> IntoFuture for Poller<M, F>
500where
501 M: StatusMonitor + 'static,
502 M::Output: Send + 'static,
503 M::Format: Send + 'static,
504 F: Format + 'static,
505{
506 type Output = crate::Result<Response<M::Output, M::Format>>;
507
508 type IntoFuture = Pin<Box<dyn Future<Output = Self::Output> + Send>>;
509
510 fn into_future(mut self) -> Self::IntoFuture {
511 Box::pin(async move {
512 while let Some(result) = self.stream.next().await {
514 result?;
516 }
517
518 let target = self.target.ok_or_else(|| {
520 crate::Error::new(
521 ErrorKind::Other,
522 "poller completed without a target response",
523 )
524 })?;
525
526 Box::into_pin(target).await
528 })
529 }
530}
531
532impl<M, F> fmt::Debug for Poller<M, F>
533where
534 M: StatusMonitor,
535 F: Format,
536{
537 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
538 f.write_str("Poller")
539 }
540}
541#[derive(Debug, Clone, PartialEq, Eq)]
542enum State {
543 Init,
544 InProgress(PollerContinuation),
545 Done,
546}
547
548type TargetTransmitterType<'a, M> = (Pin<BoxedFuture<M>>, Option<Context<'a>>);
550
551struct StreamState<'a, M, Fun>
553where
554 M: StatusMonitor,
555{
556 state: State,
558 make_request: Fun,
560 target_tx: Option<oneshot::Sender<TargetTransmitterType<'a, M>>>,
562 options: PollerOptions<'a>,
564 added_span: bool,
566}
567
568fn create_poller_stream<
569 M,
570 F: Format,
571 Fun: Fn(PollerState, PollerOptions<'static>) -> PollerResultFuture<M, F> + Send + 'static,
572>(
573 make_request: Fun,
574 options: PollerOptions<'static>,
575) -> (
576 impl Stream<Item = crate::Result<Response<M, F>>> + 'static,
577 BoxedFuture<M>,
578)
579where
580 M: StatusMonitor + 'static,
581 M::Output: Send + 'static,
582 M::Format: Send + 'static,
583{
584 let (target_tx, target_rx) = oneshot::channel();
585
586 assert!(
587 options.frequency >= MIN_RETRY_TIME,
588 "minimum polling frequency is 1 second"
589 );
590 let stream = unfold(
591 StreamState::<M, Fun> {
593 state: State::Init,
594 make_request,
595 target_tx: Some(target_tx),
596 options,
597 added_span: false,
598 },
599 move |mut poller_stream_state| async move {
600 let result = match poller_stream_state.state {
601 State::Init => {
602 let span =
604 create_public_api_span(&poller_stream_state.options.context, None, None);
605 if let Some(ref s) = span {
606 poller_stream_state.added_span = true;
607 poller_stream_state.options.context =
608 poller_stream_state.options.context.with_value(s.clone());
609 }
610 (poller_stream_state.make_request)(
611 PollerState::Initial,
612 poller_stream_state.options.clone(),
613 )
614 .await
615 }
616 State::InProgress(continuation) => {
617 tracing::debug!(
618 "subsequent operation request to {:?}",
619 continuation.to_string()
620 );
621 (poller_stream_state.make_request)(
622 PollerState::More(continuation),
623 poller_stream_state.options.clone(),
624 )
625 .await
626 }
627 State::Done => {
628 tracing::debug!("done");
629 return None;
630 }
631 };
632 let (item, next_state) = match result {
633 Err(e) => {
634 if poller_stream_state.added_span {
635 if let Some(span) =
636 poller_stream_state.options.context.value::<Arc<dyn Span>>()
637 {
638 span.set_status(SpanStatus::Error {
640 description: e.to_string(),
641 });
642 span.set_attribute("error.type", e.kind().to_string().into());
643 span.end();
644 }
645 }
646
647 poller_stream_state.state = State::Done;
648 return Some((Err(e), poller_stream_state));
649 }
650 Ok(PollerResult::InProgress {
651 response,
652 retry_after,
653 continuation: n,
654 }) => {
655 tracing::trace!("retry poller in {}s", retry_after.whole_seconds());
658 sleep(retry_after).await;
659
660 (Ok(response), State::InProgress(n))
661 }
662 Ok(PollerResult::Done { response }) => (Ok(response), State::Done),
666 Ok(PollerResult::Succeeded {
667 response,
668 target: get_target,
669 }) => {
670 if let Some(tx) = poller_stream_state.target_tx.take() {
672 let _ = tx.send((
673 get_target(),
674 if poller_stream_state.added_span {
675 Some(poller_stream_state.options.context.clone())
676 } else {
677 None
678 },
679 ));
680 }
681 poller_stream_state.state = State::Done;
683 return Some((Ok(response), poller_stream_state));
684 }
685 };
686
687 poller_stream_state.state = next_state;
689 Some((item, poller_stream_state))
690 },
691 );
692
693 let target = Box::new(async move {
694 match target_rx.await {
695 Ok(target_state) => {
696 let res = target_state.0.await;
698 if let Some(ctx) = target_state.1 {
701 match &res {
702 Ok(response) => {
703 if let Some(span) = ctx.value::<Arc<dyn Span>>() {
706 if response.status().is_server_error() {
709 span.set_status(SpanStatus::Error {
710 description: "".to_string(),
711 });
712 }
713 if response.status().is_client_error()
714 || response.status().is_server_error()
715 {
716 span.set_attribute(
717 "error.type",
718 response.status().to_string().into(),
719 );
720 }
721
722 span.end();
723 }
724 }
725 Err(err) => {
726 if let Some(span) = ctx.value::<Arc<dyn Span>>() {
727 span.set_status(SpanStatus::Error {
728 description: err.to_string(),
729 });
730 span.set_attribute("error.type", err.kind().to_string().into());
731 span.end();
732 }
733 }
734 }
735 }
736 res
737 }
738 Err(err) => Err(crate::Error::with_error(
739 ErrorKind::Other,
740 err,
741 "poller completed without defining a target",
742 )),
743 }
744 });
745
746 (stream, target)
747}
748
749pub fn get_retry_after(
751 headers: &Headers,
752 retry_headers: &[HeaderName],
753 options: &PollerOptions,
754) -> Duration {
755 #[cfg_attr(feature = "test", allow(unused_mut))]
756 let duration =
757 crate::http::policies::get_retry_after(headers, OffsetDateTime::now_utc, retry_headers)
758 .unwrap_or(options.frequency);
759
760 #[cfg(feature = "test")]
761 {
762 use crate::test::RecordingMode;
763
764 if matches!(headers.get_optional::<RecordingMode>(), Ok(Some(mode)) if mode == RecordingMode::Playback)
767 {
768 if duration > Duration::ZERO {
769 tracing::debug!(
770 "overriding {}s poller retry in playback",
771 duration.whole_seconds()
772 );
773 }
774
775 return Duration::ZERO;
776 }
777 }
778
779 duration
780}
781
782fn check_status_code<T, F: Format>(response: &Response<T, F>) -> crate::Result<()> {
783 let status = response.status();
784 match status {
785 StatusCode::Ok | StatusCode::Accepted | StatusCode::Created | StatusCode::NoContent => {
786 Ok(())
787 }
788 _ => {
789 let raw_response = Box::new(response.to_raw_response());
791 let error_code = F::deserialize(raw_response.body())
792 .ok()
793 .and_then(|err: ErrorResponse| err.error)
794 .and_then(|details| details.code);
795 Err(ErrorKind::HttpResponse {
796 status,
797 error_code,
798 raw_response: Some(raw_response),
799 }
800 .into_error())
801 }
802 }
803}
804
805#[cfg(test)]
806mod tests {
807 use super::*;
808 #[cfg(feature = "xml")]
809 use crate::http::XmlFormat;
810 use crate::http::{
811 headers::Headers, AsyncRawResponse, HttpClient, Method, NoFormat, RawResponse, Request,
812 };
813 use azure_core_test::http::MockHttpClient;
814 use futures::{FutureExt as _, TryStreamExt as _};
815 use std::sync::{Arc, Mutex};
816
817 #[derive(Debug, serde::Deserialize)]
818 struct TestStatus {
819 status: String,
820 #[serde(default)]
821 target: Option<String>,
822 }
823
824 #[derive(Debug, serde::Deserialize)]
825 struct TestOutput {
826 #[serde(default)]
827 id: Option<String>,
828 #[serde(default)]
829 name: Option<String>,
830 }
831
832 impl StatusMonitor for TestStatus {
833 type Output = TestOutput;
834 type Format = JsonFormat;
835
836 fn status(&self) -> PollerStatus {
837 self.status.parse().unwrap_or_default()
838 }
839 }
840
841 #[cfg(feature = "xml")]
842 #[derive(Debug, serde::Deserialize)]
843 struct XmlTestStatus {
844 status: String,
845 }
846
847 #[cfg(feature = "xml")]
848 impl StatusMonitor for XmlTestStatus {
849 type Output = TestOutput;
850 type Format = XmlFormat;
851
852 fn status(&self) -> PollerStatus {
853 self.status.parse().unwrap_or_default()
854 }
855 }
856
857 #[tokio::test]
858 async fn poller_succeeded() {
859 let call_count = Arc::new(Mutex::new(0));
860
861 let mock_client = {
862 let call_count = call_count.clone();
863 Arc::new(MockHttpClient::new(move |_| {
864 let call_count = call_count.clone();
865 async move {
866 let mut count = call_count.lock().unwrap();
867 *count += 1;
868
869 if *count == 1 {
870 Ok(AsyncRawResponse::from_bytes(
872 StatusCode::Created,
873 Headers::new(),
874 br#"{"status":"InProgress"}"#.to_vec(),
875 ))
876 } else {
877 Ok(AsyncRawResponse::from_bytes(
879 StatusCode::Ok,
880 Headers::new(),
881 br#"{"status":"Succeeded"}"#.to_vec(),
882 ))
883 }
884 }
885 .boxed()
886 }))
887 };
888
889 let mut poller = Poller::new(
890 move |_, _| {
891 let client = mock_client.clone();
892 Box::pin(async move {
893 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
894 let raw_response = client.execute_request(&req).await?;
895 let (status, headers, body) = raw_response.deconstruct();
896 let bytes = body.collect().await?;
897
898 let test_status: TestStatus = crate::json::from_json(&bytes)?;
899 let response: Response<TestStatus> =
900 RawResponse::from_bytes(status, headers, bytes).into();
901
902 match test_status.status() {
903 PollerStatus::InProgress => Ok(PollerResult::InProgress {
904 response,
905 retry_after: Duration::ZERO,
906 continuation: PollerContinuation::Links {
907 next_link: req.url().clone(),
908 final_link: None,
909 },
910 }),
911 _ => Ok(PollerResult::Done { response }),
912 }
913 })
914 },
915 None,
916 );
917
918 let first_result = poller.next().await;
920 assert!(first_result.is_some());
921 let first_response = first_result.unwrap().unwrap();
922 assert_eq!(first_response.status(), StatusCode::Created);
923 let first_body = first_response.into_model().unwrap();
924 assert_eq!(first_body.status(), PollerStatus::InProgress);
925
926 let second_result = poller.next().await;
928 assert!(second_result.is_some());
929 let second_response = second_result.unwrap().unwrap();
930 assert_eq!(second_response.status(), StatusCode::Ok);
931 let second_body = second_response.into_model().unwrap();
932 assert_eq!(second_body.status(), PollerStatus::Succeeded);
933
934 let third_result = poller.next().await;
936 assert!(third_result.is_none());
937
938 assert_eq!(*call_count.lock().unwrap(), 2);
940 }
941
942 #[tokio::test]
943 async fn poller_failed() {
944 let call_count = Arc::new(Mutex::new(0));
945
946 let mock_client = {
947 let call_count = call_count.clone();
948 Arc::new(MockHttpClient::new(move |_| {
949 let call_count = call_count.clone();
950 async move {
951 let mut count = call_count.lock().unwrap();
952 *count += 1;
953
954 if *count == 1 {
955 Ok(AsyncRawResponse::from_bytes(
957 StatusCode::Created,
958 Headers::new(),
959 br#"{"status":"InProgress"}"#.to_vec(),
960 ))
961 } else {
962 Ok(AsyncRawResponse::from_bytes(
964 StatusCode::Ok,
965 Headers::new(),
966 br#"{"status":"Failed"}"#.to_vec(),
967 ))
968 }
969 }
970 .boxed()
971 }))
972 };
973 let mut poller = Poller::new(
974 move |_, _| {
975 let client = mock_client.clone();
976 Box::pin(async move {
977 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
978 let raw_response = client
979 .execute_request(&req)
980 .await?
981 .try_into_raw_response()
982 .await?;
983 let (status, headers, body) = raw_response.deconstruct();
984
985 let test_status: TestStatus = crate::json::from_json(&body)?;
986 let response: Response<TestStatus> =
987 RawResponse::from_bytes(status, headers, body).into();
988
989 match test_status.status() {
990 PollerStatus::InProgress => Ok(PollerResult::InProgress {
991 response,
992 retry_after: Duration::ZERO,
993 continuation: PollerContinuation::Links {
994 next_link: req.url().clone(),
995 final_link: None,
996 },
997 }),
998 _ => Ok(PollerResult::Done { response }),
999 }
1000 })
1001 },
1002 None,
1003 );
1004
1005 let first_result = poller.next().await;
1007 assert!(first_result.is_some());
1008 let first_response = first_result.unwrap().unwrap();
1009 assert_eq!(first_response.status(), StatusCode::Created);
1010 let first_body = first_response.into_model().unwrap();
1011 assert_eq!(first_body.status(), PollerStatus::InProgress);
1012
1013 let second_result = poller.next().await;
1015 assert!(second_result.is_some());
1016 let second_response = second_result.unwrap().unwrap();
1017 assert_eq!(second_response.status(), StatusCode::Ok);
1018 let second_body = second_response.into_model().unwrap();
1019 assert_eq!(second_body.status(), PollerStatus::Failed);
1020
1021 let third_result = poller.next().await;
1023 assert!(third_result.is_none());
1024
1025 assert_eq!(*call_count.lock().unwrap(), 2);
1027 }
1028
1029 #[tokio::test]
1030 async fn poller_failed_with_http_429() {
1031 let call_count = Arc::new(Mutex::new(0));
1032
1033 let mock_client = {
1034 let call_count = call_count.clone();
1035 Arc::new(MockHttpClient::new(move |_| {
1036 let call_count = call_count.clone();
1037 async move {
1038 let mut count = call_count.lock().unwrap();
1039 *count += 1;
1040
1041 if *count == 1 {
1042 Ok(AsyncRawResponse::from_bytes(
1044 StatusCode::Ok,
1045 Headers::new(),
1046 br#"{"status":"InProgress"}"#.to_vec(),
1047 ))
1048 } else {
1049 Ok(AsyncRawResponse::from_bytes(
1051 StatusCode::TooManyRequests,
1052 Headers::new(),
1053 vec![],
1054 ))
1055 }
1056 }
1057 .boxed()
1058 }))
1059 };
1060
1061 let mut poller = Poller::new(
1062 move |_, _| {
1063 let client = mock_client.clone();
1064 Box::pin(async move {
1065 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1066 let raw_response = client
1067 .execute_request(&req)
1068 .await?
1069 .try_into_raw_response()
1070 .await?;
1071 let (status, headers, body) = raw_response.deconstruct();
1072
1073 if status == StatusCode::Ok {
1074 let test_status: TestStatus = crate::json::from_json(&body)?;
1075 let response: Response<TestStatus> =
1076 RawResponse::from_bytes(status, headers, body).into();
1077
1078 match test_status.status() {
1079 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1080 response,
1081 retry_after: Duration::ZERO,
1082 continuation: PollerContinuation::Links {
1083 next_link: req.url().clone(),
1084 final_link: None,
1085 },
1086 }),
1087 _ => Ok(PollerResult::Done { response }),
1088 }
1089 } else {
1090 let response: Response<TestStatus> =
1092 RawResponse::from_bytes(status, headers, body).into();
1093 Ok(PollerResult::Done { response })
1094 }
1095 })
1096 },
1097 None,
1098 );
1099
1100 let first_result = poller.next().await;
1102 assert!(first_result.is_some());
1103 assert!(first_result.unwrap().is_ok());
1104
1105 let second_result = poller.next().await;
1107 assert!(second_result.is_some());
1108 let error = second_result.unwrap().unwrap_err();
1109
1110 match error.kind() {
1112 ErrorKind::HttpResponse { status, .. } => {
1113 assert_eq!(*status, StatusCode::TooManyRequests);
1114 }
1115 _ => panic!("Expected HttpResponse error, got {:?}", error.kind()),
1116 }
1117
1118 assert_eq!(*call_count.lock().unwrap(), 2);
1120 }
1121
1122 #[tokio::test]
1123 async fn poller_into_future_succeeds() {
1124 let call_count = Arc::new(Mutex::new(0));
1125
1126 let mock_client = {
1127 let call_count = call_count.clone();
1128 Arc::new(MockHttpClient::new(move |_| {
1129 let call_count = call_count.clone();
1130 async move {
1131 let mut count = call_count.lock().unwrap();
1132 *count += 1;
1133
1134 if *count == 1 {
1135 Ok(AsyncRawResponse::from_bytes(
1137 StatusCode::Created,
1138 Headers::new(),
1139 br#"{"status":"InProgress"}"#.to_vec(),
1140 ))
1141 } else {
1142 Ok(AsyncRawResponse::from_bytes(
1144 StatusCode::Ok,
1145 Headers::new(),
1146 br#"{"status":"Succeeded","id":"op1","name":"Operation completed successfully"}"#.to_vec(),
1147 ))
1148 }
1149 }
1150 .boxed()
1151 }))
1152 };
1153
1154 let poller = Poller::new(
1155 move |_, _| {
1156 let client = mock_client.clone();
1157 Box::pin(async move {
1158 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1159 let raw_response = client.execute_request(&req).await?;
1160 let (status, headers, body) = raw_response.deconstruct();
1161 let bytes = body.collect().await?;
1162
1163 let test_status: TestStatus = crate::json::from_json(&bytes)?;
1164 let response: Response<TestStatus> =
1165 RawResponse::from_bytes(status, headers.clone(), bytes.clone()).into();
1166
1167 match test_status.status() {
1168 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1169 response,
1170 retry_after: Duration::ZERO,
1171 continuation: PollerContinuation::Links {
1172 next_link: req.url().clone(),
1173 final_link: None,
1174 },
1175 }),
1176 PollerStatus::Succeeded => {
1177 Ok(PollerResult::Succeeded {
1179 response,
1180 target: Box::new(|| {
1181 Box::pin(async {
1182 use crate::http::headers::Headers;
1185 let headers = Headers::new();
1186 let bytes = bytes::Bytes::from(
1187 r#"{"id": "op1", "name": "Operation completed successfully"}"#,
1188 );
1189 Ok(RawResponse::from_bytes(StatusCode::Ok, headers, bytes)
1190 .into())
1191 })
1192 }),
1193 })
1194 }
1195 _ => Ok(PollerResult::Done { response }),
1196 }
1197 })
1198 },
1199 None,
1200 );
1201
1202 let result = poller.await;
1204 assert!(result.is_ok());
1205 let response = result.unwrap();
1206 assert_eq!(response.status(), StatusCode::Ok);
1207 let output = response.into_model().unwrap();
1208 assert_eq!(output.id.as_deref(), Some("op1"));
1209 assert_eq!(
1210 output.name.as_deref(),
1211 Some("Operation completed successfully")
1212 );
1213
1214 assert_eq!(*call_count.lock().unwrap(), 2);
1216 }
1217
1218 #[tokio::test]
1219 async fn poller_into_future_with_target_url() {
1220 let call_count = Arc::new(Mutex::new(0));
1221
1222 let mock_client = {
1223 let call_count = call_count.clone();
1224 Arc::new(MockHttpClient::new(move |req: &Request| {
1225 let call_count = call_count.clone();
1226 let url = req.url().to_string();
1227 async move {
1228 let mut count = call_count.lock().unwrap();
1229 *count += 1;
1230
1231 if *count == 1 {
1232 Ok(AsyncRawResponse::from_bytes(
1234 StatusCode::Accepted,
1235 Headers::new(),
1236 br#"{"status":"InProgress"}"#.to_vec(),
1237 ))
1238 } else if *count == 2 {
1239 Ok(AsyncRawResponse::from_bytes(
1241 StatusCode::Ok,
1242 Headers::new(),
1243 br#"{"status":"Succeeded","target":"https://example.com/resources/123"}"#.to_vec(),
1244 ))
1245 } else {
1246 assert_eq!(url, "https://example.com/resources/123");
1248 Ok(AsyncRawResponse::from_bytes(
1249 StatusCode::Ok,
1250 Headers::new(),
1251 br#"{"id":"123","name":"Test Resource"}"#.to_vec(),
1252 ))
1253 }
1254 }
1255 .boxed()
1256 }))
1257 };
1258
1259 let poller = Poller::new(
1260 move |_, _| {
1261 let client = mock_client.clone();
1262 Box::pin(async move {
1263 let req = Request::new(
1264 "https://example.com/operations/op1".parse().unwrap(),
1265 Method::Get,
1266 );
1267 let raw_response = client.execute_request(&req).await?;
1268 let (status, headers, body) = raw_response.deconstruct();
1269 let bytes = body.collect().await?;
1270
1271 let operation_status: TestStatus = crate::json::from_json(&bytes)?;
1272 let response: Response<TestStatus> =
1273 RawResponse::from_bytes(status, headers.clone(), bytes.clone()).into();
1274
1275 match operation_status.status() {
1276 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1277 response,
1278 retry_after: Duration::ZERO,
1279 continuation: PollerContinuation::Links {
1280 next_link: req.url().clone(),
1281 final_link: None,
1282 },
1283 }),
1284 PollerStatus::Succeeded => {
1285 if let Some(target_url) = operation_status.target {
1287 let client_clone = client.clone();
1288 Ok(PollerResult::Succeeded {
1289 response,
1290 target: Box::new(move || {
1291 Box::pin(async move {
1292 let target_req = Request::new(
1293 target_url.parse().unwrap(),
1294 Method::Get,
1295 );
1296 let target_response =
1297 client_clone.execute_request(&target_req).await?;
1298 let (target_status, target_headers, target_body) =
1299 target_response.deconstruct();
1300 let target_bytes = target_body.collect().await?;
1301
1302 Ok(RawResponse::from_bytes(
1303 target_status,
1304 target_headers,
1305 target_bytes,
1306 )
1307 .into())
1308 })
1309 }),
1310 })
1311 } else {
1312 Err(crate::Error::new(
1313 ErrorKind::Other,
1314 "no target URL in succeeded response",
1315 ))
1316 }
1317 }
1318 _ => Ok(PollerResult::Done { response }),
1319 }
1320 })
1321 },
1322 None,
1323 );
1324
1325 let result = poller.await;
1327 assert!(result.is_ok());
1328 let response = result.unwrap();
1329 assert_eq!(response.status(), StatusCode::Ok);
1330 let resource = response.into_model().unwrap();
1331 assert_eq!(resource.id.as_deref(), Some("123"));
1332 assert_eq!(resource.name.as_deref(), Some("Test Resource"));
1333
1334 assert_eq!(*call_count.lock().unwrap(), 3);
1336 }
1337
1338 #[tokio::test]
1339 async fn poller_into_future_no_response_body() {
1340 #[derive(Debug, serde::Deserialize)]
1341 struct NoBodyStatus {
1342 status: String,
1343 }
1344
1345 impl StatusMonitor for NoBodyStatus {
1346 type Output = ();
1347 type Format = NoFormat;
1348
1349 fn status(&self) -> PollerStatus {
1350 self.status.parse().unwrap_or_default()
1351 }
1352 }
1353
1354 let call_count = Arc::new(Mutex::new(0));
1355
1356 let mock_client = {
1357 let call_count = call_count.clone();
1358 Arc::new(MockHttpClient::new(move |_| {
1359 let call_count = call_count.clone();
1360 async move {
1361 let mut count = call_count.lock().unwrap();
1362 *count += 1;
1363
1364 if *count == 1 {
1365 Ok(AsyncRawResponse::from_bytes(
1367 StatusCode::Accepted,
1368 Headers::new(),
1369 br#"{"status":"InProgress"}"#.to_vec(),
1370 ))
1371 } else {
1372 Ok(AsyncRawResponse::from_bytes(
1374 StatusCode::Ok,
1375 Headers::new(),
1376 br#"{"status":"Succeeded"}"#.to_vec(),
1377 ))
1378 }
1379 }
1380 .boxed()
1381 }))
1382 };
1383
1384 let poller = Poller::new(
1385 move |_, _| {
1386 let client = mock_client.clone();
1387 Box::pin(async move {
1388 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1389 let raw_response = client.execute_request(&req).await?;
1390 let (status, headers, body) = raw_response.deconstruct();
1391 let bytes = body.collect().await?;
1392
1393 let no_body_status: NoBodyStatus = crate::json::from_json(&bytes)?;
1394 let response: Response<NoBodyStatus> =
1395 RawResponse::from_bytes(status, headers.clone(), bytes.clone()).into();
1396
1397 match no_body_status.status() {
1398 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1399 response,
1400 retry_after: Duration::ZERO,
1401 continuation: PollerContinuation::Links {
1402 next_link: req.url().clone(),
1403 final_link: None,
1404 },
1405 }),
1406 PollerStatus::Succeeded => {
1407 Ok(PollerResult::Succeeded {
1409 response,
1410 target: Box::new(move || {
1411 Box::pin(async move {
1412 use crate::http::headers::Headers;
1414 let headers = Headers::new();
1415 Ok(RawResponse::from_bytes(status, headers, Vec::new())
1416 .into())
1417 })
1418 }),
1419 })
1420 }
1421 _ => Ok(PollerResult::Done { response }),
1422 }
1423 })
1424 },
1425 None,
1426 );
1427
1428 let result = poller.await;
1430 assert!(result.is_ok());
1431 let response = result.unwrap();
1432 assert_eq!(response.status(), StatusCode::Ok);
1433 assert_eq!(*call_count.lock().unwrap(), 2);
1438 }
1439
1440 #[cfg(feature = "xml")]
1441 #[tokio::test]
1442 async fn poller_succeeded_xml() {
1443 let call_count = Arc::new(Mutex::new(0));
1444
1445 let mock_client = {
1446 let call_count = call_count.clone();
1447 Arc::new(MockHttpClient::new(move |_| {
1448 let call_count = call_count.clone();
1449 async move {
1450 let mut count = call_count.lock().unwrap();
1451 *count += 1;
1452
1453 if *count == 1 {
1454 Ok(AsyncRawResponse::from_bytes(
1456 StatusCode::Created,
1457 Headers::new(),
1458 b"<XmlTestStatus><status>InProgress</status></XmlTestStatus>".to_vec(),
1459 ))
1460 } else {
1461 Ok(AsyncRawResponse::from_bytes(
1463 StatusCode::Ok,
1464 Headers::new(),
1465 b"<XmlTestStatus><status>Succeeded</status></XmlTestStatus>".to_vec(),
1466 ))
1467 }
1468 }
1469 .boxed()
1470 }))
1471 };
1472
1473 let mut poller = Poller::new(
1474 move |_, _| {
1475 let client = mock_client.clone();
1476 Box::pin(async move {
1477 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1478 let raw_response = client.execute_request(&req).await?;
1479 let (status, headers, body) = raw_response.deconstruct();
1480 let bytes = body.collect().await?;
1481
1482 let test_status: XmlTestStatus = crate::xml::from_xml(&bytes)?;
1483 let response: Response<XmlTestStatus, XmlFormat> =
1484 RawResponse::from_bytes(status, headers, bytes).into();
1485
1486 match test_status.status() {
1487 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1488 response,
1489 retry_after: Duration::ZERO,
1490 continuation: PollerContinuation::Links {
1491 next_link: req.url().clone(),
1492 final_link: None,
1493 },
1494 }),
1495 _ => Ok(PollerResult::Done { response }),
1496 }
1497 })
1498 },
1499 None,
1500 );
1501
1502 let first_result = poller.next().await;
1504 assert!(first_result.is_some());
1505 let first_response = first_result.unwrap().unwrap();
1506 assert_eq!(first_response.status(), StatusCode::Created);
1507 let first_body = first_response.into_model().unwrap();
1508 assert_eq!(first_body.status(), PollerStatus::InProgress);
1509
1510 let second_result = poller.next().await;
1512 assert!(second_result.is_some());
1513 let second_response = second_result.unwrap().unwrap();
1514 assert_eq!(second_response.status(), StatusCode::Ok);
1515 let second_body = second_response.into_model().unwrap();
1516 assert_eq!(second_body.status(), PollerStatus::Succeeded);
1517
1518 let third_result = poller.next().await;
1520 assert!(third_result.is_none());
1521
1522 assert_eq!(*call_count.lock().unwrap(), 2);
1524 }
1525
1526 #[cfg(feature = "xml")]
1527 #[tokio::test]
1528 async fn poller_into_future_succeeds_xml() {
1529 let call_count = Arc::new(Mutex::new(0));
1530
1531 let mock_client = {
1532 let call_count = call_count.clone();
1533 Arc::new(MockHttpClient::new(move |_| {
1534 let call_count = call_count.clone();
1535 async move {
1536 let mut count = call_count.lock().unwrap();
1537 *count += 1;
1538
1539 if *count == 1 {
1540 Ok(AsyncRawResponse::from_bytes(
1542 StatusCode::Created,
1543 Headers::new(),
1544 b"<XmlTestStatus><status>InProgress</status></XmlTestStatus>"
1545 .to_vec(),
1546 ))
1547 } else {
1548 Ok(AsyncRawResponse::from_bytes(
1551 StatusCode::Ok,
1552 Headers::new(),
1553 b"<XmlTestStatus><status>Succeeded</status><id>op1</id><name>Operation completed successfully</name></XmlTestStatus>"
1554 .to_vec(),
1555 ))
1556 }
1557 }
1558 .boxed()
1559 }))
1560 };
1561
1562 let poller = Poller::new(
1563 move |_, _| {
1564 let client = mock_client.clone();
1565 Box::pin(async move {
1566 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1567 let raw_response = client.execute_request(&req).await?;
1568 let (status, headers, body) = raw_response.deconstruct();
1569 let bytes = body.collect().await?;
1570
1571 let test_status: XmlTestStatus = crate::xml::from_xml(&bytes)?;
1572 let response: Response<XmlTestStatus, XmlFormat> =
1573 RawResponse::from_bytes(status, headers.clone(), bytes.clone()).into();
1574
1575 match test_status.status() {
1576 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1577 response,
1578 retry_after: Duration::ZERO,
1579 continuation: PollerContinuation::Links {
1580 next_link: req.url().clone(),
1581 final_link: None,
1582 },
1583 }),
1584 PollerStatus::Succeeded => {
1585 Ok(PollerResult::Succeeded {
1587 response,
1588 target: Box::new(move || {
1589 Box::pin(async move {
1590 let headers = Headers::new();
1592 let bytes = bytes::Bytes::from(
1593 r#"<TestOutput><id>op1</id><name>Operation completed successfully</name></TestOutput>"#,
1594 );
1595 Ok(RawResponse::from_bytes(StatusCode::Ok, headers, bytes)
1596 .into())
1597 })
1598 }),
1599 })
1600 }
1601 _ => Ok(PollerResult::Done { response }),
1602 }
1603 })
1604 },
1605 None,
1606 );
1607
1608 let result = poller.await;
1610 assert!(result.is_ok());
1611 let response = result.unwrap();
1612 assert_eq!(response.status(), StatusCode::Ok);
1613 let output = response.into_model().unwrap();
1614 assert_eq!(output.id.as_deref(), Some("op1"));
1615 assert_eq!(
1616 output.name.as_deref(),
1617 Some("Operation completed successfully")
1618 );
1619
1620 assert_eq!(*call_count.lock().unwrap(), 2);
1622 }
1623
1624 #[tokio::test]
1625 async fn poller_into_future_output_is_self() {
1626 #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
1628 struct SelfContainedStatus {
1629 status: String,
1630 id: Option<String>,
1631 result: Option<String>,
1632 }
1633
1634 impl StatusMonitor for SelfContainedStatus {
1635 type Output = Self; type Format = JsonFormat;
1637
1638 fn status(&self) -> PollerStatus {
1639 self.status.parse().unwrap_or_default()
1640 }
1641 }
1642
1643 let call_count = Arc::new(Mutex::new(0));
1644
1645 let mock_client = {
1646 let call_count = call_count.clone();
1647 Arc::new(MockHttpClient::new(move |_| {
1648 let call_count = call_count.clone();
1649 async move {
1650 let mut count = call_count.lock().unwrap();
1651 *count += 1;
1652
1653 if *count == 1 {
1654 Ok(AsyncRawResponse::from_bytes(
1656 StatusCode::Created,
1657 Headers::new(),
1658 br#"{"status":"InProgress","id":"op1"}"#.to_vec(),
1659 ))
1660 } else {
1661 Ok(AsyncRawResponse::from_bytes(
1663 StatusCode::Ok,
1664 Headers::new(),
1665 br#"{"status":"Succeeded","id":"op1","result":"Operation completed successfully"}"#.to_vec(),
1666 ))
1667 }
1668 }
1669 .boxed()
1670 }))
1671 };
1672
1673 let poller = Poller::new(
1674 move |_, _| {
1675 let client = mock_client.clone();
1676 Box::pin(async move {
1677 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1678 let raw_response = client.execute_request(&req).await?;
1679 let (status, headers, body) = raw_response.deconstruct();
1680 let bytes = body.collect().await?;
1681
1682 let self_status: SelfContainedStatus = crate::json::from_json(&bytes)?;
1683 let response: Response<SelfContainedStatus> =
1684 RawResponse::from_bytes(status, headers.clone(), bytes.clone()).into();
1685
1686 match self_status.status() {
1687 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1688 response,
1689 retry_after: Duration::ZERO,
1690 continuation: PollerContinuation::Links {
1691 next_link: req.url().clone(),
1692 final_link: None,
1693 },
1694 }),
1695 PollerStatus::Succeeded => {
1696 let final_bytes = bytes.clone();
1699 Ok(PollerResult::Succeeded {
1700 response,
1701 target: Box::new(move || {
1702 Box::pin(async move {
1703 let headers = Headers::new();
1705 Ok(RawResponse::from_bytes(
1706 StatusCode::Ok,
1707 headers,
1708 final_bytes,
1709 )
1710 .into())
1711 })
1712 }),
1713 })
1714 }
1715 _ => Ok(PollerResult::Done { response }),
1716 }
1717 })
1718 },
1719 None,
1720 );
1721
1722 let result = poller.await;
1724 assert!(result.is_ok());
1725 let response = result.unwrap();
1726 assert_eq!(response.status(), StatusCode::Ok);
1727 let output = response.into_model().unwrap();
1728 assert_eq!(output.id.as_deref(), Some("op1"));
1729 assert_eq!(
1730 output.result.as_deref(),
1731 Some("Operation completed successfully")
1732 );
1733
1734 assert_eq!(*call_count.lock().unwrap(), 2);
1736 }
1737
1738 #[tokio::test]
1739 async fn poller_stream_output_is_self() {
1740 #[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
1743 struct SelfContainedStatus {
1744 status: String,
1745 id: Option<String>,
1746 result: Option<String>,
1747 }
1748
1749 impl StatusMonitor for SelfContainedStatus {
1750 type Output = Self; type Format = JsonFormat;
1752
1753 fn status(&self) -> PollerStatus {
1754 self.status.parse().unwrap_or_default()
1755 }
1756 }
1757
1758 let call_count = Arc::new(Mutex::new(0));
1759
1760 let mock_client = {
1761 let call_count = call_count.clone();
1762 Arc::new(MockHttpClient::new(move |_| {
1763 let call_count = call_count.clone();
1764 async move {
1765 let mut count = call_count.lock().unwrap();
1766 *count += 1;
1767
1768 if *count == 1 {
1769 Ok(AsyncRawResponse::from_bytes(
1771 StatusCode::Created,
1772 Headers::new(),
1773 br#"{"status":"InProgress","id":"op1"}"#.to_vec(),
1774 ))
1775 } else {
1776 Ok(AsyncRawResponse::from_bytes(
1778 StatusCode::Ok,
1779 Headers::new(),
1780 br#"{"status":"Succeeded","id":"op1","result":"Operation completed successfully"}"#.to_vec(),
1781 ))
1782 }
1783 }
1784 .boxed()
1785 }))
1786 };
1787
1788 let mut poller = Poller::new(
1789 move |_, _| {
1790 let client = mock_client.clone();
1791 Box::pin(async move {
1792 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1793 let raw_response = client.execute_request(&req).await?;
1794 let (status, headers, body) = raw_response.deconstruct();
1795 let bytes = body.collect().await?;
1796
1797 let self_status: SelfContainedStatus = crate::json::from_json(&bytes)?;
1798 let response: Response<SelfContainedStatus> =
1799 RawResponse::from_bytes(status, headers.clone(), bytes.clone()).into();
1800
1801 match self_status.status() {
1802 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1803 response,
1804 retry_after: Duration::ZERO,
1805 continuation: PollerContinuation::Links {
1806 next_link: req.url().clone(),
1807 final_link: None,
1808 },
1809 }),
1810 PollerStatus::Succeeded => {
1811 let final_bytes = bytes.clone();
1813 Ok(PollerResult::Succeeded {
1814 response,
1815 target: Box::new(move || {
1816 Box::pin(async move {
1817 use crate::http::headers::Headers;
1818 let headers = Headers::new();
1819 Ok(RawResponse::from_bytes(
1820 StatusCode::Ok,
1821 headers,
1822 final_bytes,
1823 )
1824 .into())
1825 })
1826 }),
1827 })
1828 }
1829 _ => Ok(PollerResult::Done { response }),
1830 }
1831 })
1832 },
1833 None,
1834 );
1835
1836 let mut statuses = Vec::new();
1838 while let Some(status_response) = poller.try_next().await.unwrap() {
1839 let status = status_response.into_model().unwrap();
1840 statuses.push(status);
1841 }
1842
1843 assert_eq!(statuses.len(), 2);
1845 assert_eq!(statuses[0].status, "InProgress");
1846 assert_eq!(statuses[0].id.as_deref(), Some("op1"));
1847 assert_eq!(statuses[0].result, None);
1848
1849 assert_eq!(statuses[1].status, "Succeeded");
1850 assert_eq!(statuses[1].id.as_deref(), Some("op1"));
1851 assert_eq!(
1852 statuses[1].result.as_deref(),
1853 Some("Operation completed successfully")
1854 );
1855
1856 assert_eq!(*call_count.lock().unwrap(), 2);
1858 }
1859
1860 #[tokio::test]
1861 async fn into_future_output_model_and_raw_response() {
1862 let call_count = Arc::new(Mutex::new(0));
1863 let final_json = br#"{"id":"res1","name":"My Resource"}"#;
1864
1865 let mock_client = {
1866 let call_count = call_count.clone();
1867 Arc::new(MockHttpClient::new(move |_| {
1868 let call_count = call_count.clone();
1869 async move {
1870 let mut count = call_count.lock().unwrap();
1871 *count += 1;
1872
1873 if *count == 1 {
1874 Ok(AsyncRawResponse::from_bytes(
1875 StatusCode::Accepted,
1876 Headers::new(),
1877 br#"{"status":"InProgress"}"#.to_vec(),
1878 ))
1879 } else {
1880 Ok(AsyncRawResponse::from_bytes(
1881 StatusCode::Ok,
1882 Headers::new(),
1883 br#"{"status":"Succeeded"}"#.to_vec(),
1884 ))
1885 }
1886 }
1887 .boxed()
1888 }))
1889 };
1890
1891 let poller = Poller::new(
1892 move |_, _| {
1893 let client = mock_client.clone();
1894 Box::pin(async move {
1895 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1896 let raw_response = client.execute_request(&req).await?;
1897 let (status, headers, body) = raw_response.deconstruct();
1898 let bytes = body.collect().await?;
1899
1900 let test_status: TestStatus = crate::json::from_json(&bytes)?;
1901 let response: Response<TestStatus> =
1902 RawResponse::from_bytes(status, headers.clone(), bytes).into();
1903
1904 match test_status.status() {
1905 PollerStatus::InProgress => Ok(PollerResult::InProgress {
1906 response,
1907 retry_after: Duration::ZERO,
1908 continuation: PollerContinuation::Links {
1909 next_link: req.url().clone(),
1910 final_link: None,
1911 },
1912 }),
1913 PollerStatus::Succeeded => {
1914 let target_body =
1915 bytes::Bytes::from_static(br#"{"id":"res1","name":"My Resource"}"#);
1916 Ok(PollerResult::Succeeded {
1917 response,
1918 target: Box::new(move || {
1919 Box::pin(async move {
1920 Ok(RawResponse::from_bytes(
1921 StatusCode::Ok,
1922 Headers::new(),
1923 target_body,
1924 )
1925 .into())
1926 })
1927 }),
1928 })
1929 }
1930 _ => Ok(PollerResult::Done { response }),
1931 }
1932 })
1933 },
1934 None,
1935 );
1936
1937 let response = poller.await.unwrap();
1939 assert_eq!(response.status(), StatusCode::Ok);
1940
1941 let raw = response.to_raw_response();
1943
1944 let output = response.into_model().unwrap();
1946 assert_eq!(output.id.as_deref(), Some("res1"));
1947 assert_eq!(output.name.as_deref(), Some("My Resource"));
1948
1949 let (raw_status, _, raw_body) = raw.deconstruct();
1951 assert_eq!(raw_status, StatusCode::Ok);
1952 assert_eq!(raw_body.as_ref(), final_json);
1953
1954 assert_eq!(*call_count.lock().unwrap(), 2);
1955 }
1956
1957 #[tokio::test]
1958 #[deny(unfulfilled_lint_expectations)]
1959 async fn must_use_poller() {
1960 fn create() -> crate::Result<Poller<TestStatus>> {
1961 let call_count = Arc::new(Mutex::new(0));
1962
1963 let mock_client = {
1964 let call_count = call_count.clone();
1965 Arc::new(MockHttpClient::new(move |_| {
1966 let call_count = call_count.clone();
1967 async move {
1968 let mut count = call_count.lock().unwrap();
1969 *count += 1;
1970
1971 if *count == 1 {
1972 Ok(AsyncRawResponse::from_bytes(
1974 StatusCode::Created,
1975 Headers::new(),
1976 br#"{"status":"InProgress"}"#.to_vec(),
1977 ))
1978 } else {
1979 Ok(AsyncRawResponse::from_bytes(
1981 StatusCode::Ok,
1982 Headers::new(),
1983 br#"{"status":"Succeeded"}"#.to_vec(),
1984 ))
1985 }
1986 }
1987 .boxed()
1988 }))
1989 };
1990
1991 let poller = Poller::new(
1992 move |_, _| {
1993 let client = mock_client.clone();
1994 Box::pin(async move {
1995 let req = Request::new("https://example.com".parse().unwrap(), Method::Get);
1996 let raw_response = client.execute_request(&req).await?;
1997 let (status, headers, body) = raw_response.deconstruct();
1998 let bytes = body.collect().await?;
1999
2000 let test_status: TestStatus = crate::json::from_json(&bytes)?;
2001 let response: Response<TestStatus> =
2002 RawResponse::from_bytes(status, headers, bytes).into();
2003
2004 match test_status.status() {
2005 PollerStatus::InProgress => Ok(PollerResult::InProgress {
2006 response,
2007 retry_after: Duration::ZERO,
2008 continuation: PollerContinuation::Links {
2009 next_link: req.url().clone(),
2010 final_link: None,
2011 },
2012 }),
2013 _ => Ok(PollerResult::Done { response }),
2014 }
2015 })
2016 },
2017 None,
2018 );
2019
2020 Ok(poller)
2021 }
2022
2023 #[expect(unused_must_use)]
2024 create();
2025
2026 #[expect(unused_must_use)]
2027 create().unwrap();
2028
2029 #[deny(unused_must_use)]
2031 let _poller = create().unwrap();
2032
2033 #[deny(unused_must_use)]
2034 let mut poller = create().unwrap();
2035 assert!(poller.next().await.is_some());
2036 assert!(poller.next().await.is_some());
2037 assert!(poller.next().await.is_none());
2038 }
2039}