df_consul/
with_index.rs

1use std::fmt::{Debug, Display, Write};
2
3use anyhow::{bail, Result};
4use log::*;
5use reqwest::Response;
6use serde::Deserialize;
7
8use crate::Consul;
9
10impl Consul {
11    pub(crate) async fn get_with_index<T: for<'de> Deserialize<'de>>(
12        &self,
13        mut url: String,
14        last_index: Option<usize>,
15    ) -> Result<WithIndex<T>> {
16        if let Some(i) = last_index {
17            if url.contains('?') {
18                write!(&mut url, "&index={}", i).unwrap();
19            } else {
20                write!(&mut url, "?index={}", i).unwrap();
21            }
22        }
23        debug!("GET {} as {}", url, std::any::type_name::<T>());
24
25        let http = self.client.get(&url).send().await?;
26
27        Ok(WithIndex::<T>::index_from(&http)?.value(http.json().await?))
28    }
29}
30
31/// Wraps the returned value of an [API call with blocking
32/// possibility](https://developer.hashicorp.com/consul/api-docs/features/blocking) with the
33/// returned Consul index
34pub struct WithIndex<T> {
35    pub(crate) value: T,
36    pub(crate) index: usize,
37}
38
39impl<T> WithIndex<T> {
40    /// (for internal use, mostly)
41    pub fn index_from(resp: &Response) -> Result<WithIndexBuilder<T>> {
42        let index = match resp.headers().get("X-Consul-Index") {
43            Some(v) => v.to_str()?.parse::<usize>()?,
44            None => bail!("X-Consul-Index header not found"),
45        };
46        Ok(WithIndexBuilder {
47            index,
48            _phantom: Default::default(),
49        })
50    }
51
52    /// Returns the inner value, discarding the index
53    pub fn into_inner(self) -> T {
54        self.value
55    }
56
57    /// Returns the Consul index, to be used in future calls to the same API endpoint to make them
58    /// blocking
59    pub fn index(&self) -> usize {
60        self.index
61    }
62}
63
64impl<T> std::convert::AsRef<T> for WithIndex<T> {
65    fn as_ref(&self) -> &T {
66        &self.value
67    }
68}
69
70impl<T> std::borrow::Borrow<T> for WithIndex<T> {
71    fn borrow(&self) -> &T {
72        &self.value
73    }
74}
75
76impl<T> std::ops::Deref for WithIndex<T> {
77    type Target = T;
78    fn deref(&self) -> &T {
79        &self.value
80    }
81}
82
83impl<T: Debug> Debug for WithIndex<T> {
84    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
85        <T as Debug>::fmt(self, f)
86    }
87}
88
89impl<T: Display> Display for WithIndex<T> {
90    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
91        <T as Display>::fmt(self, f)
92    }
93}
94
95/// (for internal use, mostly)
96pub struct WithIndexBuilder<T> {
97    _phantom: std::marker::PhantomData<T>,
98    index: usize,
99}
100
101impl<T> WithIndexBuilder<T> {
102    /// (for internal use, mostly)
103    pub fn value(self, value: T) -> WithIndex<T> {
104        WithIndex {
105            value,
106            index: self.index,
107        }
108    }
109}