nimbuspulse_client/
lib.rs

1use anyhow::{bail, Ok, Result};
2use serde::Serialize;
3pub use types::instance::{Instance, InstanceStatus, Terrain};
4use types::{dcs_runtime::DcsRuntime, system_resources::ServerResources};
5use uuid::Uuid;
6
7mod types;
8
9#[derive(Debug, Serialize)]
10#[cfg_attr(test, derive(ts_rs::TS))]
11#[cfg_attr(test, ts(export, export_to = "../../javascript/lib/types/"))]
12pub struct CreateInstanceRequest {
13    pub user_id: String,
14    pub product_id: Uuid,
15    pub settings: DcsSettingsPayload,
16    pub active_mods: Vec<String>,
17    pub wanted_terrains: Vec<Terrain>,
18}
19
20#[derive(Debug, Serialize)]
21#[cfg_attr(test, derive(ts_rs::TS))]
22#[cfg_attr(test, ts(export, export_to = "../../javascript/lib/types/"))]
23pub struct DcsSettingsPayload {
24    pub initial_server_name: String,
25    pub initial_server_password: String,
26    pub initial_max_players: u32,
27    pub use_own_credentials: bool,
28    pub credentials: Option<DcsCredentials>,
29    pub initial_use_voice_chat: bool,
30}
31
32#[derive(Debug, Serialize)]
33#[cfg_attr(test, derive(ts_rs::TS))]
34#[cfg_attr(test, ts(export, export_to = "../../javascript/lib/types/"))]
35pub struct DcsCredentials {
36    pub username: String,
37    pub password: String,
38}
39
40#[derive(Debug, Clone)]
41pub struct Client {
42    api_key: String,
43    reqwest_client: reqwest::Client,
44}
45
46impl Client {
47    const BASE_URL: &'static str = "https://coordinator.nimbuspulse.com";
48
49    pub fn new(api_key: impl Into<String>) -> Self {
50        Self {
51            reqwest_client: reqwest::Client::new(),
52            api_key: api_key.into(),
53        }
54    }
55
56    pub fn set_api_key(&mut self, api_key: impl Into<String>) {
57        self.api_key = api_key.into();
58    }
59
60    pub async fn create_server(
61        &self,
62        name: impl Into<String>,
63        password: Option<impl Into<String>>,
64        max_players: u32,
65        plan: Uuid,
66        active_mods: Vec<impl Into<String>>,
67        terrains: Vec<Terrain>,
68        credentials: Option<DcsCredentials>,
69        use_voice_chat: bool,
70    ) -> Result<Instance> {
71        let payload = CreateInstanceRequest {
72            user_id: "".to_string(),
73            product_id: plan,
74            settings: DcsSettingsPayload {
75                initial_server_name: name.into(),
76                initial_server_password: password.map(|p| p.into()).unwrap_or_default(),
77                initial_max_players: max_players,
78                use_own_credentials: credentials.is_some(),
79                credentials,
80                initial_use_voice_chat: use_voice_chat,
81            },
82            active_mods: active_mods.into_iter().map(|m| m.into()).collect(),
83            wanted_terrains: terrains,
84        };
85
86        let response = self
87            .reqwest_client
88            .post(format!("{}/game_servers", Self::BASE_URL))
89            .bearer_auth(self.api_key.clone())
90            .json(&payload)
91            .send()
92            .await?;
93
94        if !response.status().is_success() {
95            bail!(format!(
96                "Failed to create server: {:?}",
97                response.text().await?
98            ));
99        }
100
101        Ok(response.json::<Instance>().await?)
102    }
103
104    pub async fn get_runtime(&self, id: &Uuid) -> Result<DcsRuntime> {
105        let response = self
106            .reqwest_client
107            .get(format!("{}/game_servers/{}/runtime", Self::BASE_URL, id))
108            .bearer_auth(self.api_key.clone())
109            .send()
110            .await?;
111
112        if !response.status().is_success() {
113            bail!(format!("Failed to get runtime: {:?}", response));
114        }
115
116        Ok(response.json::<DcsRuntime>().await?)
117    }
118
119    pub async fn get_server_resources(&self, id: &Uuid) -> Result<ServerResources> {
120        let response = self
121            .reqwest_client
122            .get(format!("{}/game_servers/{}/resources", Self::BASE_URL, id))
123            .bearer_auth(self.api_key.clone())
124            .send()
125            .await?;
126
127        if !response.status().is_success() {
128            bail!(format!("Failed to get server resources: {:?}", response));
129        }
130
131        Ok(response.json::<ServerResources>().await?)
132    }
133
134    pub async fn add_missions(&self, id: &Uuid, missions: Vec<String>) -> Result<()> {
135        let response = self
136            .reqwest_client
137            .post(format!(
138                "{}/game_servers/{}/dcs-api/missions",
139                Self::BASE_URL,
140                id
141            ))
142            .bearer_auth(self.api_key.clone())
143            .json(&missions)
144            .send()
145            .await?;
146
147        if !response.status().is_success() {
148            bail!("Failed to add missions: {:?}", response.text().await?);
149        }
150
151        Ok(())
152    }
153
154    pub async fn start_mission(&self, id: &Uuid, mission_idx: u32) -> Result<()> {
155        let response = self
156            .reqwest_client
157            .post(format!(
158                "{}/game_servers/{}/dcs-api/missions/{}/start",
159                Self::BASE_URL,
160                id,
161                mission_idx
162            ))
163            .bearer_auth(self.api_key.clone())
164            .send()
165            .await?;
166
167        if !response.status().is_success() {
168            bail!("Failed to start mission: {:?}", response);
169        }
170
171        Ok(())
172    }
173
174    pub async fn get_servers(&self) -> Result<Vec<Instance>> {
175        let response = self
176            .reqwest_client
177            .get(format!("{}/game_servers", Self::BASE_URL))
178            .bearer_auth(self.api_key.clone())
179            .send()
180            .await?;
181
182        if !response.status().is_success() {
183            bail!(format!("Failed to create server: {:?}", response));
184        }
185
186        Ok(response.json::<Vec<Instance>>().await?)
187    }
188
189    pub async fn start_server(&self, id: &Uuid) -> Result<()> {
190        let response = self
191            .reqwest_client
192            .post(format!("{}/game_servers/{}/start", Self::BASE_URL, id))
193            .bearer_auth(self.api_key.clone())
194            .send()
195            .await?;
196
197        if !response.status().is_success() {
198            bail!(format!("Failed to start server: {:?}", response));
199        }
200
201        Ok(())
202    }
203
204    pub async fn stop_server(&self, id: &Uuid) -> Result<()> {
205        let response = self
206            .reqwest_client
207            .post(format!("{}/game_servers/{}/stop", Self::BASE_URL, id))
208            .bearer_auth(self.api_key.clone())
209            .send()
210            .await?;
211
212        if !response.status().is_success() {
213            bail!(format!("Failed to stop server: {:?}", response));
214        }
215
216        Ok(())
217    }
218
219    pub async fn resume_server(&self, id: &Uuid) -> Result<()> {
220        let response = self
221            .reqwest_client
222            .post(format!(
223                "{}/game_servers/{}/dcs-api/resume",
224                Self::BASE_URL,
225                id
226            ))
227            .bearer_auth(self.api_key.clone())
228            .send()
229            .await?;
230
231        if !response.status().is_success() {
232            bail!("Failed to resume server: {:?}", response);
233        }
234
235        Ok(())
236    }
237
238    pub async fn pause_server(&self, id: &Uuid) -> Result<()> {
239        let response = self
240            .reqwest_client
241            .post(format!(
242                "{}/game_servers/{}/dcs-api/pause",
243                Self::BASE_URL,
244                id
245            ))
246            .bearer_auth(self.api_key.clone())
247            .send()
248            .await?;
249
250        if !response.status().is_success() {
251            bail!("Failed to pause server: {:?}", response);
252        }
253
254        Ok(())
255    }
256
257    pub async fn delete_server(&self, id: &Uuid) -> Result<()> {
258        let response = self
259            .reqwest_client
260            .delete(format!("{}/game_servers/{}", Self::BASE_URL, id))
261            .bearer_auth(self.api_key.clone())
262            .send()
263            .await?;
264
265        if !response.status().is_success() {
266            bail!(format!("Failed to delete server: {:?}", response));
267        }
268
269        Ok(())
270    }
271}