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    type Composer = ();
176
177    fn info(&self) -> ServiceInfo {
178        self.info.clone()
179    }
180
181    fn capability(&self) -> Capability {
182        self.capability
183    }
184
185    async fn create_dir(
186        &self,
187        _ctx: &OperationContext,
188        _path: &str,
189        _args: OpCreateDir,
190    ) -> Result<RpCreateDir> {
191        Err(Error::new(
192            ErrorKind::Unsupported,
193            "operation is not supported",
194        ))
195    }
196
197    async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
198        let p = build_abs_path(&self.root, path);
199
200        if p == build_abs_path(&self.root, "") {
201            Ok(RpStat::new(MetadataBuilder::dir().build()))
202        } else {
203            let bs = self.core.get(&p)?;
204            match bs {
205                Some(bs) => Ok(RpStat::new({
206                    let metadata = MetadataBuilder::file(bs.len() as u64);
207                    metadata.build()
208                })),
209                None => Err(Error::new(ErrorKind::NotFound, "kv not found in redb")),
210            }
211        }
212    }
213    fn read(&self, _ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
214        let output: oio::StreamReader<RedbReader> = {
215            Ok(oio::StreamReader::new(RedbReader::new(
216                self.clone(),
217                path,
218                args,
219            )))
220        }?;
221
222        Ok(output)
223    }
224
225    fn write(&self, _ctx: &OperationContext, path: &str, _: OpWrite) -> Result<Self::Writer> {
226        let output: RedbWriter = {
227            let p = build_abs_path(&self.root, path);
228            Ok(RedbWriter::new(self.core.clone(), p))
229        }?;
230
231        Ok(output)
232    }
233
234    fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
235        let output: oio::OneShotDeleter<RedbDeleter> = {
236            Ok(oio::OneShotDeleter::new(RedbDeleter::new(
237                self.core.clone(),
238                self.root.clone(),
239            )))
240        }?;
241
242        Ok(output)
243    }
244
245    fn list(&self, _ctx: &OperationContext, _path: &str, _args: OpList) -> Result<Self::Lister> {
246        Err(Error::new(
247            ErrorKind::Unsupported,
248            "operation is not supported",
249        ))
250    }
251
252    fn copy(
253        &self,
254        _ctx: &OperationContext,
255        _from: &str,
256        _to: &str,
257        _args: OpCopy,
258    ) -> Result<Self::Copier> {
259        Err(Error::new(
260            ErrorKind::Unsupported,
261            "operation is not supported",
262        ))
263    }
264
265    async fn rename(
266        &self,
267        _ctx: &OperationContext,
268        _from: &str,
269        _to: &str,
270        _args: OpRename,
271    ) -> Result<RpRename> {
272        Err(Error::new(
273            ErrorKind::Unsupported,
274            "operation is not supported",
275        ))
276    }
277
278    async fn presign(
279        &self,
280        _ctx: &OperationContext,
281        _path: &str,
282        _args: OpPresign,
283    ) -> Result<RpPresign> {
284        Err(Error::new(
285            ErrorKind::Unsupported,
286            "operation is not supported",
287        ))
288    }
289}