opendal_service_surrealdb/
backend.rs1use std::fmt::Debug;
19use std::sync::Arc;
20
21use mea::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
245 fn info(&self) -> ServiceInfo {
246 self.info.clone()
247 }
248
249 fn capability(&self) -> Capability {
250 self.capability
251 }
252
253 async fn create_dir(
254 &self,
255 _ctx: &OperationContext,
256 _path: &str,
257 _args: OpCreateDir,
258 ) -> Result<RpCreateDir> {
259 Err(Error::new(
260 ErrorKind::Unsupported,
261 "operation is not supported",
262 ))
263 }
264
265 async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
266 let p = build_abs_path(&self.root, path);
267
268 if p == build_abs_path(&self.root, "") {
269 Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
270 } else {
271 match self.core.get_length(&p).await? {
272 Some(length) => Ok(RpStat::new(
273 Metadata::new(EntryMode::FILE).with_content_length(length as u64),
274 )),
275 None => Err(Error::new(ErrorKind::NotFound, "kv not found in surrealdb")),
276 }
277 }
278 }
279 fn read(&self, _ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
280 let output: oio::StreamReader<SurrealdbReader> = {
281 Ok(oio::StreamReader::new(SurrealdbReader::new(
282 self.clone(),
283 path,
284 args,
285 )))
286 }?;
287
288 Ok(output)
289 }
290
291 fn write(&self, _ctx: &OperationContext, path: &str, _: OpWrite) -> Result<Self::Writer> {
292 let output: SurrealdbWriter = {
293 let p = build_abs_path(&self.root, path);
294 Ok(SurrealdbWriter::new(self.core.clone(), p))
295 }?;
296
297 Ok(output)
298 }
299
300 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
301 let output: oio::OneShotDeleter<SurrealdbDeleter> = {
302 Ok(oio::OneShotDeleter::new(SurrealdbDeleter::new(
303 self.core.clone(),
304 self.root.clone(),
305 )))
306 }?;
307
308 Ok(output)
309 }
310
311 fn list(&self, _ctx: &OperationContext, _path: &str, _args: OpList) -> Result<Self::Lister> {
312 Err(Error::new(
313 ErrorKind::Unsupported,
314 "operation is not supported",
315 ))
316 }
317
318 fn copy(
319 &self,
320 _ctx: &OperationContext,
321 _from: &str,
322 _to: &str,
323 _args: OpCopy,
324 _opts: OpCopier,
325 ) -> Result<Self::Copier> {
326 Err(Error::new(
327 ErrorKind::Unsupported,
328 "operation is not supported",
329 ))
330 }
331
332 async fn rename(
333 &self,
334 _ctx: &OperationContext,
335 _from: &str,
336 _to: &str,
337 _args: OpRename,
338 ) -> Result<RpRename> {
339 Err(Error::new(
340 ErrorKind::Unsupported,
341 "operation is not supported",
342 ))
343 }
344
345 async fn presign(
346 &self,
347 _ctx: &OperationContext,
348 _path: &str,
349 _args: OpPresign,
350 ) -> Result<RpPresign> {
351 Err(Error::new(
352 ErrorKind::Unsupported,
353 "operation is not supported",
354 ))
355 }
356}