1use std::{
6 collections::{BTreeMap, HashMap},
7 hash::Hash,
8};
9
10use hyper::Body;
11use serde::{Deserialize, Serialize};
12use serde_json::{json, Value};
13
14use crate::{
15 errors::{Error, Result},
16 Docker,
17};
18
19#[cfg(feature = "chrono")]
20use chrono::{DateTime, Utc};
21
22pub struct Volumes<'docker> {
26 docker: &'docker Docker,
27}
28
29impl<'docker> Volumes<'docker> {
30 pub fn new(docker: &'docker Docker) -> Self {
32 Volumes { docker }
33 }
34
35 pub async fn create(
39 &self,
40 opts: &VolumeCreateOptions,
41 ) -> Result<VolumeCreateInfo> {
42 let body: Body = opts.serialize()?.into();
43 let path = vec!["/volumes/create".to_owned()];
44
45 self.docker
46 .post_json(&path.join("?"), Some((body, mime::APPLICATION_JSON)))
47 .await
48 }
49
50 pub async fn list(&self) -> Result<Vec<VolumeInfo>> {
54 let path = vec!["/volumes".to_owned()];
55
56 let volumes_rep = self.docker.get_json::<VolumesInfo>(&path.join("?")).await?;
57 Ok(match volumes_rep.volumes {
58 Some(volumes) => volumes,
59 None => vec![],
60 })
61 }
62
63 pub fn get(
65 &self,
66 name: &str,
67 ) -> Volume<'docker> {
68 Volume::new(self.docker, name)
69 }
70}
71
72pub struct Volume<'docker> {
76 docker: &'docker Docker,
77 name: String,
78}
79
80impl<'docker> Volume<'docker> {
81 pub fn new<S>(
83 docker: &'docker Docker,
84 name: S,
85 ) -> Self
86 where
87 S: Into<String>,
88 {
89 Volume {
90 docker,
91 name: name.into(),
92 }
93 }
94
95 pub async fn delete(&self) -> Result<()> {
99 self.docker
100 .delete(&format!("/volumes/{}", self.name)[..])
101 .await?;
102 Ok(())
103 }
104}
105
106#[derive(Serialize, Debug)]
108pub struct VolumeCreateOptions {
109 params: HashMap<&'static str, Value>,
110}
111
112impl VolumeCreateOptions {
113 pub fn serialize(&self) -> Result<String> {
115 serde_json::to_string(&self.params).map_err(Error::from)
116 }
117
118 pub fn parse_from<'a, K, V>(
119 &self,
120 params: &'a HashMap<K, V>,
121 body: &mut BTreeMap<String, Value>,
122 ) where
123 &'a HashMap<K, V>: IntoIterator,
124 K: ToString + Eq + Hash,
125 V: Serialize,
126 {
127 for (k, v) in params.iter() {
128 let key = k.to_string();
129 let value = serde_json::to_value(v).unwrap();
130
131 body.insert(key, value);
132 }
133 }
134
135 pub fn builder() -> VolumeCreateOptionsBuilder {
137 VolumeCreateOptionsBuilder::new()
138 }
139}
140
141#[derive(Default)]
142pub struct VolumeCreateOptionsBuilder {
143 params: HashMap<&'static str, Value>,
144}
145
146impl VolumeCreateOptionsBuilder {
147 pub(crate) fn new() -> Self {
148 let params = HashMap::new();
149 VolumeCreateOptionsBuilder { params }
150 }
151
152 pub fn driver(
153 &mut self,
154 driver_name: &str,
155 driver_opts: Option<&HashMap<&str, &str>>,
156 ) -> &mut Self {
157 self.params.insert("Driver", json!(driver_name));
158 if let Some(opts) = driver_opts {
159 self.params.insert("DriverOpts", json!(opts));
160 }
161 self
162 }
163
164 pub fn name(
165 &mut self,
166 name: &str,
167 ) -> &mut Self {
168 self.params.insert("Name", json!(name));
169 self
170 }
171
172 pub fn labels(
173 &mut self,
174 labels: &HashMap<&str, &str>,
175 ) -> &mut Self {
176 self.params.insert("Labels", json!(labels));
177 self
178 }
179
180 pub fn build(&self) -> VolumeCreateOptions {
181 VolumeCreateOptions {
182 params: self.params.clone(),
183 }
184 }
185}
186
187#[derive(Clone, Debug, Serialize, Deserialize)]
188#[serde(rename_all = "PascalCase")]
189pub struct VolumeCreateInfo {
190 pub name: String,
191}
192
193#[derive(Clone, Debug, Serialize, Deserialize)]
194#[serde(rename_all = "PascalCase")]
195pub struct VolumesInfo {
196 pub volumes: Option<Vec<VolumeInfo>>,
197 pub warnings: Option<Vec<String>>,
198}
199
200#[derive(Clone, Debug, Serialize, Deserialize)]
201#[serde(rename_all = "PascalCase")]
202pub struct VolumeInfo {
203 #[cfg(feature = "chrono")]
204 pub created_at: DateTime<Utc>,
205 #[cfg(not(feature = "chrono"))]
206 pub created_at: String,
207 pub driver: String,
208 pub labels: Option<HashMap<String, String>>,
209 pub name: String,
210 pub mountpoint: String,
211 pub options: Option<HashMap<String, String>>,
212 pub scope: String,
213}
214
215#[cfg(test)]
216mod test {
217 use super::*;
218
219 #[derive(Deserialize)]
220 #[serde(rename_all = "PascalCase")]
221 pub struct VolumeTestInfo {
222 pub driver: Option<String>,
223 pub name: Option<String>,
224 pub driver_opts: Option<HashMap<String, String>>,
225 }
226
227 #[test]
228 fn test_volumecreateoptionsbuilder_driver() {
229 let volume = VolumeCreateOptions::builder()
230 .driver("my_driver", None)
231 .build();
232
233 let serialized = volume.serialize().unwrap();
234 let volume_info: VolumeTestInfo = serde_json::from_str(&serialized).unwrap();
235
236 assert_eq!(volume_info.driver, Some("my_driver".to_string()));
237 assert_eq!(volume_info.name, None);
238 assert_eq!(volume_info.driver_opts, None)
239 }
240
241 #[test]
242 fn test_volumecreateoptionsbuilder_driver_opts() {
243 let opts: HashMap<&str, &str> = [("option", "value")].iter().cloned().collect();
244 let volume = VolumeCreateOptions::builder()
245 .driver("my_driver", Some(&opts))
246 .build();
247
248 let serialized = volume.serialize().unwrap();
249 let volume_info: VolumeTestInfo = serde_json::from_str(&serialized).unwrap();
250 let mut driver_options = HashMap::new();
251 driver_options.insert("option".to_string(), "value".to_string());
252
253 assert_eq!(volume_info.driver, Some("my_driver".to_string()));
254 assert_eq!(volume_info.name, None);
255 assert_eq!(volume_info.driver_opts, Some(driver_options))
256 }
257}