Skip to main content

opendal_service_redb/
backend.rs

1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements.  See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership.  The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License.  You may obtain a copy of the License at
8//
9//   http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied.  See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18use std::fmt::Debug;
19use std::sync::Arc;
20
21use super::REDB_SCHEME;
22use super::config::RedbConfig;
23use super::core::*;
24use super::deleter::RedbDeleter;
25use super::reader::*;
26use super::writer::RedbWriter;
27use opendal_core::raw::*;
28use opendal_core::*;
29
30/// Redb service support.
31#[doc = include_str!("docs.md")]
32#[derive(Default)]
33pub struct RedbBuilder {
34    pub(super) config: RedbConfig,
35
36    pub(super) database: Option<Arc<redb::Database>>,
37}
38
39impl Debug for RedbBuilder {
40    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41        f.debug_struct("RedbBuilder")
42            .field("config", &self.config)
43            .finish_non_exhaustive()
44    }
45}
46
47impl RedbBuilder {
48    /// Set the database for Redb.
49    ///
50    /// This method should be called when you want to
51    /// use multiple tables of one database because
52    /// Redb doesn't allow opening a database that have been opened.
53    ///
54    /// <div class="warning">
55    ///
56    /// `datadir` and `database` should not be set simultaneously.
57    /// If both are set, `database` will take precedence.
58    ///
59    /// </div>
60    pub fn database(mut self, db: Arc<redb::Database>) -> Self {
61        self.database = Some(db);
62        self
63    }
64
65    /// Set the path to the redb data directory. Will create if not exists.
66    ///
67    ///
68    /// <div class="warning">
69    ///
70    /// Opening redb database via `datadir` takes away the ability to access multiple redb tables.
71    /// If you need to access multiple redb tables, the correct solution is to
72    /// create an `Arc<redb::database>` beforehand and then share it via [`database`]
73    /// with multiple builders where every builder will open one redb table.
74    ///
75    /// </div>
76    ///
77    /// [`database`]: RedbBuilder::database
78    pub fn datadir(mut self, path: &str) -> Self {
79        self.config.datadir = Some(path.into());
80        self
81    }
82
83    /// Set the table name for Redb. Will create if not exists.
84    pub fn table(mut self, table: &str) -> Self {
85        self.config.table = Some(table.into());
86        self
87    }
88
89    /// Set the root for Redb.
90    pub fn root(mut self, path: &str) -> Self {
91        self.config.root = Some(path.into());
92        self
93    }
94}
95
96impl Builder for RedbBuilder {
97    type Config = RedbConfig;
98
99    fn build(self) -> Result<impl Service> {
100        let table_name = self.config.table.ok_or_else(|| {
101            Error::new(ErrorKind::ConfigInvalid, "table is required but not set")
102                .with_context("service", REDB_SCHEME)
103        })?;
104
105        let (datadir, db) = if let Some(db) = self.database {
106            (None, db)
107        } else {
108            let datadir = self.config.datadir.ok_or_else(|| {
109                Error::new(ErrorKind::ConfigInvalid, "datadir is required but not set")
110                    .with_context("service", REDB_SCHEME)
111            })?;
112
113            let db = redb::Database::create(&datadir)
114                .map_err(parse_database_error)?
115                .into();
116
117            (Some(datadir), db)
118        };
119
120        create_table(&db, &table_name)?;
121
122        let root = normalize_root(&self.config.root.unwrap_or_default());
123
124        Ok(RedbBackend::new(RedbCore {
125            datadir,
126            table: table_name,
127            db,
128        })
129        .with_normalized_root(root))
130    }
131}
132
133/// Backend for Redb services.
134#[derive(Clone, Debug)]
135pub struct RedbBackend {
136    pub(crate) core: Arc<RedbCore>,
137    pub(crate) root: String,
138    pub(crate) info: ServiceInfo,
139    pub(crate) capability: Capability,
140}
141
142impl RedbBackend {
143    pub fn new(core: RedbCore) -> Self {
144        let info = ServiceInfo::new(REDB_SCHEME, "/", &core.table);
145        let capability = Capability {
146            read: true,
147            stat: true,
148            write: true,
149            write_can_empty: true,
150            delete: true,
151            ..Default::default()
152        };
153
154        Self {
155            core: Arc::new(core),
156            root: "/".to_string(),
157            info,
158            capability,
159        }
160    }
161
162    fn with_normalized_root(mut self, root: String) -> Self {
163        self.info = self.info.with_root(&root);
164        self.root = root;
165        self
166    }
167}
168
169impl Service for RedbBackend {
170    type Reader = oio::StreamReader<RedbReader>;
171    type Writer = RedbWriter;
172    type Lister = ();
173    type Deleter = oio::OneShotDeleter<RedbDeleter>;
174    type Copier = ();
175
176    fn info(&self) -> ServiceInfo {
177        self.info.clone()
178    }
179
180    fn capability(&self) -> Capability {
181        self.capability
182    }
183
184    async fn create_dir(
185        &self,
186        _ctx: &OperationContext,
187        _path: &str,
188        _args: OpCreateDir,
189    ) -> Result<RpCreateDir> {
190        Err(Error::new(
191            ErrorKind::Unsupported,
192            "operation is not supported",
193        ))
194    }
195
196    async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
197        let p = build_abs_path(&self.root, path);
198
199        if p == build_abs_path(&self.root, "") {
200            Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
201        } else {
202            let bs = self.core.get(&p)?;
203            match bs {
204                Some(bs) => Ok(RpStat::new(
205                    Metadata::new(EntryMode::FILE).with_content_length(bs.len() as u64),
206                )),
207                None => Err(Error::new(ErrorKind::NotFound, "kv not found in redb")),
208            }
209        }
210    }
211    fn read(&self, _ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
212        let output: oio::StreamReader<RedbReader> = {
213            Ok(oio::StreamReader::new(RedbReader::new(
214                self.clone(),
215                path,
216                args,
217            )))
218        }?;
219
220        Ok(output)
221    }
222
223    fn write(&self, _ctx: &OperationContext, path: &str, _: OpWrite) -> Result<Self::Writer> {
224        let output: RedbWriter = {
225            let p = build_abs_path(&self.root, path);
226            Ok(RedbWriter::new(self.core.clone(), p))
227        }?;
228
229        Ok(output)
230    }
231
232    fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
233        let output: oio::OneShotDeleter<RedbDeleter> = {
234            Ok(oio::OneShotDeleter::new(RedbDeleter::new(
235                self.core.clone(),
236                self.root.clone(),
237            )))
238        }?;
239
240        Ok(output)
241    }
242
243    fn list(&self, _ctx: &OperationContext, _path: &str, _args: OpList) -> Result<Self::Lister> {
244        Err(Error::new(
245            ErrorKind::Unsupported,
246            "operation is not supported",
247        ))
248    }
249
250    fn copy(
251        &self,
252        _ctx: &OperationContext,
253        _from: &str,
254        _to: &str,
255        _args: OpCopy,
256        _opts: OpCopier,
257    ) -> Result<Self::Copier> {
258        Err(Error::new(
259            ErrorKind::Unsupported,
260            "operation is not supported",
261        ))
262    }
263
264    async fn rename(
265        &self,
266        _ctx: &OperationContext,
267        _from: &str,
268        _to: &str,
269        _args: OpRename,
270    ) -> Result<RpRename> {
271        Err(Error::new(
272            ErrorKind::Unsupported,
273            "operation is not supported",
274        ))
275    }
276
277    async fn presign(
278        &self,
279        _ctx: &OperationContext,
280        _path: &str,
281        _args: OpPresign,
282    ) -> Result<RpPresign> {
283        Err(Error::new(
284            ErrorKind::Unsupported,
285            "operation is not supported",
286        ))
287    }
288}