1use tracing::{debug, info};
4use uuid::Uuid;
5
6use crate::{
7 client::DaytonaClient,
8 error::Result,
9 models::volume::{CreateVolumeRequest, VolumeDto},
10};
11
12pub struct VolumeManager<'a> {
14 client: &'a DaytonaClient,
15}
16
17impl<'a> VolumeManager<'a> {
18 pub(crate) fn new(client: &'a DaytonaClient) -> Self {
19 Self { client }
20 }
21
22 pub async fn create(&self, name: String) -> Result<VolumeDto> {
24 info!("Creating new volume: {}", name);
25
26 let request = CreateVolumeRequest { name };
27
28 let response = self
29 .client
30 .build_request(reqwest::Method::POST, "/volumes")
31 .json(&request)
32 .send()
33 .await?;
34
35 self.client.handle_response(response).await
36 }
37
38 pub async fn delete(&self, volume_id: &Uuid) -> Result<()> {
40 info!("Deleting volume: {}", volume_id);
41
42 let response = self
43 .client
44 .build_request(reqwest::Method::DELETE, &format!("/volumes/{}", volume_id))
45 .send()
46 .await?;
47
48 if response.status().is_success() {
49 Ok(())
50 } else {
51 let error_text = response.text().await?;
52 Err(crate::error::DaytonaError::RequestFailed(format!(
53 "Failed to delete volume: {}",
54 error_text
55 )))
56 }
57 }
58
59 pub async fn get(&self, volume_id: &Uuid) -> Result<VolumeDto> {
61 debug!("Getting volume: {}", volume_id);
62
63 let response = self
64 .client
65 .build_request(reqwest::Method::GET, &format!("/volumes/{}", volume_id))
66 .send()
67 .await?;
68
69 self.client.handle_response(response).await
70 }
71
72 pub async fn get_by_name(&self, name: &str) -> Result<VolumeDto> {
74 debug!("Getting volume by name: {}", name);
75
76 let response = self
77 .client
78 .build_request(reqwest::Method::GET, &format!("/volumes/by-name/{}", name))
79 .send()
80 .await?;
81
82 self.client.handle_response(response).await
83 }
84
85 pub async fn list(&self, include_deleted: bool) -> Result<Vec<VolumeDto>> {
87 debug!("Listing volumes (include_deleted: {})", include_deleted);
88
89 let mut url = "/volumes".to_string();
90 if include_deleted {
91 url.push_str("?includeDeleted=true");
92 }
93
94 let response = self
95 .client
96 .build_request(reqwest::Method::GET, &url)
97 .send()
98 .await?;
99
100 self.client.handle_response(response).await
101 }
102
103 pub async fn wait_for_state(
105 &self,
106 volume_id: &Uuid,
107 target_state: crate::models::volume::VolumeState,
108 max_wait_seconds: u64,
109 ) -> Result<VolumeDto> {
110 let start = std::time::Instant::now();
111 let max_duration = std::time::Duration::from_secs(max_wait_seconds);
112
113 loop {
114 let volume = self.get(volume_id).await?;
115
116 if volume.state == target_state {
117 return Ok(volume);
118 }
119
120 if volume.state == crate::models::volume::VolumeState::Error {
122 return Err(crate::error::DaytonaError::RequestFailed(format!(
123 "Volume entered error state: {}",
124 volume
125 .error_reason
126 .unwrap_or_else(|| "Unknown error".to_string())
127 )));
128 }
129
130 if start.elapsed() > max_duration {
132 return Err(crate::error::DaytonaError::RequestFailed(format!(
133 "Timeout waiting for volume to reach {:?} state",
134 target_state
135 )));
136 }
137
138 tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
140 }
141 }
142}
143
144impl DaytonaClient {
145 pub fn volumes(&self) -> VolumeManager<'_> {
147 VolumeManager::new(self)
148 }
149}