azure_sdk_storage_table 0.41.0

Rust wrappers around Microsoft Azure REST APIs - Table storage crate
Documentation
use crate::de;
use azure_sdk_core::errors::AzureError;
use chrono::{DateTime, Utc};
use http::header;
use http::HeaderMap;
use serde::de::DeserializeOwned;
use serde_json;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct NoData {}

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct TableEntity<T> {
    #[serde(rename = "RowKey")]
    pub row_key: String,
    #[serde(rename = "PartitionKey")]
    pub partition_key: String,

    #[serde(skip_serializing, rename = "odata.etag")]
    pub etag: Option<String>,

    #[serde(
        skip_serializing,
        deserialize_with = "de::optional_timestamp",
        rename = "Timestamp"
    )]
    pub timestamp: Option<DateTime<Utc>>,

    #[serde(flatten)]
    pub payload: T,
}

impl<T> std::convert::TryFrom<(&HeaderMap, &[u8])> for TableEntity<T>
where
    T: DeserializeOwned,
{
    type Error = AzureError;

    fn try_from(value: (&HeaderMap, &[u8])) -> Result<Self, Self::Error> {
        let headers = value.0;
        let body = value.1;
        log::trace!("headers == {:?}", headers);
        log::trace!("body == {:?}", std::str::from_utf8(body));

        let mut entity: Self = serde_json::from_slice(&body)?;

        if let Some(etag) = headers.get(header::ETAG) {
            entity.etag = Some(etag.to_str()?.to_owned());
        }

        Ok(entity)
    }
}

#[derive(Debug, Clone)]
pub(crate) struct ContinuationCursor {
    pub(crate) partition_key: String,
    pub(crate) row_key: String,
}

#[derive(Debug, Clone)]
pub struct Continuation {
    pub(crate) fused: bool,
    pub(crate) next: Option<ContinuationCursor>,
}

impl Continuation {
    pub fn start() -> Self {
        Continuation {
            fused: false,
            next: None,
        }
    }
}

impl std::convert::TryFrom<&HeaderMap> for Continuation {
    type Error = AzureError;

    fn try_from(headers: &HeaderMap) -> Result<Self, Self::Error> {
        const HEADER_NEXTPARTITIONKEY: &str = "x-ms-continuation-NextPartitionKey";
        const HEADER_NEXTROWKEY: &str = "x-ms-continuation-NextRowKey";

        if headers.contains_key(HEADER_NEXTPARTITIONKEY) && headers.contains_key(HEADER_NEXTROWKEY)
        {
            Ok(Continuation {
                fused: false,
                next: Some(ContinuationCursor {
                    partition_key: headers[HEADER_NEXTPARTITIONKEY].to_str()?.to_string(),
                    row_key: headers[HEADER_NEXTROWKEY].to_str()?.to_string(),
                }),
            })
        } else {
            Ok(Continuation {
                fused: true,
                next: None,
            })
        }
    }
}