opendal/services/sled/
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::fmt::Formatter;
20use std::str;
21
22use crate::raw::adapters::kv;
23use crate::raw::*;
24use crate::services::SledConfig;
25use crate::Builder;
26use crate::Error;
27use crate::ErrorKind;
28use crate::Scheme;
29use crate::*;
30
31// https://github.com/spacejam/sled/blob/69294e59c718289ab3cb6bd03ac3b9e1e072a1e7/src/db.rs#L5
32const DEFAULT_TREE_ID: &str = r#"__sled__default"#;
33
34impl Configurator for SledConfig {
35    type Builder = SledBuilder;
36    fn into_builder(self) -> Self::Builder {
37        SledBuilder { config: self }
38    }
39}
40
41/// Sled services support.
42#[doc = include_str!("docs.md")]
43#[derive(Default)]
44pub struct SledBuilder {
45    config: SledConfig,
46}
47
48impl Debug for SledBuilder {
49    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
50        f.debug_struct("SledBuilder")
51            .field("config", &self.config)
52            .finish()
53    }
54}
55
56impl SledBuilder {
57    /// Set the path to the sled data directory. Will create if not exists.
58    pub fn datadir(mut self, path: &str) -> Self {
59        self.config.datadir = Some(path.into());
60        self
61    }
62
63    /// Set the root for sled.
64    pub fn root(mut self, root: &str) -> Self {
65        self.config.root = if root.is_empty() {
66            None
67        } else {
68            Some(root.to_string())
69        };
70
71        self
72    }
73
74    /// Set the tree for sled.
75    pub fn tree(mut self, tree: &str) -> Self {
76        self.config.tree = Some(tree.into());
77        self
78    }
79}
80
81impl Builder for SledBuilder {
82    const SCHEME: Scheme = Scheme::Sled;
83    type Config = SledConfig;
84
85    fn build(self) -> Result<impl Access> {
86        let datadir_path = self.config.datadir.ok_or_else(|| {
87            Error::new(ErrorKind::ConfigInvalid, "datadir is required but not set")
88                .with_context("service", Scheme::Sled)
89        })?;
90
91        let db = sled::open(&datadir_path).map_err(|e| {
92            Error::new(ErrorKind::ConfigInvalid, "open db")
93                .with_context("service", Scheme::Sled)
94                .with_context("datadir", datadir_path.clone())
95                .set_source(e)
96        })?;
97
98        // use "default" tree if not set
99        let tree_name = self
100            .config
101            .tree
102            .unwrap_or_else(|| DEFAULT_TREE_ID.to_string());
103
104        let tree = db.open_tree(&tree_name).map_err(|e| {
105            Error::new(ErrorKind::ConfigInvalid, "open tree")
106                .with_context("service", Scheme::Sled)
107                .with_context("datadir", datadir_path.clone())
108                .with_context("tree", tree_name.clone())
109                .set_source(e)
110        })?;
111
112        Ok(SledBackend::new(Adapter {
113            datadir: datadir_path,
114            tree,
115        })
116        .with_root(self.config.root.as_deref().unwrap_or("/")))
117    }
118}
119
120/// Backend for sled services.
121pub type SledBackend = kv::Backend<Adapter>;
122
123#[derive(Clone)]
124pub struct Adapter {
125    datadir: String,
126    tree: sled::Tree,
127}
128
129impl Debug for Adapter {
130    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
131        let mut ds = f.debug_struct("Adapter");
132        ds.field("path", &self.datadir);
133        ds.finish()
134    }
135}
136
137impl kv::Adapter for Adapter {
138    type Scanner = kv::Scanner;
139
140    fn info(&self) -> kv::Info {
141        kv::Info::new(
142            Scheme::Sled,
143            &self.datadir,
144            Capability {
145                read: true,
146                write: true,
147                list: true,
148                shared: false,
149                ..Default::default()
150            },
151        )
152    }
153
154    async fn get(&self, path: &str) -> Result<Option<Buffer>> {
155        Ok(self
156            .tree
157            .get(path)
158            .map_err(parse_error)?
159            .map(|v| Buffer::from(v.to_vec())))
160    }
161
162    async fn set(&self, path: &str, value: Buffer) -> Result<()> {
163        self.tree
164            .insert(path, value.to_vec())
165            .map_err(parse_error)?;
166        Ok(())
167    }
168
169    async fn delete(&self, path: &str) -> Result<()> {
170        self.tree.remove(path).map_err(parse_error)?;
171
172        Ok(())
173    }
174
175    async fn scan(&self, path: &str) -> Result<Self::Scanner> {
176        let it = self.tree.scan_prefix(path).keys();
177        let mut res = Vec::default();
178
179        for i in it {
180            let bs = i.map_err(parse_error)?.to_vec();
181            let v = String::from_utf8(bs).map_err(|err| {
182                Error::new(ErrorKind::Unexpected, "store key is not valid utf-8 string")
183                    .set_source(err)
184            })?;
185
186            res.push(v);
187        }
188
189        Ok(Box::new(kv::ScanStdIter::new(res.into_iter().map(Ok))))
190    }
191}
192
193fn parse_error(err: sled::Error) -> Error {
194    Error::new(ErrorKind::Unexpected, "error from sled").set_source(err)
195}