zulip-api 0.0.8

Client library for interacting with the Zulip API
Documentation
use serde::{Deserialize, Serialize};

use crate::{field_access, impl_default, impl_display, Error, Result};

/// Represents the response to a `Upload File` API request.
#[derive(Clone, Debug, Eq, PartialEq, Deserialize, Serialize)]
pub struct Response {
    #[serde(skip_serializing_if = "Option::is_none")]
    uri: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    url: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    filename: Option<String>,
}

field_access! {
    Response {
        uri: option_cow_deref { str, String },
        url: option_cow_deref { str, String },
        filename: option_cow_deref { str, String },
    }
}

impl Response {
    /// Gets the realm ID  from the file URL.
    pub fn realm_id(&self) -> Result<u64> {
        self.url
            .as_deref()
            .and_then(|url| url.strip_prefix("/user_uploads/"))
            .ok_or(Error::io_other("invalid upload URL"))
            .and_then(|url| {
                url.chars()
                    .take_while(|c| c.is_ascii_digit())
                    .collect::<String>()
                    .parse::<u64>()
                    .map_err(Error::from)
            })
    }

    /// Gets the upload file path from the file URL.
    pub fn filename_path(&self) -> Result<String> {
        self.url
            .as_deref()
            .and_then(|url| url.strip_prefix("/user_uploads/"))
            .ok_or(Error::io_other("invalid upload URL"))
            .map(|url| {
                url.chars()
                    .skip_while(|c| c.is_ascii_digit() || c.is_ascii_hexdigit())
                    .skip_while(|c| c == &'/')
                    .collect::<String>()
            })
    }
}

impl Response {
    /// Creates a new [Response].
    pub const fn new() -> Self {
        Self {
            uri: None,
            url: None,
            filename: None,
        }
    }
}

impl_default!(Response);
impl_display!(Response);

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

    #[test]
    fn test_success_upload_file_response() {
        let uri = "/test/uri";
        let uri_json = serde_json::to_string(&uri).unwrap();

        let url = "/test/url";
        let url_json = serde_json::to_string(&url).unwrap();

        let filename = "/test/filename";
        let filename_json = serde_json::to_string(&filename).unwrap();

        let response = Response::new()
            .with_uri(uri)
            .with_url(url)
            .with_filename(filename);

        let response_json =
            format!(r#"{{"uri":{uri_json},"url":{url_json},"filename":{filename_json}}}"#);

        assert_eq!(serde_json::to_string(&response).unwrap(), response_json);
        assert_eq!(
            serde_json::from_str::<Response>(&response_json).unwrap(),
            response
        );

        assert_eq!(response.uri(), Some(uri));
        assert_eq!(response.url(), Some(url));
        assert_eq!(response.filename(), Some(filename));
    }
}