opendal_service_surrealdb/
backend.rs1use std::fmt::Debug;
19use std::sync::Arc;
20
21use asyncband::once::OnceCell;
22
23use super::SURREALDB_SCHEME;
24use super::config::SurrealdbConfig;
25use super::core::*;
26use super::deleter::SurrealdbDeleter;
27use super::reader::*;
28use super::writer::SurrealdbWriter;
29use opendal_core::raw::*;
30use opendal_core::*;
31
32#[doc = include_str!("docs.md")]
33#[derive(Debug, Default)]
34pub struct SurrealdbBuilder {
35 pub(super) config: SurrealdbConfig,
36}
37
38impl SurrealdbBuilder {
39 pub fn connection_string(mut self, connection_string: &str) -> Self {
50 if !connection_string.is_empty() {
51 self.config.connection_string = Some(connection_string.to_string());
52 }
53 self
54 }
55
56 pub fn root(mut self, root: &str) -> Self {
60 self.config.root = if root.is_empty() {
61 None
62 } else {
63 Some(root.to_string())
64 };
65
66 self
67 }
68
69 pub fn table(mut self, table: &str) -> Self {
71 if !table.is_empty() {
72 self.config.table = Some(table.to_string());
73 }
74 self
75 }
76
77 pub fn username(mut self, username: &str) -> Self {
79 if !username.is_empty() {
80 self.config.username = Some(username.to_string());
81 }
82 self
83 }
84
85 pub fn password(mut self, password: &str) -> Self {
87 if !password.is_empty() {
88 self.config.password = Some(password.to_string());
89 }
90 self
91 }
92
93 pub fn namespace(mut self, namespace: &str) -> Self {
95 if !namespace.is_empty() {
96 self.config.namespace = Some(namespace.to_string());
97 }
98 self
99 }
100
101 pub fn database(mut self, database: &str) -> Self {
103 if !database.is_empty() {
104 self.config.database = Some(database.to_string());
105 }
106 self
107 }
108
109 pub fn key_field(mut self, key_field: &str) -> Self {
113 if !key_field.is_empty() {
114 self.config.key_field = Some(key_field.to_string());
115 }
116 self
117 }
118
119 pub fn value_field(mut self, value_field: &str) -> Self {
123 if !value_field.is_empty() {
124 self.config.value_field = Some(value_field.to_string());
125 }
126 self
127 }
128}
129
130impl Builder for SurrealdbBuilder {
131 type Config = SurrealdbConfig;
132
133 fn build(self) -> Result<impl Service> {
134 let connection_string = match self.config.connection_string.clone() {
135 Some(v) => v,
136 None => {
137 return Err(
138 Error::new(ErrorKind::ConfigInvalid, "connection_string is empty")
139 .with_context("service", SURREALDB_SCHEME),
140 );
141 }
142 };
143
144 let namespace = match self.config.namespace.clone() {
145 Some(v) => v,
146 None => {
147 return Err(Error::new(ErrorKind::ConfigInvalid, "namespace is empty")
148 .with_context("service", SURREALDB_SCHEME));
149 }
150 };
151 let database = match self.config.database.clone() {
152 Some(v) => v,
153 None => {
154 return Err(Error::new(ErrorKind::ConfigInvalid, "database is empty")
155 .with_context("service", SURREALDB_SCHEME));
156 }
157 };
158 let table = match self.config.table.clone() {
159 Some(v) => v,
160 None => {
161 return Err(Error::new(ErrorKind::ConfigInvalid, "table is empty")
162 .with_context("service", SURREALDB_SCHEME));
163 }
164 };
165
166 let username = self.config.username.clone().unwrap_or_default();
167 let password = self.config.password.clone().unwrap_or_default();
168 let key_field = self
169 .config
170 .key_field
171 .clone()
172 .unwrap_or_else(|| "key".to_string());
173 let value_field = self
174 .config
175 .value_field
176 .clone()
177 .unwrap_or_else(|| "value".to_string());
178 let root = normalize_root(
179 self.config
180 .root
181 .clone()
182 .unwrap_or_else(|| "/".to_string())
183 .as_str(),
184 );
185
186 Ok(SurrealdbBackend::new(SurrealdbCore {
187 db: OnceCell::new(),
188 connection_string,
189 username,
190 password,
191 namespace,
192 database,
193 table,
194 key_field,
195 value_field,
196 })
197 .with_normalized_root(root))
198 }
199}
200
201#[derive(Clone, Debug)]
203pub struct SurrealdbBackend {
204 pub(crate) core: Arc<SurrealdbCore>,
205 pub(crate) root: String,
206 pub(crate) info: ServiceInfo,
207 pub(crate) capability: Capability,
208}
209
210impl SurrealdbBackend {
211 pub fn new(core: SurrealdbCore) -> Self {
212 let info = ServiceInfo::new(SURREALDB_SCHEME, "/", &core.table);
213 let capability = Capability {
214 read: true,
215 stat: true,
216 write: true,
217 write_can_empty: true,
218 delete: true,
219 shared: true,
220 ..Default::default()
221 };
222
223 Self {
224 core: Arc::new(core),
225 root: "/".to_string(),
226 info,
227 capability,
228 }
229 }
230
231 fn with_normalized_root(mut self, root: String) -> Self {
232 self.info = self.info.with_root(&root);
233 self.root = root;
234 self
235 }
236}
237
238impl Service for SurrealdbBackend {
239 type Reader = oio::StreamReader<SurrealdbReader>;
240 type Writer = SurrealdbWriter;
241 type Lister = ();
242 type Deleter = oio::OneShotDeleter<SurrealdbDeleter>;
243 type Copier = ();
244 type Composer = ();
245
246 fn info(&self) -> ServiceInfo {
247 self.info.clone()
248 }
249
250 fn capability(&self) -> Capability {
251 self.capability
252 }
253
254 async fn create_dir(
255 &self,
256 _ctx: &OperationContext,
257 _path: &str,
258 _args: OpCreateDir,
259 ) -> Result<RpCreateDir> {
260 Err(Error::new(
261 ErrorKind::Unsupported,
262 "operation is not supported",
263 ))
264 }
265
266 async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
267 let p = build_abs_path(&self.root, path);
268
269 if p == build_abs_path(&self.root, "") {
270 Ok(RpStat::new(MetadataBuilder::dir().build()))
271 } else {
272 match self.core.get_length(&p).await? {
273 Some(length) => Ok(RpStat::new({
274 let metadata = MetadataBuilder::file(length as u64);
275 metadata.build()
276 })),
277 None => Err(Error::new(ErrorKind::NotFound, "kv not found in surrealdb")),
278 }
279 }
280 }
281 fn read(&self, _ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
282 let output: oio::StreamReader<SurrealdbReader> = {
283 Ok(oio::StreamReader::new(SurrealdbReader::new(
284 self.clone(),
285 path,
286 args,
287 )))
288 }?;
289
290 Ok(output)
291 }
292
293 fn write(&self, _ctx: &OperationContext, path: &str, _: OpWrite) -> Result<Self::Writer> {
294 let output: SurrealdbWriter = {
295 let p = build_abs_path(&self.root, path);
296 Ok(SurrealdbWriter::new(self.core.clone(), p))
297 }?;
298
299 Ok(output)
300 }
301
302 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
303 let output: oio::OneShotDeleter<SurrealdbDeleter> = {
304 Ok(oio::OneShotDeleter::new(SurrealdbDeleter::new(
305 self.core.clone(),
306 self.root.clone(),
307 )))
308 }?;
309
310 Ok(output)
311 }
312
313 fn list(&self, _ctx: &OperationContext, _path: &str, _args: OpList) -> Result<Self::Lister> {
314 Err(Error::new(
315 ErrorKind::Unsupported,
316 "operation is not supported",
317 ))
318 }
319
320 fn copy(
321 &self,
322 _ctx: &OperationContext,
323 _from: &str,
324 _to: &str,
325 _args: OpCopy,
326 ) -> Result<Self::Copier> {
327 Err(Error::new(
328 ErrorKind::Unsupported,
329 "operation is not supported",
330 ))
331 }
332
333 async fn rename(
334 &self,
335 _ctx: &OperationContext,
336 _from: &str,
337 _to: &str,
338 _args: OpRename,
339 ) -> Result<RpRename> {
340 Err(Error::new(
341 ErrorKind::Unsupported,
342 "operation is not supported",
343 ))
344 }
345
346 async fn presign(
347 &self,
348 _ctx: &OperationContext,
349 _path: &str,
350 _args: OpPresign,
351 ) -> Result<RpPresign> {
352 Err(Error::new(
353 ErrorKind::Unsupported,
354 "operation is not supported",
355 ))
356 }
357}