digitalocean_api/api/
mod.rs

1//! API specific documentation.
2
3mod account;
4mod action;
5mod certificate;
6mod custom_image;
7mod domain;
8mod domain_record;
9mod droplet;
10mod droplet_action;
11mod floating_ip;
12mod floating_ip_action;
13mod image;
14mod image_action;
15mod load_balancer;
16mod region;
17mod size;
18mod snapshot;
19mod ssh_key;
20mod tag;
21mod volume;
22mod volume_action;
23
24use serde::de::DeserializeOwned;
25use serde::{Deserialize, Serialize};
26use url::Url;
27
28mod url_option_serde {
29    use serde::{Deserialize, Deserializer, Serializer};
30    use url::Url;
31
32    pub fn serialize<S>(url: &Option<Url>, serializer: S) -> Result<S::Ok, S::Error>
33    where
34        S: Serializer,
35    {
36        match url {
37            Some(url) => serializer.serialize_some(&url.to_string()),
38            None => serializer.serialize_none(),
39        }
40    }
41
42    pub fn deserialize<'de, D>(deserializer: D) -> Result<Option<Url>, D::Error>
43    where
44        D: Deserializer<'de>,
45    {
46        let url_str: Option<String> = Option::deserialize(deserializer)?;
47        match url_str {
48            Some(s) => {
49                let url = Url::parse(&s).map_err(serde::de::Error::custom)?;
50                Ok(Some(url))
51            }
52            None => Ok(None),
53        }
54    }
55}
56
57pub use self::account::Account;
58pub use self::action::Action;
59pub use self::certificate::Certificate;
60pub use self::custom_image::CustomImage;
61pub use self::domain::Domain;
62pub use self::domain_record::DomainRecord;
63pub use self::droplet::{droplet_fields, Droplet};
64pub use self::floating_ip::FloatingIp;
65pub use self::image::Image;
66pub use self::load_balancer::{load_balancer_fields, LoadBalancer};
67pub use self::region::Region;
68pub use self::size::Size;
69pub use self::snapshot::Snapshot;
70pub use self::ssh_key::SshKey;
71pub use self::tag::Tag;
72pub use self::volume::Volume;
73
74// Defined in https://developers.digitalocean.com/documentation/v2/#links
75pub const MAX_PER_PAGE: usize = 200;
76
77#[derive(Deserialize, Serialize, Debug, Clone)]
78struct ApiLinks {
79    pages: Option<ApiPages>,
80}
81
82impl ApiLinks {
83    fn next(&self) -> Option<Url> {
84        match self.pages {
85            Some(ref pages) => pages.next.clone(),
86            None => None,
87        }
88    }
89}
90
91#[derive(Deserialize, Serialize, Debug, Clone)]
92struct ApiPages {
93    #[serde(with = "url_option_serde", default)]
94    prev: Option<Url>,
95
96    #[serde(with = "url_option_serde", default)]
97    first: Option<Url>,
98
99    #[serde(with = "url_option_serde", default)]
100    next: Option<Url>,
101
102    #[serde(with = "url_option_serde", default)]
103    last: Option<Url>,
104}
105
106#[derive(Deserialize, Serialize, Debug, Clone)]
107struct ApiMeta {
108    total: usize,
109}
110
111pub trait HasPagination {
112    fn next_page(&self) -> Option<Url>;
113}
114
115pub trait HasValue {
116    type Value: DeserializeOwned;
117    fn value(self) -> Self::Value;
118}
119
120impl HasValue for () {
121    type Value = ();
122
123    fn value(self) -> Self::Value {}
124}
125
126pub trait HasResponse: DeserializeOwned + Clone {
127    type Response: DeserializeOwned + Clone + HasValue<Value = Self>;
128}
129
130impl HasResponse for () {
131    type Response = ();
132}