1use crate::client::NexusClient;
4use crate::error::{NexusError, Result};
5use crate::models::*;
6use serde::{Deserialize, Serialize};
7use std::collections::HashMap;
8
9#[derive(Debug, Clone, Serialize)]
11pub struct CreateNodeRequest {
12 pub labels: Vec<String>,
14 #[serde(default)]
16 pub properties: HashMap<String, Value>,
17 #[serde(default, skip_serializing_if = "Option::is_none")]
21 pub external_id: Option<String>,
22 #[serde(default, skip_serializing_if = "Option::is_none")]
25 pub conflict_policy: Option<String>,
26}
27
28#[derive(Debug, Clone, Deserialize)]
30pub struct CreateNodeResponse {
31 pub node_id: u64,
33 pub message: String,
35 #[serde(skip_serializing_if = "Option::is_none")]
37 pub error: Option<String>,
38}
39
40#[derive(Debug, Clone, Deserialize)]
42pub struct GetNodeResponse {
43 pub node: Option<Node>,
45 pub message: String,
47 #[serde(skip_serializing_if = "Option::is_none")]
49 pub error: Option<String>,
50}
51
52#[derive(Debug, Clone, Serialize)]
54pub struct UpdateNodeRequest {
55 pub node_id: u64,
57 pub properties: HashMap<String, Value>,
59}
60
61#[derive(Debug, Clone, Deserialize)]
63pub struct UpdateNodeResponse {
64 pub message: String,
66 #[serde(skip_serializing_if = "Option::is_none")]
68 pub error: Option<String>,
69}
70
71#[derive(Debug, Clone, Deserialize)]
73pub struct DeleteNodeResponse {
74 pub message: String,
76 #[serde(skip_serializing_if = "Option::is_none")]
78 pub error: Option<String>,
79}
80
81#[derive(Debug, Clone, Serialize)]
83pub struct CreateRelRequest {
84 pub source_id: u64,
86 pub target_id: u64,
88 pub rel_type: String,
90 #[serde(default)]
92 pub properties: HashMap<String, Value>,
93}
94
95#[derive(Debug, Clone, Deserialize)]
97pub struct CreateRelResponse {
98 pub rel_id: u64,
100 pub message: String,
102 #[serde(skip_serializing_if = "Option::is_none")]
104 pub error: Option<String>,
105}
106
107#[derive(Debug, Clone, Serialize)]
109pub struct UpdateRelRequest {
110 pub rel_id: u64,
112 pub properties: HashMap<String, Value>,
114}
115
116#[derive(Debug, Clone, Deserialize)]
118pub struct UpdateRelResponse {
119 pub message: String,
121 #[serde(skip_serializing_if = "Option::is_none")]
123 pub error: Option<String>,
124}
125
126#[derive(Debug, Clone, Deserialize)]
128pub struct DeleteRelResponse {
129 pub message: String,
131 #[serde(skip_serializing_if = "Option::is_none")]
133 pub error: Option<String>,
134}
135
136impl NexusClient {
137 pub async fn create_node(
160 &self,
161 labels: Vec<String>,
162 properties: HashMap<String, Value>,
163 ) -> Result<CreateNodeResponse> {
164 let request = CreateNodeRequest {
165 labels,
166 properties,
167 external_id: None,
168 conflict_policy: None,
169 };
170
171 let url = self.get_base_url().join("/data/nodes")?;
172 let mut request_builder = self.get_client().post(url).json(&request);
173
174 request_builder = self.add_auth_headers(request_builder)?;
175
176 let response = self.execute_with_retry(request_builder).await?;
177 let status = response.status();
178
179 if status.is_success() {
180 let result: CreateNodeResponse = response.json().await?;
181 Ok(result)
182 } else {
183 let error_text = response
184 .text()
185 .await
186 .unwrap_or_else(|_| "Unknown error".to_string());
187 Err(NexusError::Api {
188 message: error_text,
189 status: status.as_u16(),
190 })
191 }
192 }
193
194 pub async fn create_node_with_external_id(
203 &self,
204 labels: Vec<String>,
205 properties: HashMap<String, Value>,
206 external_id: impl Into<String>,
207 conflict_policy: Option<&str>,
208 ) -> Result<CreateNodeResponse> {
209 let request = CreateNodeRequest {
210 labels,
211 properties,
212 external_id: Some(external_id.into()),
213 conflict_policy: conflict_policy.map(str::to_owned),
214 };
215
216 let url = self.get_base_url().join("/data/nodes")?;
217 let mut request_builder = self.get_client().post(url).json(&request);
218 request_builder = self.add_auth_headers(request_builder)?;
219 let response = self.execute_with_retry(request_builder).await?;
220 let status = response.status();
221 if status.is_success() {
222 Ok(response.json().await?)
223 } else {
224 let error_text = response
225 .text()
226 .await
227 .unwrap_or_else(|_| "Unknown error".to_string());
228 Err(NexusError::Api {
229 message: error_text,
230 status: status.as_u16(),
231 })
232 }
233 }
234
235 pub async fn get_node_by_external_id(
239 &self,
240 external_id: impl AsRef<str>,
241 ) -> Result<GetNodeResponse> {
242 let mut url = self.get_base_url().join("/data/nodes/by-external-id")?;
243 url.query_pairs_mut()
244 .append_pair("external_id", external_id.as_ref());
245 let mut request_builder = self.get_client().get(url);
246 request_builder = self.add_auth_headers(request_builder)?;
247 let response = self.execute_with_retry(request_builder).await?;
248 let status = response.status();
249 if status.is_success() {
250 Ok(response.json().await?)
251 } else {
252 let error_text = response
253 .text()
254 .await
255 .unwrap_or_else(|_| "Unknown error".to_string());
256 Err(NexusError::Api {
257 message: error_text,
258 status: status.as_u16(),
259 })
260 }
261 }
262
263 pub async fn get_node(&self, node_id: u64) -> Result<GetNodeResponse> {
284 let url = self
285 .get_base_url()
286 .join(&format!("/data/nodes?id={}", node_id))?;
287 let mut request_builder = self.get_client().get(url);
288
289 request_builder = self.add_auth_headers(request_builder)?;
290
291 let response = self.execute_with_retry(request_builder).await?;
292 let status = response.status();
293
294 if status.is_success() {
295 let result: GetNodeResponse = response.json().await?;
296 Ok(result)
297 } else {
298 let error_text = response
299 .text()
300 .await
301 .unwrap_or_else(|_| "Unknown error".to_string());
302 Err(NexusError::Api {
303 message: error_text,
304 status: status.as_u16(),
305 })
306 }
307 }
308
309 pub async fn update_node(
332 &self,
333 node_id: u64,
334 properties: HashMap<String, Value>,
335 ) -> Result<UpdateNodeResponse> {
336 let request = UpdateNodeRequest {
337 node_id,
338 properties,
339 };
340
341 let url = self.get_base_url().join("/data/nodes")?;
342 let mut request_builder = self.get_client().put(url).json(&request);
343
344 request_builder = self.add_auth_headers(request_builder)?;
345
346 let response = self.execute_with_retry(request_builder).await?;
347 let status = response.status();
348
349 if status.is_success() {
350 let result: UpdateNodeResponse = response.json().await?;
351 Ok(result)
352 } else {
353 let error_text = response
354 .text()
355 .await
356 .unwrap_or_else(|_| "Unknown error".to_string());
357 Err(NexusError::Api {
358 message: error_text,
359 status: status.as_u16(),
360 })
361 }
362 }
363
364 pub async fn delete_node(&self, node_id: u64) -> Result<DeleteNodeResponse> {
383 let url = self
384 .get_base_url()
385 .join(&format!("/data/nodes?id={}", node_id))?;
386 let mut request_builder = self.get_client().delete(url);
387
388 request_builder = self.add_auth_headers(request_builder)?;
389
390 let response = self.execute_with_retry(request_builder).await?;
391 let status = response.status();
392
393 if status.is_success() {
394 let result: DeleteNodeResponse = response.json().await?;
395 Ok(result)
396 } else {
397 let error_text = response
398 .text()
399 .await
400 .unwrap_or_else(|_| "Unknown error".to_string());
401 Err(NexusError::Api {
402 message: error_text,
403 status: status.as_u16(),
404 })
405 }
406 }
407
408 pub async fn create_relationship(
433 &self,
434 source_id: u64,
435 target_id: u64,
436 rel_type: String,
437 properties: HashMap<String, Value>,
438 ) -> Result<CreateRelResponse> {
439 let request = CreateRelRequest {
440 source_id,
441 target_id,
442 rel_type,
443 properties,
444 };
445
446 let url = self.get_base_url().join("/data/relationships")?;
447 let mut request_builder = self.get_client().post(url).json(&request);
448
449 request_builder = self.add_auth_headers(request_builder)?;
450
451 let response = self.execute_with_retry(request_builder).await?;
452 let status = response.status();
453
454 if status.is_success() {
455 let result: CreateRelResponse = response.json().await?;
456 Ok(result)
457 } else {
458 let error_text = response
459 .text()
460 .await
461 .unwrap_or_else(|_| "Unknown error".to_string());
462 Err(NexusError::Api {
463 message: error_text,
464 status: status.as_u16(),
465 })
466 }
467 }
468
469 pub async fn update_relationship(
492 &self,
493 rel_id: u64,
494 properties: HashMap<String, Value>,
495 ) -> Result<UpdateRelResponse> {
496 let mut props_str = Vec::new();
498 let mut params = HashMap::new();
499
500 for (key, value) in properties {
501 let param_name = format!("prop_{}", key.replace('-', "_"));
502 props_str.push(format!("r.{} = ${}", key, param_name));
503 params.insert(param_name, value);
504 }
505
506 let query = format!(
507 "MATCH ()-[r]->() WHERE id(r) = $rel_id SET {} RETURN r",
508 props_str.join(", ")
509 );
510 params.insert("rel_id".to_string(), Value::Int(rel_id as i64));
511
512 let _result = self.execute_cypher(&query, Some(params)).await?;
513
514 Ok(UpdateRelResponse {
515 message: "Relationship updated successfully".to_string(),
516 error: None,
517 })
518 }
519
520 pub async fn delete_relationship(&self, rel_id: u64) -> Result<DeleteRelResponse> {
539 let mut params = HashMap::new();
540 params.insert("rel_id".to_string(), Value::Int(rel_id as i64));
541
542 let query = "MATCH ()-[r]->() WHERE id(r) = $rel_id DELETE r RETURN count(r) as deleted";
543 let _result = self.execute_cypher(query, Some(params)).await?;
544
545 Ok(DeleteRelResponse {
546 message: "Relationship deleted successfully".to_string(),
547 error: None,
548 })
549 }
550}