flickr_api/
lib.rs

1#![allow(dead_code)]
2use image::io::Reader;
3use serde::{Deserialize, Deserializer, Serialize};
4use serde_json::Value;
5use std::error::Error;
6use std::io::Cursor;
7use std::rc::Rc;
8use warp::hyper::body::Bytes;
9
10mod oauth;
11pub use oauth::{ApiKey, Token as OauthToken};
12
13pub mod get_info;
14pub mod get_sizes;
15pub mod login;
16pub mod test_login;
17pub mod upload_photo;
18
19static URL_ACCESS: &str = "https://www.flickr.com/services/oauth/access_token";
20static URL_AUTHORIZE: &str = "https://www.flickr.com/services/oauth/authorize";
21static URL_REQUEST: &str = "https://www.flickr.com/services/oauth/request_token";
22
23static URL_API: &str = "https://api.flickr.com/services/rest/";
24
25static URL_UPLOAD: &str = "https://up.flickr.com/services/upload/";
26
27pub use get_sizes::FlickrSize;
28pub use test_login::UserData;
29
30/// This is meant to turn the abominations the XML conversion creates into easier on the eyes
31/// structs:
32/// ```json
33/// "username": {
34///   "_contents": "itsame"
35/// }
36/// ```
37/// Becomes:
38/// ```rs
39/// username: String // "itsame"
40/// ```
41fn deserialize_content<'de, D>(deserializer: D) -> Result<String, D::Error>
42where
43    D: Deserializer<'de>,
44{
45    let v: Value = Deserialize::deserialize(deserializer)?;
46    Ok(v["_content"].as_str().unwrap_or("").to_string())
47}
48
49trait Resultable<T, E> {
50    fn to_result(self) -> Result<T, E>;
51}
52
53/// Common error type for all flickr API answers
54#[derive(Deserialize, Debug, Hash)]
55pub struct FlickrError {
56    pub stat: String,
57    pub code: u32,
58    pub message: String,
59}
60
61impl std::error::Error for FlickrError {}
62
63use std::fmt::Display;
64impl Display for FlickrError {
65    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
66        write!(formatter, "{} (code {})", self.message, self.code)
67    }
68}
69
70/// Convenience function to download an image using the library's client
71pub async fn download_image(url: &String) -> Result<Reader<Cursor<Bytes>>, Box<dyn Error>> {
72    let res = reqwest::get(url).await?;
73
74    Ok(Reader::new(Cursor::new(res.bytes().await?)))
75}
76
77#[derive(Clone)]
78struct FlickrAPIData {
79    client: reqwest::Client,
80    key: ApiKey,
81    token: Option<OauthToken>,
82}
83
84/// API client
85pub struct FlickrAPI {
86    data: Rc<FlickrAPIData>,
87}
88
89pub struct PhotoRequestBuilder {
90    handle: Rc<FlickrAPIData>,
91}
92
93pub struct TestRequestBuilder {
94    handle: Rc<FlickrAPIData>,
95}
96
97impl FlickrAPI {
98    pub fn new(key: ApiKey) -> Self {
99        let data = Rc::new(FlickrAPIData {
100            client: reqwest::Client::new(),
101            key,
102            token: None,
103        });
104
105        FlickrAPI { data }
106    }
107
108    pub fn with_token(self, token: OauthToken) -> Self {
109        let mut data = (*self.data).clone();
110        data.token = Some(token);
111
112        FlickrAPI {
113            data: Rc::new(data),
114        }
115    }
116
117    pub fn token(&self) -> Option<OauthToken> {
118        self.data.token.clone()
119    }
120
121    pub fn photos(&self) -> PhotoRequestBuilder {
122        PhotoRequestBuilder {
123            handle: self.data.clone(),
124        }
125    }
126
127    pub fn test(&self) -> TestRequestBuilder {
128        TestRequestBuilder {
129            handle: self.data.clone(),
130        }
131    }
132}