digitalocean_api/api/
load_balancer.rs

1use self::load_balancer_fields::{ForwardingRule, HealthCheck, StickySessions};
2use super::Region;
3use super::{ApiLinks, ApiMeta};
4use super::{HasPagination, HasResponse, HasValue};
5use crate::method::{Create, Delete, Get, List, Update};
6use crate::request::LoadBalancerRequest;
7use crate::request::Request;
8use crate::{ROOT_URL, STATIC_URL_ERROR};
9use chrono::{DateTime, Utc};
10use getset::{Getters, Setters};
11use serde::Deserialize;
12use serde::Serialize;
13use std::fmt::Display;
14use std::net::IpAddr;
15use url::Url;
16
17const LOAD_BALANCERS_SEGMENT: &str = "load_balancers";
18const DROPLETS_SEGMENT: &str = "droplets";
19const FORWARDING_RULES_SEGMENT: &str = "forwarding_rules";
20
21/// Load Balancers provide a way to distribute traffic across multiple
22/// Droplets.
23///
24/// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#load-balancers)
25#[derive(Deserialize, Serialize, Debug, Clone, Getters, Setters)]
26#[get = "pub"]
27pub struct LoadBalancer {
28    /// A unique ID that can be used to identify and reference a Load Balancer.
29    id: String,
30
31    /// A human-readable name for a Load Balancer instance.
32    name: String,
33
34    /// An attribute containing the public-facing IP address of the Load
35    /// Balancer.
36    ip: IpAddr,
37
38    /// The load balancing algorithm used to determine which backend Droplet
39    /// will be selected by a client. It must be either "round_robin" or
40    /// "least_connections".
41    algorithm: String,
42
43    /// A status string indicating the current state of the Load Balancer.
44    /// This can be "new", "active", or "errored".
45    status: String,
46
47    /// A time value given in ISO8601 combined date and time format that
48    /// represents when the Load Balancer was created.
49    created_at: DateTime<Utc>,
50
51    /// An object specifying the forwarding rules for a Load Balancer.
52    forwarding_rules: Vec<ForwardingRule>,
53
54    /// An object specifying health check settings for the Load Balancer.
55    health_check: HealthCheck,
56
57    /// An object specifying sticky sessions settings for the Load Balancer.
58    sticky_sessions: StickySessions,
59
60    /// The region where the Load Balancer instance is located.
61    region: Region,
62
63    /// The name of a Droplet tag corresponding to Droplets assigned to the
64    /// Load Balancer.
65    tag: String,
66
67    /// An array containing the IDs of the Droplets assigned to the Load
68    /// Balancer.
69    droplet_ids: Vec<usize>,
70
71    /// A boolean value indicating whether HTTP requests to the Load Balancer
72    /// on port 80 will be redirected to HTTPS on port 443.
73    redirect_http_to_https: bool,
74}
75
76/// Fields which exists inside Droplets.
77pub mod load_balancer_fields {
78    use serde::Serialize;
79
80    use serde::Deserialize;
81
82    /// This exists in the `forwarding_rules` field of a droplet.
83    ///
84    /// Forwarding rules determine how traffic will be routed from the Load
85    /// Balancer to the Droplets assigned to it. They can be used to configure
86    /// the type of traffic (HTTP, HTTPS, or TCP) and to map ports on the Load
87    /// Balancer to ports on the Droplets. For SSL encrypted traffic, you may
88    /// also configure whether to use SSL termination at the Load Balancer (by
89    /// specifying an SSL certificate) or to pass the encrypted traffic
90    /// through to the Droplet. Currently, each Load Balancer may have up to 15
91    /// forwarding rules.
92    #[derive(Deserialize, Serialize, Debug, Clone)]
93    pub struct ForwardingRule {
94        /// The protocol used for traffic to the Load Balancer. The possible
95        /// values are: "http", "https", or "tcp".
96        pub entry_protocol: String,
97
98        /// The port on which the Load Balancer instance will listen.
99        pub entry_port: usize,
100
101        /// The protocol used for traffic from the Load Balancer to the backend
102        /// Droplets. The possible values are: "http", "https", or "tcp".
103        pub target_protocol: String,
104
105        /// An integer representing the port on the backend Droplets to which
106        /// the Load Balancer will send traffic.
107        pub target_port: usize,
108
109        /// The ID of the TLS certificate used for SSL termination if enabled.
110        pub certificate_id: Option<String>,
111
112        /// A boolean value indicating whether SSL encrypted traffic will be
113        /// passed through to the backend Droplets.
114        pub tls_passthrough: bool,
115    }
116
117    impl ForwardingRule {
118        pub fn new<S: AsRef<str>>(
119            entry_protocol: S,
120            entry_port: usize,
121            target_protocol: S,
122            target_port: usize,
123        ) -> Self {
124            ForwardingRule {
125                entry_protocol: entry_protocol.as_ref().to_string(),
126                entry_port,
127                target_protocol: target_protocol.as_ref().to_string(),
128                target_port,
129                certificate_id: None,
130                tls_passthrough: false,
131            }
132        }
133
134        pub fn certificate_id<S: AsRef<str>>(mut self, certificate_id: Option<S>) -> Self {
135            self.certificate_id = certificate_id.map(|v| v.as_ref().to_string());
136            self
137        }
138
139        pub fn tls_passthrough(mut self, tls_passthrough: bool) -> Self {
140            self.tls_passthrough = tls_passthrough;
141            self
142        }
143    }
144
145    impl<S: AsRef<str>> From<(S, usize, S, usize)> for ForwardingRule {
146        fn from(val: (S, usize, S, usize)) -> Self {
147            ForwardingRule::new(val.0, val.1, val.2, val.3)
148        }
149    }
150
151    impl<S: AsRef<str>> From<(S, usize, S, usize, Option<S>)> for ForwardingRule {
152        fn from(val: (S, usize, S, usize, Option<S>)) -> Self {
153            ForwardingRule::new(val.0, val.1, val.2, val.3).certificate_id(val.4)
154        }
155    }
156
157    impl<S: AsRef<str>> From<(S, usize, S, usize, Option<S>, bool)> for ForwardingRule {
158        fn from(val: (S, usize, S, usize, Option<S>, bool)) -> Self {
159            ForwardingRule::new(val.0, val.1, val.2, val.3)
160                .certificate_id(val.4)
161                .tls_passthrough(val.5)
162        }
163    }
164
165    /// This exists in the `health_check` field of a droplet.
166    ///
167    /// Health checks are used to tell if a Droplet is responding and should
168    /// receive traffic. The Load Balancer will automatically stop sending
169    /// traffic to unresponsive Droplets. You may specify the protocol, port,
170    /// and path for a health check as well as additional setting such as the
171    /// check interval and response timeout.
172    #[derive(Deserialize, Serialize, Debug, Clone)]
173    pub struct HealthCheck {
174        /// The protocol used for health checks sent to the backend Droplets.
175        /// The possible values are "http" or "tcp".
176        pub protocol: String,
177
178        /// An integer representing the port on the backend Droplets on which
179        /// the health check will attempt a connection.
180        pub port: usize,
181
182        /// The path on the backend Droplets to which the Load Balancer
183        /// instance will send a request.
184        pub path: String,
185
186        /// The number of seconds between between two consecutive health
187        /// checks.
188        pub check_interval_seconds: usize,
189
190        /// The number of seconds the Load Balancer instance will wait for a
191        /// response until marking a health check as failed.
192        pub response_timeout_seconds: usize,
193
194        /// The number of times a health check must fail for a backend Droplet
195        /// to be marked "unhealthy" and be removed from the pool.
196        pub unhealthy_threshold: usize,
197
198        /// The number of times a health check must pass for a backend Droplet
199        /// to be marked "healthy" and be re-added to the pool.
200        pub healthy_threshold: usize,
201    }
202
203    /// This exists in the `sticky_sessions` field of a droplet.
204    ///
205    /// When sticky sessions are in use, follow up requests from a client will
206    /// be sent to the same Droplet as the original request. Both the name of
207    /// the cookie and the TTL are configurable.
208    #[derive(Deserialize, Serialize, Debug, Clone)]
209    pub struct StickySessions {
210        /// An attribute indicating how and if requests from a client will be
211        /// persistently served by the same backend Droplet. The possible
212        /// values are "cookies" or "none".
213        ///
214        /// *Note:* Since `type` is a keyword in Rust `kind` is used instead.
215        #[serde(rename = "type")]
216        pub kind: String,
217
218        /// The name of the cookie sent to the client. This attribute is only
219        /// returned when using "cookies" for the sticky sessions type.
220        pub cookie_name: Option<String>,
221
222        /// The number of seconds until the cookie set by the Load Balancer
223        /// expires. This attribute is only returned when using "cookies" for
224        /// the sticky sessions type.
225        pub cookie_ttl_seconds: Option<String>,
226    }
227}
228
229impl LoadBalancer {
230    /// Be sure to include a forwarding rule by chaining `.forwarding_rule()` onto this.
231    ///
232    /// **Note:** It may contain one of the droplets_ids or tag attributes as they are mutually exclusive.
233    ///
234    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
235    pub fn create<S>(name: S, region: S) -> LoadBalancerRequest<Create, LoadBalancer>
236    where
237        S: AsRef<str> + Serialize + Display,
238    {
239        let mut url = ROOT_URL.clone();
240        url.path_segments_mut()
241            .expect(STATIC_URL_ERROR)
242            .push(LOAD_BALANCERS_SEGMENT);
243
244        let mut req = Request::new(url);
245        req.set_body(json!({
246            "name": name,
247            "region": region,
248            "forwarding_rules": [],
249        }));
250        req
251    }
252
253    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#retrieve-an-existing-load-balancer)
254    pub fn get<S>(id: S) -> LoadBalancerRequest<Get, LoadBalancer>
255    where
256        S: AsRef<str> + Serialize + Display,
257    {
258        let mut url = ROOT_URL.clone();
259        url.path_segments_mut()
260            .expect(STATIC_URL_ERROR)
261            .push(LOAD_BALANCERS_SEGMENT)
262            .push(id.as_ref());
263
264        Request::new(url)
265    }
266
267    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#list-all-load-balancers)
268    pub fn list() -> LoadBalancerRequest<List, Vec<LoadBalancer>> {
269        let mut url = ROOT_URL.clone();
270        url.path_segments_mut()
271            .expect(STATIC_URL_ERROR)
272            .push(LOAD_BALANCERS_SEGMENT);
273
274        Request::new(url)
275    }
276
277    /// **Note:** Any attribute that is not provided will be reset to its default value.
278    ///
279    /// **Note:** It may contain one of the droplets_ids or tag attributes as they are mutually exclusive.
280    ///
281    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
282    pub fn update<S>(id: S) -> LoadBalancerRequest<Update, LoadBalancer>
283    where
284        S: AsRef<str> + Serialize + Display,
285    {
286        let mut url = ROOT_URL.clone();
287        url.path_segments_mut()
288            .expect(STATIC_URL_ERROR)
289            .push(LOAD_BALANCERS_SEGMENT)
290            .push(id.as_ref());
291
292        Request::new(url)
293    }
294    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#delete-a-load-balancer)
295    pub fn delete<S>(id: S) -> LoadBalancerRequest<Delete, ()>
296    where
297        S: AsRef<str> + Serialize + Display,
298    {
299        let mut url = ROOT_URL.clone();
300        url.path_segments_mut()
301            .expect(STATIC_URL_ERROR)
302            .push(LOAD_BALANCERS_SEGMENT)
303            .push(id.as_ref());
304
305        Request::new(url)
306    }
307}
308
309impl LoadBalancerRequest<Create, LoadBalancer> {
310    /// The load balancing algorithm used to determine which backend Droplet
311    /// will be selected by a client. It must be either "round_robin" or
312    /// "least_connections". The default value is "round_robin".
313    ///
314    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
315    pub fn algorithm<S>(mut self, val: S) -> LoadBalancerRequest<Create, LoadBalancer>
316    where
317        S: Display + Serialize,
318    {
319        self.body_mut()["algorithm"] = json!(val);
320        self
321    }
322
323    /// An array of objects specifying the forwarding rules for a Load
324    /// Balancer. At least one forwarding rule is required when creating a new
325    /// Load Balancer instance.
326    ///
327    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
328    pub fn forwarding_rule<T>(mut self, val: T) -> LoadBalancerRequest<Create, LoadBalancer>
329    where
330        T: Into<ForwardingRule>,
331    {
332        if !self.body_mut()["forwarding_rules"].is_array() {
333            self.body_mut()["forwarding_rules"] = json!([]);
334        }
335
336        {
337            let rules = self.body_mut()["forwarding_rules"].as_array_mut().expect(
338                "forwarding_rules \
339				 should always \
340				 be an array.\
341				 ",
342            );
343
344            rules.push(json!(val.into()));
345        }
346        self
347    }
348
349    /// The (optional) health check settings.
350    ///
351    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
352    #[allow(clippy::too_many_arguments)]
353    pub fn health_check<S: AsRef<str> + Display + Serialize>(
354        mut self,
355        protocol: S,
356        port: usize,
357        path: Option<S>,
358        check_interval_seconds: Option<usize>,
359        response_timeout_seconds: Option<usize>,
360        unhealthy_threshold: Option<usize>,
361        healthy_threshold: Option<usize>,
362    ) -> LoadBalancerRequest<Create, LoadBalancer> {
363        self.body_mut()["health_check"] = json!({
364            "protocol": protocol,
365            "port": port,
366        });
367
368        if let Some(path) = path {
369            self.body_mut()["health_check"]["path"] = json!(path);
370        }
371
372        if let Some(check_interval_seconds) = check_interval_seconds {
373            self.body_mut()["health_check"]["check_interval_seconds"] =
374                json!(check_interval_seconds);
375        }
376
377        if let Some(response_timeout_seconds) = response_timeout_seconds {
378            self.body_mut()["health_check"]["response_timeout_seconds"] =
379                json!(response_timeout_seconds);
380        }
381
382        if let Some(unhealthy_threshold) = unhealthy_threshold {
383            self.body_mut()["health_check"]["unhealthy_threshold"] = json!(unhealthy_threshold);
384        }
385
386        if let Some(healthy_threshold) = healthy_threshold {
387            self.body_mut()["health_check"]["healthy_threshold"] = json!(healthy_threshold);
388        }
389
390        self
391    }
392
393    /// The (optional) sticky sessions settings. `kind` must be `cookies` or
394    /// `none`. If `kind` is `cookies` then `cookie_name` and
395    /// `cookie_ttl_seconds` should be set as well.
396    ///
397    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
398    pub fn sticky_sessions<S: AsRef<str> + Display + Serialize>(
399        mut self,
400        kind: S,
401        cookie_name: Option<S>,
402        cookie_ttl_seconds: Option<usize>,
403    ) -> LoadBalancerRequest<Create, LoadBalancer> {
404        self.body_mut()["sticky_sessions"] = json!({
405            "type": kind,
406        });
407
408        if let Some(cookie_name) = cookie_name {
409            self.body_mut()["sticky_sessions"]["cookie_name"] = json!(cookie_name);
410        }
411
412        if let Some(cookie_ttl_seconds) = cookie_ttl_seconds {
413            self.body_mut()["sticky_sessions"]["cookie_ttl_seconds"] = json!(cookie_ttl_seconds);
414        }
415
416        self
417    }
418
419    /// A boolean value indicating whether HTTP requests to the Load Balancer
420    /// on port 80 will be redirected to HTTPS on port 443. Default value is false.
421    ///
422    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
423    pub fn redirect_http_to_https(
424        mut self,
425        setting: bool,
426    ) -> LoadBalancerRequest<Create, LoadBalancer> {
427        self.body_mut()["redirect_http_to_https"] = json!(setting);
428        self
429    }
430
431    /// The IDs of the Droplets to be assigned to the Load Balancer.
432    ///
433    /// **Note:** Not intended to be used alongside the `tag` function.
434    ///
435    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
436    pub fn droplets(mut self, ids: Vec<usize>) -> LoadBalancerRequest<Create, LoadBalancer> {
437        self.body_mut()["droplet_ids"] = json!(ids);
438        self
439    }
440
441    /// The name of a Droplet tag corresponding to Droplets to be assigned to
442    /// the Load Balancer.
443    ///
444    /// **Note:** Not intended to be used alongside the `droplets` function.
445    ///
446    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#create-a-new-load-balancer)
447    pub fn tag<S>(mut self, tag: S) -> LoadBalancerRequest<Create, LoadBalancer>
448    where
449        S: AsRef<str> + Display + Serialize,
450    {
451        self.body_mut()["tag"] = json!(tag);
452        self
453    }
454}
455
456impl LoadBalancerRequest<Update, LoadBalancer> {
457    /// A human-readable name for a Load Balancer instance.
458    ///
459    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
460    pub fn name<S>(mut self, val: S) -> LoadBalancerRequest<Update, LoadBalancer>
461    where
462        S: Display + Serialize,
463    {
464        self.body_mut()["name"] = json!(val);
465        self
466    }
467
468    /// The region where the Load Balancer instance will be located.
469    ///
470    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
471    pub fn region<S>(mut self, val: S) -> LoadBalancerRequest<Update, LoadBalancer>
472    where
473        S: Display + Serialize,
474    {
475        self.body_mut()["region"] = json!(val);
476        self
477    }
478
479    /// The load balancing algorithm used to determine which backend Droplet
480    /// will be selected by a client. It must be either "round_robin" or
481    /// "least_connections". The default value is "round_robin".
482    ///
483    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
484    pub fn algorithm<S>(mut self, val: S) -> LoadBalancerRequest<Update, LoadBalancer>
485    where
486        S: Display + Serialize,
487    {
488        self.body_mut()["algorithm"] = json!(val);
489        self
490    }
491
492    /// An array of objects specifying the forwarding rules for a Load
493    /// Balancer. At least one forwarding rule is required when creating a new
494    /// Load Balancer instance.
495    ///
496    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
497    pub fn forwarding_rule<T>(mut self, val: T) -> LoadBalancerRequest<Update, LoadBalancer>
498    where
499        T: Into<ForwardingRule>,
500    {
501        if !self.body_mut()["forwarding_rules"].is_array() {
502            self.body_mut()["forwarding_rules"] = json!([]);
503        }
504
505        {
506            let rules = self.body_mut()["forwarding_rules"].as_array_mut().expect(
507                "forwarding_rules \
508				 should always \
509				 be an array.\
510				 ",
511            );
512
513            rules.push(json!(val.into()));
514        }
515        self
516    }
517
518    /// The (optional) health check settings.
519    ///
520    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
521    #[allow(clippy::too_many_arguments)]
522    pub fn health_check<S: AsRef<str> + Display + Serialize>(
523        mut self,
524        protocol: S,
525        port: usize,
526        path: Option<S>,
527        check_interval_seconds: Option<usize>,
528        response_timeout_seconds: Option<usize>,
529        unhealthy_threshold: Option<usize>,
530        healthy_threshold: Option<usize>,
531    ) -> LoadBalancerRequest<Update, LoadBalancer> {
532        self.body_mut()["health_check"] = json!({
533            "protocol": protocol,
534            "port": port,
535        });
536
537        if let Some(path) = path {
538            self.body_mut()["health_check"]["path"] = json!(path);
539        }
540
541        if let Some(check_interval_seconds) = check_interval_seconds {
542            self.body_mut()["health_check"]["check_interval_seconds"] =
543                json!(check_interval_seconds);
544        }
545
546        if let Some(response_timeout_seconds) = response_timeout_seconds {
547            self.body_mut()["health_check"]["response_timeout_seconds"] =
548                json!(response_timeout_seconds);
549        }
550
551        if let Some(unhealthy_threshold) = unhealthy_threshold {
552            self.body_mut()["health_check"]["unhealthy_threshold"] = json!(unhealthy_threshold);
553        }
554
555        if let Some(healthy_threshold) = healthy_threshold {
556            self.body_mut()["health_check"]["healthy_threshold"] = json!(healthy_threshold);
557        }
558
559        self
560    }
561
562    /// The (optional) sticky sessions settings. `kind` must be `cookies` or
563    /// `none`. If `kind` is `cookies` then `cookie_name` and
564    /// `cookie_ttl_seconds` should be set as well.
565    ///
566    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
567    pub fn sticky_sessions<S: AsRef<str> + Display + Serialize>(
568        mut self,
569        kind: S,
570        cookie_name: Option<S>,
571        cookie_ttl_seconds: Option<usize>,
572    ) -> LoadBalancerRequest<Update, LoadBalancer> {
573        self.body_mut()["sticky_sessions"] = json!({
574            "type": kind,
575        });
576
577        if let Some(cookie_name) = cookie_name {
578            self.body_mut()["sticky_sessions"]["cookie_name"] = json!(cookie_name);
579        }
580
581        if let Some(cookie_ttl_seconds) = cookie_ttl_seconds {
582            self.body_mut()["sticky_sessions"]["cookie_ttl_seconds"] = json!(cookie_ttl_seconds);
583        }
584
585        self
586    }
587
588    /// A boolean value indicating whether HTTP requests to the Load Balancer
589    /// on port 80 will be redirected to HTTPS on port 443. Default value is false.
590    ///
591    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
592    pub fn redirect_http_to_https(
593        mut self,
594        setting: bool,
595    ) -> LoadBalancerRequest<Update, LoadBalancer> {
596        self.body_mut()["redirect_http_to_https"] = json!(setting);
597        self
598    }
599
600    /// The IDs of the Droplets to be assigned to the Load Balancer.
601    ///
602    /// **Note:** Not intended to be used alongside the `tag` function.
603    ///
604    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
605    pub fn droplets(mut self, ids: Vec<usize>) -> LoadBalancerRequest<Update, LoadBalancer> {
606        self.body_mut()["droplet_ids"] = json!(ids);
607        self
608    }
609
610    /// The name of a Droplet tag corresponding to Droplets to be assigned to
611    /// the Load Balancer.
612    ///
613    /// **Note:** Not intended to be used alongside the `droplets` function.
614    ///
615    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#update-a-load-balancer)
616    pub fn tag<S>(mut self, tag: S) -> LoadBalancerRequest<Update, LoadBalancer>
617    where
618        S: AsRef<str> + Display + Serialize,
619    {
620        self.body_mut()["tag"] = json!(tag);
621        self
622    }
623}
624
625impl LoadBalancerRequest<Get, LoadBalancer> {
626    /// Add droplets (by id) to the load balancer.
627    ///
628    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#add-droplets-to-a-load-balancer)
629    pub fn add_droplets(mut self, ids: Vec<usize>) -> LoadBalancerRequest<Create, ()> {
630        self.url_mut()
631            .path_segments_mut()
632            .expect(STATIC_URL_ERROR)
633            .push(DROPLETS_SEGMENT);
634
635        self.set_body(json!({
636            "droplet_ids": ids,
637        }));
638
639        self.transmute()
640    }
641
642    /// Remove droplets (by id) from the load balancer.
643    ///
644    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#remove-droplets-from-a-load-balancer)
645    pub fn remove_droplets(mut self, ids: Vec<usize>) -> LoadBalancerRequest<Delete, ()> {
646        self.url_mut()
647            .path_segments_mut()
648            .expect(STATIC_URL_ERROR)
649            .push(DROPLETS_SEGMENT);
650
651        self.set_body(json!({
652            "droplet_ids": ids,
653        }));
654
655        self.transmute()
656    }
657
658    /// Add a forwarding rule to the Load Balancer.
659    ///
660    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#add-forwarding-rules-to-a-load-balancer)
661    pub fn add_forwarding_rules<T>(mut self, items: Vec<T>) -> LoadBalancerRequest<Create, ()>
662    where
663        T: Into<ForwardingRule>,
664    {
665        self.url_mut()
666            .path_segments_mut()
667            .expect(STATIC_URL_ERROR)
668            .push(FORWARDING_RULES_SEGMENT);
669
670        if !self.body_mut()["forwarding_rules"].is_array() {
671            self.body_mut()["forwarding_rules"] = json!([]);
672        }
673
674        {
675            let rules = self.body_mut()["forwarding_rules"].as_array_mut().expect(
676                "forwarding_rules \
677				 should always \
678				 be an array.\
679				 ",
680            );
681
682            for item in items {
683                let rule: ForwardingRule = item.into();
684                rules.push(json!(rule));
685            }
686        }
687
688        self.transmute()
689    }
690
691    /// Remove a forwarding rule to the Load Balancer.
692    ///
693    /// [Digital Ocean Documentation.](https://developers.digitalocean.com/documentation/v2/#remove-forwarding-rules-from-a-load-balancer)
694    pub fn remove_forwarding_rules<T>(mut self, items: Vec<T>) -> LoadBalancerRequest<Delete, ()>
695    where
696        T: Into<ForwardingRule>,
697    {
698        self.url_mut()
699            .path_segments_mut()
700            .expect(STATIC_URL_ERROR)
701            .push(FORWARDING_RULES_SEGMENT);
702
703        if !self.body_mut()["forwarding_rules"].is_array() {
704            self.body_mut()["forwarding_rules"] = json!([]);
705        }
706
707        {
708            let rules = self.body_mut()["forwarding_rules"].as_array_mut().expect(
709                "forwarding_rules \
710				 should always \
711				 be an array.\
712				 ",
713            );
714
715            for item in items {
716                let rule: ForwardingRule = item.into();
717                rules.push(json!(rule));
718            }
719        }
720
721        self.transmute()
722    }
723}
724
725/// Response type returned from Digital Ocean.
726#[derive(Deserialize, Serialize, Debug, Clone)]
727pub struct LoadBalancerResponse {
728    load_balancer: LoadBalancer,
729}
730
731impl HasResponse for LoadBalancer {
732    type Response = LoadBalancerResponse;
733}
734
735impl HasValue for LoadBalancerResponse {
736    type Value = LoadBalancer;
737
738    fn value(self) -> LoadBalancer {
739        self.load_balancer
740    }
741}
742
743/// Response type returned from Digital Ocean.
744#[derive(Deserialize, Serialize, Debug, Clone)]
745pub struct LoadBalancerListResponse {
746    load_balancers: Vec<LoadBalancer>,
747    links: ApiLinks,
748    meta: ApiMeta,
749}
750
751impl HasResponse for Vec<LoadBalancer> {
752    type Response = LoadBalancerListResponse;
753}
754
755impl HasPagination for LoadBalancerListResponse {
756    fn next_page(&self) -> Option<Url> {
757        self.links.next()
758    }
759}
760
761impl HasValue for LoadBalancerListResponse {
762    type Value = Vec<LoadBalancer>;
763
764    fn value(self) -> Vec<LoadBalancer> {
765        self.load_balancers
766    }
767}