1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
use crate::{
    client::{AuthorizedClient, GeneralErrHandler, ID},
    errors::{Error, ErrorKind, Result},
};

use chrono::{DateTime, FixedOffset};
use failure::Fail;
use log::debug;
use reqwest::{blocking::Response, StatusCode};
use serde::{self, Deserialize, Serialize};
use std::string::ToString;

#[derive(Serialize, Debug)]
pub struct CollectionsQuery<'a> {
    include_public: bool,
    name:           Option<&'a str>,
    ids:            Option<Vec<&'a str>>,
}

impl<'a> Default for CollectionsQuery<'a> {
    fn default() -> Self {
        CollectionsQuery {
            include_public: false,
            name:           None,
            ids:            None,
        }
    }
}

impl<'a> CollectionsQuery<'a> {
    pub fn new() -> Self { Default::default() }

    pub fn include_public(self) -> CollectionsQuery<'a> {
        CollectionsQuery {
            include_public: true,
            ..self
        }
    }

    pub fn name(self, name: &'a str) -> CollectionsQuery<'a> {
        CollectionsQuery {
            name: Some(name),
            ..self
        }
    }

    pub fn ids(self, ids: Vec<&'a str>) -> CollectionsQuery<'a> { CollectionsQuery { ids: Some(ids), ..self } }
}

#[derive(Deserialize, Serialize, Debug)]
pub struct CollectionsResult {
    pub collections: Vec<Collection>,
}

impl Default for CollectionsResult {
    fn default() -> CollectionsResult {
        CollectionsResult {
            collections: Vec::with_capacity(0),
        }
    }
}

#[derive(Deserialize, Serialize, Debug)]
pub struct Collection {
    pub id:            String,
    pub public:        bool,
    pub name:          String,
    pub owner:         ID,
    pub auditing:      bool,
    #[serde(rename = "archived-date")]
    pub archived_date: Option<DateTime<FixedOffset>>,
    #[serde(rename = "has-folders")]
    pub has_folders:   Option<bool>,
}

pub fn search_collections(
    authorized_client: &AuthorizedClient,
    collection_query: CollectionsQuery,
) -> Result<CollectionsResult> {
    let url = format!("https://api.{}/v2/collections", authorized_client.base_url);

    let ids_str; // Borrow checker
    let mut params = Vec::new();
    if collection_query.include_public {
        params.push(("include-public", "true"));
    }
    if let Some(name) = collection_query.name {
        params.push(("name", name));
    }
    if let Some(ids) = collection_query.ids {
        ids_str = ids.as_slice().join(",");
        params.push(("ids", &ids_str));
    }

    let request = authorized_client
        .http_client
        .get(&url)
        .query(&params)
        .bearer_auth(&authorized_client.token.access_token);
    debug!("Request: '{:#?}'", request);

    let response: Response = request
        .send()
        .map_err(|e| e.context(ErrorKind::HttpRequestFailed))?
        .general_err_handler(&[StatusCode::OK, StatusCode::NO_CONTENT])?;
    debug!("Response: '{:#?}'", response);

    let result = match response.status() {
        status @ StatusCode::OK => {
            response.json().map_err(|e| {
                e.context(ErrorKind::FailedToProcessHttpResponse(
                    status,
                    "reading body".to_string(),
                ))
            })?
        }
        StatusCode::NO_CONTENT => CollectionsResult::default(),
        code => {
            return Err(Error::from(ErrorKind::ApiCallFailed(
                code,
                "unexpected response code".to_string(),
            )))
        }
    };

    Ok(result)
}