Skip to main content

uni_db/api/
sync.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2024-2026 Dragonscale Team
3
4use crate::api::Uni;
5use crate::api::query_builder::QueryBuilder;
6use crate::api::transaction::Transaction;
7use std::sync::Arc;
8use uni_common::core::schema::{DataType, Schema};
9use uni_common::{Result, UniError};
10use uni_query::{ExecuteResult, QueryResult}; // for execute_internal? No, it's pub(crate).
11
12/// Blocking API wrapper for Uni.
13pub struct UniSync {
14    inner: Option<Uni>,
15    rt: tokio::runtime::Runtime,
16}
17
18impl UniSync {
19    pub fn new(inner: Uni) -> Result<Self> {
20        let rt = tokio::runtime::Runtime::new().map_err(UniError::Io)?;
21        Ok(Self {
22            inner: Some(inner),
23            rt,
24        })
25    }
26
27    /// Open an in-memory database (blocking)
28    pub fn in_memory() -> Result<Self> {
29        let rt = tokio::runtime::Runtime::new().map_err(UniError::Io)?;
30        let inner = rt.block_on(Uni::in_memory().build())?;
31        Ok(Self {
32            inner: Some(inner),
33            rt,
34        })
35    }
36
37    fn inner(&self) -> &Uni {
38        self.inner.as_ref().expect("UniSync already shut down")
39    }
40
41    pub fn query(&self, cypher: &str) -> Result<QueryResult> {
42        self.rt.block_on(self.inner().query(cypher))
43    }
44
45    pub fn execute(&self, cypher: &str) -> Result<ExecuteResult> {
46        self.rt.block_on(self.inner().execute(cypher))
47    }
48
49    pub fn query_with<'a>(&'a self, cypher: &'a str) -> QueryBuilderSync<'a> {
50        QueryBuilderSync {
51            inner: self.inner().query_with(cypher),
52            rt: &self.rt,
53        }
54    }
55
56    pub fn schema_meta(&self) -> Arc<Schema> {
57        self.inner().get_schema()
58    }
59
60    pub fn schema(&self) -> SchemaBuilderSync<'_> {
61        SchemaBuilderSync {
62            inner: self.inner().schema(),
63            rt: &self.rt,
64        }
65    }
66
67    pub fn begin(&self) -> Result<TransactionSync<'_>> {
68        let tx = self.rt.block_on(self.inner().begin())?;
69        Ok(TransactionSync { tx, rt: &self.rt })
70    }
71
72    /// Shutdown the database gracefully (blocking).
73    ///
74    /// Note: This consumes self, which prevents the Drop impl from also
75    /// triggering shutdown. Use this for explicit shutdown with error handling.
76    pub fn shutdown(mut self) -> Result<()> {
77        // Take ownership of the inner Uni to prevent Drop from also running
78        if let Some(uni) = self.inner.take() {
79            let result = self.rt.block_on(uni.shutdown());
80
81            // Prevent Drop from running by forgetting self
82            // (we've already done the cleanup in the async shutdown)
83            std::mem::forget(self);
84
85            result
86        } else {
87            Ok(()) // Already shut down
88        }
89    }
90}
91
92impl Drop for UniSync {
93    fn drop(&mut self) {
94        if let Some(ref uni) = self.inner {
95            uni.shutdown_handle.shutdown_blocking();
96            tracing::debug!("UniSync dropped");
97        }
98    }
99}
100
101pub struct TransactionSync<'a> {
102    tx: Transaction<'a>,
103    rt: &'a tokio::runtime::Runtime,
104}
105
106impl<'a> TransactionSync<'a> {
107    pub fn query(&self, cypher: &str) -> Result<QueryResult> {
108        self.rt.block_on(self.tx.query(cypher))
109    }
110
111    pub fn execute(&self, cypher: &str) -> Result<ExecuteResult> {
112        self.rt.block_on(self.tx.execute(cypher))
113    }
114
115    pub fn commit(self) -> Result<()> {
116        self.rt.block_on(self.tx.commit())
117    }
118
119    pub fn rollback(self) -> Result<()> {
120        self.rt.block_on(self.tx.rollback())
121    }
122}
123
124pub struct SchemaBuilderSync<'a> {
125    inner: crate::api::schema::SchemaBuilder<'a>,
126    rt: &'a tokio::runtime::Runtime,
127}
128
129impl<'a> SchemaBuilderSync<'a> {
130    pub fn label(self, name: &str) -> LabelBuilderSync<'a> {
131        LabelBuilderSync {
132            inner: self.inner.label(name),
133            rt: self.rt,
134        }
135    }
136
137    pub fn edge_type(self, name: &str, from: &[&str], to: &[&str]) -> EdgeTypeBuilderSync<'a> {
138        EdgeTypeBuilderSync {
139            inner: self.inner.edge_type(name, from, to),
140            rt: self.rt,
141        }
142    }
143
144    pub fn apply(self) -> Result<()> {
145        self.rt.block_on(self.inner.apply())
146    }
147}
148
149pub struct LabelBuilderSync<'a> {
150    inner: crate::api::schema::LabelBuilder<'a>,
151    rt: &'a tokio::runtime::Runtime,
152}
153
154impl<'a> LabelBuilderSync<'a> {
155    pub fn property(mut self, name: &str, data_type: DataType) -> Self {
156        self.inner = self.inner.property(name, data_type);
157        self
158    }
159
160    pub fn property_nullable(mut self, name: &str, data_type: DataType) -> Self {
161        self.inner = self.inner.property_nullable(name, data_type);
162        self
163    }
164
165    pub fn vector(mut self, name: &str, dimensions: usize) -> Self {
166        self.inner = self.inner.vector(name, dimensions);
167        self
168    }
169
170    pub fn done(self) -> SchemaBuilderSync<'a> {
171        SchemaBuilderSync {
172            inner: self.inner.done(),
173            rt: self.rt,
174        }
175    }
176
177    pub fn label(self, name: &str) -> LabelBuilderSync<'a> {
178        self.done().label(name)
179    }
180
181    pub fn apply(self) -> Result<()> {
182        self.rt.block_on(self.inner.apply())
183    }
184}
185
186pub struct EdgeTypeBuilderSync<'a> {
187    inner: crate::api::schema::EdgeTypeBuilder<'a>,
188    rt: &'a tokio::runtime::Runtime,
189}
190
191impl<'a> EdgeTypeBuilderSync<'a> {
192    pub fn property(mut self, name: &str, data_type: DataType) -> Self {
193        self.inner = self.inner.property(name, data_type);
194        self
195    }
196
197    pub fn property_nullable(mut self, name: &str, data_type: DataType) -> Self {
198        self.inner = self.inner.property_nullable(name, data_type);
199        self
200    }
201
202    pub fn done(self) -> SchemaBuilderSync<'a> {
203        SchemaBuilderSync {
204            inner: self.inner.done(),
205            rt: self.rt,
206        }
207    }
208
209    pub fn apply(self) -> Result<()> {
210        self.rt.block_on(self.inner.apply())
211    }
212}
213
214pub struct QueryBuilderSync<'a> {
215    inner: QueryBuilder<'a>,
216    rt: &'a tokio::runtime::Runtime,
217}
218
219impl<'a> QueryBuilderSync<'a> {
220    pub fn param(mut self, name: &str, value: impl Into<uni_query::Value>) -> Self {
221        self.inner = self.inner.param(name, value);
222        self
223    }
224
225    pub fn fetch_all(self) -> Result<QueryResult> {
226        self.rt.block_on(self.inner.fetch_all())
227    }
228}