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