opendal/services/surrealdb/
backend.rs1use std::fmt::Debug;
19use std::fmt::Formatter;
20use std::sync::Arc;
21
22use tokio::sync::OnceCell;
23
24use super::config::SurrealdbConfig;
25use super::core::*;
26use super::deleter::SurrealdbDeleter;
27use super::writer::SurrealdbWriter;
28use crate::raw::*;
29use crate::*;
30
31#[doc = include_str!("docs.md")]
32#[derive(Default)]
33pub struct SurrealdbBuilder {
34 pub(super) config: SurrealdbConfig,
35}
36
37impl Debug for SurrealdbBuilder {
38 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
39 f.debug_struct("SurrealdbBuilder")
40 .field("config", &self.config)
41 .finish()
42 }
43}
44
45impl SurrealdbBuilder {
46 pub fn connection_string(mut self, connection_string: &str) -> Self {
57 if !connection_string.is_empty() {
58 self.config.connection_string = Some(connection_string.to_string());
59 }
60 self
61 }
62
63 pub fn root(mut self, root: &str) -> Self {
67 self.config.root = if root.is_empty() {
68 None
69 } else {
70 Some(root.to_string())
71 };
72
73 self
74 }
75
76 pub fn table(mut self, table: &str) -> Self {
78 if !table.is_empty() {
79 self.config.table = Some(table.to_string());
80 }
81 self
82 }
83
84 pub fn username(mut self, username: &str) -> Self {
86 if !username.is_empty() {
87 self.config.username = Some(username.to_string());
88 }
89 self
90 }
91
92 pub fn password(mut self, password: &str) -> Self {
94 if !password.is_empty() {
95 self.config.password = Some(password.to_string());
96 }
97 self
98 }
99
100 pub fn namespace(mut self, namespace: &str) -> Self {
102 if !namespace.is_empty() {
103 self.config.namespace = Some(namespace.to_string());
104 }
105 self
106 }
107
108 pub fn database(mut self, database: &str) -> Self {
110 if !database.is_empty() {
111 self.config.database = Some(database.to_string());
112 }
113 self
114 }
115
116 pub fn key_field(mut self, key_field: &str) -> Self {
120 if !key_field.is_empty() {
121 self.config.key_field = Some(key_field.to_string());
122 }
123 self
124 }
125
126 pub fn value_field(mut self, value_field: &str) -> Self {
130 if !value_field.is_empty() {
131 self.config.value_field = Some(value_field.to_string());
132 }
133 self
134 }
135}
136
137impl Builder for SurrealdbBuilder {
138 type Config = SurrealdbConfig;
139
140 fn build(self) -> Result<impl Access> {
141 let connection_string = match self.config.connection_string.clone() {
142 Some(v) => v,
143 None => {
144 return Err(
145 Error::new(ErrorKind::ConfigInvalid, "connection_string is empty")
146 .with_context("service", Scheme::Surrealdb),
147 );
148 }
149 };
150
151 let namespace = match self.config.namespace.clone() {
152 Some(v) => v,
153 None => {
154 return Err(Error::new(ErrorKind::ConfigInvalid, "namespace is empty")
155 .with_context("service", Scheme::Surrealdb));
156 }
157 };
158 let database = match self.config.database.clone() {
159 Some(v) => v,
160 None => {
161 return Err(Error::new(ErrorKind::ConfigInvalid, "database is empty")
162 .with_context("service", Scheme::Surrealdb));
163 }
164 };
165 let table = match self.config.table.clone() {
166 Some(v) => v,
167 None => {
168 return Err(Error::new(ErrorKind::ConfigInvalid, "table is empty")
169 .with_context("service", Scheme::Surrealdb));
170 }
171 };
172
173 let username = self.config.username.clone().unwrap_or_default();
174 let password = self.config.password.clone().unwrap_or_default();
175 let key_field = self
176 .config
177 .key_field
178 .clone()
179 .unwrap_or_else(|| "key".to_string());
180 let value_field = self
181 .config
182 .value_field
183 .clone()
184 .unwrap_or_else(|| "value".to_string());
185 let root = normalize_root(
186 self.config
187 .root
188 .clone()
189 .unwrap_or_else(|| "/".to_string())
190 .as_str(),
191 );
192
193 Ok(SurrealdbBackend::new(SurrealdbCore {
194 db: OnceCell::new(),
195 connection_string,
196 username,
197 password,
198 namespace,
199 database,
200 table,
201 key_field,
202 value_field,
203 })
204 .with_normalized_root(root))
205 }
206}
207
208#[derive(Clone, Debug)]
210pub struct SurrealdbBackend {
211 core: Arc<SurrealdbCore>,
212 root: String,
213 info: Arc<AccessorInfo>,
214}
215
216impl SurrealdbBackend {
217 pub fn new(core: SurrealdbCore) -> Self {
218 let info = AccessorInfo::default();
219 info.set_scheme(Scheme::Surrealdb.into_static());
220 info.set_name(&core.table);
221 info.set_root("/");
222 info.set_native_capability(Capability {
223 read: true,
224 stat: true,
225 write: true,
226 write_can_empty: true,
227 delete: true,
228 shared: true,
229 ..Default::default()
230 });
231
232 Self {
233 core: Arc::new(core),
234 root: "/".to_string(),
235 info: Arc::new(info),
236 }
237 }
238
239 fn with_normalized_root(mut self, root: String) -> Self {
240 self.info.set_root(&root);
241 self.root = root;
242 self
243 }
244}
245
246impl Access for SurrealdbBackend {
247 type Reader = Buffer;
248 type Writer = SurrealdbWriter;
249 type Lister = ();
250 type Deleter = oio::OneShotDeleter<SurrealdbDeleter>;
251
252 fn info(&self) -> Arc<AccessorInfo> {
253 self.info.clone()
254 }
255
256 async fn stat(&self, path: &str, _: OpStat) -> Result<RpStat> {
257 let p = build_abs_path(&self.root, path);
258
259 if p == build_abs_path(&self.root, "") {
260 Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
261 } else {
262 let bs = self.core.get(&p).await?;
263 match bs {
264 Some(bs) => Ok(RpStat::new(
265 Metadata::new(EntryMode::FILE).with_content_length(bs.len() as u64),
266 )),
267 None => Err(Error::new(ErrorKind::NotFound, "kv not found in surrealdb")),
268 }
269 }
270 }
271
272 async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
273 let p = build_abs_path(&self.root, path);
274 let bs = match self.core.get(&p).await? {
275 Some(bs) => bs,
276 None => {
277 return Err(Error::new(ErrorKind::NotFound, "kv not found in surrealdb"));
278 }
279 };
280 Ok((RpRead::new(), bs.slice(args.range().to_range_as_usize())))
281 }
282
283 async fn write(&self, path: &str, _: OpWrite) -> Result<(RpWrite, Self::Writer)> {
284 let p = build_abs_path(&self.root, path);
285 Ok((RpWrite::new(), SurrealdbWriter::new(self.core.clone(), p)))
286 }
287
288 async fn delete(&self) -> Result<(RpDelete, Self::Deleter)> {
289 Ok((
290 RpDelete::default(),
291 oio::OneShotDeleter::new(SurrealdbDeleter::new(self.core.clone(), self.root.clone())),
292 ))
293 }
294
295 async fn list(&self, path: &str, _: OpList) -> Result<(RpList, Self::Lister)> {
296 let _ = build_abs_path(&self.root, path);
297 Ok((RpList::default(), ()))
298 }
299}