nexus_sdk/schema.rs
1//! Schema management operations
2
3use crate::client::NexusClient;
4use crate::error::{NexusError, Result};
5use serde::{Deserialize, Serialize};
6
7/// Create label request
8#[derive(Debug, Clone, Serialize)]
9pub struct CreateLabelRequest {
10 /// Label name
11 pub name: String,
12}
13
14/// Create label response
15#[derive(Debug, Clone, Deserialize)]
16pub struct CreateLabelResponse {
17 /// Label ID (may be 0 if error)
18 #[serde(default)]
19 pub label_id: u32,
20 /// Success message
21 pub message: String,
22 /// Error message if any
23 #[serde(skip_serializing_if = "Option::is_none")]
24 pub error: Option<String>,
25}
26
27/// One entry returned by `GET /schema/labels`.
28///
29/// The wire shape is `{"name": "Person", "id": 0}`. Earlier versions
30/// of the SDK exposed this as `Vec<(String, u32)>` and external
31/// callers split the tuple as `(name, count)` because the second
32/// member was unnamed (issue #2). The struct form keeps the meaning
33/// explicit and lets us add fields (e.g. `count`) without another
34/// breaking rename.
35#[derive(Debug, Clone, Deserialize, Serialize)]
36pub struct LabelInfo {
37 /// Label name as registered in the engine catalog.
38 pub name: String,
39 /// Catalog id allocated to this label.
40 pub id: u32,
41}
42
43/// List labels response
44#[derive(Debug, Clone, Deserialize)]
45pub struct ListLabelsResponse {
46 /// Labels registered in the catalog with their allocated ids.
47 pub labels: Vec<LabelInfo>,
48 /// Error message if any
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub error: Option<String>,
51}
52
53/// Create relationship type request
54#[derive(Debug, Clone, Serialize)]
55pub struct CreateRelTypeRequest {
56 /// Relationship type name
57 pub name: String,
58}
59
60/// Create relationship type response
61#[derive(Debug, Clone, Deserialize)]
62pub struct CreateRelTypeResponse {
63 /// Relationship type ID (may be 0 if error)
64 #[serde(default)]
65 pub type_id: u32,
66 /// Success message
67 pub message: String,
68 /// Error message if any
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub error: Option<String>,
71}
72
73/// One entry returned by `GET /schema/rel_types`.
74///
75/// Mirrors `LabelInfo` — same rationale, see issue #2.
76#[derive(Debug, Clone, Deserialize, Serialize)]
77pub struct RelTypeInfo {
78 /// Relationship type name as registered in the catalog.
79 pub name: String,
80 /// Catalog id allocated to this relationship type.
81 pub id: u32,
82}
83
84/// List relationship types response
85#[derive(Debug, Clone, Deserialize)]
86pub struct ListRelTypesResponse {
87 /// Relationship types registered in the catalog with their ids.
88 pub types: Vec<RelTypeInfo>,
89 /// Error message if any
90 #[serde(skip_serializing_if = "Option::is_none")]
91 pub error: Option<String>,
92}
93
94impl NexusClient {
95 /// Create a new label
96 ///
97 /// # Arguments
98 ///
99 /// * `name` - Label name
100 ///
101 /// # Example
102 ///
103 /// ```no_run
104 /// # use nexus_sdk::NexusClient;
105 /// # #[tokio::main]
106 /// # async fn main() -> Result<(), nexus_sdk::NexusError> {
107 /// # let client = NexusClient::new("http://localhost:15474")?;
108 /// let response = client.create_label("Person".to_string()).await?;
109 /// tracing::info!("Create label result: {}", response.message);
110 /// # Ok(())
111 /// # }
112 /// ```
113 pub async fn create_label(&self, name: String) -> Result<CreateLabelResponse> {
114 let request = CreateLabelRequest { name };
115
116 let url = self.get_base_url().join("/schema/labels")?;
117 let mut request_builder = self.get_client().post(url).json(&request);
118
119 request_builder = self.add_auth_headers(request_builder)?;
120
121 let response = self.execute_with_retry(request_builder).await?;
122 let status = response.status();
123
124 if status.is_success() {
125 let result: CreateLabelResponse = response.json().await?;
126 Ok(result)
127 } else {
128 let error_text = response
129 .text()
130 .await
131 .unwrap_or_else(|_| "Unknown error".to_string());
132 Err(NexusError::Api {
133 message: error_text,
134 status: status.as_u16(),
135 })
136 }
137 }
138
139 /// List all labels
140 ///
141 /// # Example
142 ///
143 /// ```no_run
144 /// # use nexus_sdk::NexusClient;
145 /// # #[tokio::main]
146 /// # async fn main() -> Result<(), nexus_sdk::NexusError> {
147 /// # let client = NexusClient::new("http://localhost:15474")?;
148 /// let response = client.list_labels().await?;
149 /// tracing::info!("Labels: {:?}", response.labels);
150 /// # Ok(())
151 /// # }
152 /// ```
153 pub async fn list_labels(&self) -> Result<ListLabelsResponse> {
154 let url = self.get_base_url().join("/schema/labels")?;
155 let mut request_builder = self.get_client().get(url);
156
157 request_builder = self.add_auth_headers(request_builder)?;
158
159 let response = self.execute_with_retry(request_builder).await?;
160 let status = response.status();
161
162 if status.is_success() {
163 let result: ListLabelsResponse = response.json().await?;
164 Ok(result)
165 } else {
166 let error_text = response
167 .text()
168 .await
169 .unwrap_or_else(|_| "Unknown error".to_string());
170 Err(NexusError::Api {
171 message: error_text,
172 status: status.as_u16(),
173 })
174 }
175 }
176
177 /// Create a new relationship type
178 ///
179 /// # Arguments
180 ///
181 /// * `name` - Relationship type name
182 ///
183 /// # Example
184 ///
185 /// ```no_run
186 /// # use nexus_sdk::NexusClient;
187 /// # #[tokio::main]
188 /// # async fn main() -> Result<(), nexus_sdk::NexusError> {
189 /// # let client = NexusClient::new("http://localhost:15474")?;
190 /// let response = client.create_rel_type("KNOWS".to_string()).await?;
191 /// tracing::info!("Create rel type result: {}", response.message);
192 /// # Ok(())
193 /// # }
194 /// ```
195 pub async fn create_rel_type(&self, name: String) -> Result<CreateRelTypeResponse> {
196 let request = CreateRelTypeRequest { name };
197
198 let url = self.get_base_url().join("/schema/rel_types")?;
199 let mut request_builder = self.get_client().post(url).json(&request);
200
201 request_builder = self.add_auth_headers(request_builder)?;
202
203 let response = self.execute_with_retry(request_builder).await?;
204 let status = response.status();
205
206 if status.is_success() {
207 let result: CreateRelTypeResponse = response.json().await?;
208 Ok(result)
209 } else {
210 let error_text = response
211 .text()
212 .await
213 .unwrap_or_else(|_| "Unknown error".to_string());
214 Err(NexusError::Api {
215 message: error_text,
216 status: status.as_u16(),
217 })
218 }
219 }
220
221 /// List all relationship types
222 ///
223 /// # Example
224 ///
225 /// ```no_run
226 /// # use nexus_sdk::NexusClient;
227 /// # #[tokio::main]
228 /// # async fn main() -> Result<(), nexus_sdk::NexusError> {
229 /// # let client = NexusClient::new("http://localhost:15474")?;
230 /// let response = client.list_rel_types().await?;
231 /// tracing::info!("Relationship types: {:?}", response.types);
232 /// # Ok(())
233 /// # }
234 /// ```
235 pub async fn list_rel_types(&self) -> Result<ListRelTypesResponse> {
236 let url = self.get_base_url().join("/schema/rel_types")?;
237 let mut request_builder = self.get_client().get(url);
238
239 request_builder = self.add_auth_headers(request_builder)?;
240
241 let response = self.execute_with_retry(request_builder).await?;
242 let status = response.status();
243
244 if status.is_success() {
245 let result: ListRelTypesResponse = response.json().await?;
246 Ok(result)
247 } else {
248 let error_text = response
249 .text()
250 .await
251 .unwrap_or_else(|_| "Unknown error".to_string());
252 Err(NexusError::Api {
253 message: error_text,
254 status: status.as_u16(),
255 })
256 }
257 }
258}