azure_storage_datalake 0.12.0

Azure Data Lake Storage Gen2 crate from the Azure SDK for Rust
Documentation
use azure_core::Etag;
use bytes::Bytes;
use serde::{self, Deserialize, Deserializer};
use std::convert::TryFrom;
use time::OffsetDateTime;

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct FileSystem {
    pub name: String,
    #[serde(with = "azure_core::date::rfc1123")]
    pub last_modified: OffsetDateTime,
    pub etag: Etag,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub(crate) struct FileSystemList {
    #[serde(rename = "filesystems")]
    pub file_systems: Vec<FileSystem>,
}

impl TryFrom<Bytes> for FileSystemList {
    type Error = crate::Error;

    fn try_from(response: Bytes) -> Result<Self, Self::Error> {
        Ok(serde_json::from_slice::<FileSystemList>(response.as_ref())?)
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Path {
    #[serde(deserialize_with = "deserialize_i64")]
    pub content_length: i64,
    pub etag: Etag,
    pub group: String,
    #[serde(default, deserialize_with = "deserialize_bool")]
    pub is_directory: bool,
    #[serde(with = "azure_core::date::rfc1123")]
    pub last_modified: OffsetDateTime,
    pub name: String,
    pub owner: String,
    pub permissions: String,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathList {
    pub paths: Vec<Path>,
}

impl TryFrom<Bytes> for PathList {
    type Error = azure_core::error::Error;

    fn try_from(response: Bytes) -> Result<Self, Self::Error> {
        Ok(serde_json::from_slice(response.as_ref())?)
    }
}

fn deserialize_i64<'de, D>(deserializer: D) -> Result<i64, D::Error>
where
    D: Deserializer<'de>,
{
    let s = serde_json::Value::deserialize(deserializer)?;
    match s {
        serde_json::Value::String(str_val) => str_val.parse().map_err(serde::de::Error::custom),
        serde_json::Value::Number(num_val) => match num_val.as_i64() {
            Some(val) => Ok(val),
            None => Err(serde::de::Error::custom(format!(
                "could not convert {num_val:?} to i64"
            ))),
        },
        other => Err(serde::de::Error::custom(format!(
            "unexpected data format - expected string or number, got: {other:?}"
        ))),
    }
}

fn deserialize_bool<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
    D: Deserializer<'de>,
{
    let s = serde_json::Value::deserialize(deserializer)?;
    match s {
        serde_json::Value::String(str_val) => str_val.parse().map_err(serde::de::Error::custom),
        serde_json::Value::Bool(bool_val) => Ok(bool_val),
        other => Err(serde::de::Error::custom(format!(
            "unexpected data format - expected string or bool, got: {other:?}"
        ))),
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json::json;

    #[test]
    fn test_path_serialization() {
        let payload = json!({
            "contentLength": 100_i64,
            "etag": "etag",
            "group": "group",
            "isDirectory": true,
            "lastModified": "Thu, 16 Jun 2022 10:22:59 GMT",
            "name": "name",
            "permissions": "permissions",
            "owner": "owner"
        });

        let path: Path = serde_json::from_slice(payload.to_string().as_ref()).unwrap();
        assert_eq!(path.content_length, 100);
        assert!(path.is_directory);

        let payload_str = json!({
            "contentLength": "100",
            "etag": "etag",
            "group": "group",
            "isDirectory": "true",
            "lastModified": "Thu, 16 Jun 2022 10:22:59 GMT",
            "name": "name",
            "permissions": "permissions",
            "owner": "owner"
        });

        let path: Path = serde_json::from_slice(payload_str.to_string().as_ref()).unwrap();
        assert_eq!(path.content_length, 100);
        assert!(path.is_directory);
    }
}