1use std::fmt::Debug;
19use std::sync::Arc;
20
21use super::PERSY_SCHEME;
22use super::config::PersyConfig;
23use super::core::*;
24use super::deleter::PersyDeleter;
25use super::reader::*;
26use super::writer::PersyWriter;
27use opendal_core::raw::*;
28use opendal_core::*;
29
30#[doc = include_str!("docs.md")]
32#[derive(Debug, Default)]
33pub struct PersyBuilder {
34 pub(super) config: PersyConfig,
35}
36
37impl PersyBuilder {
38 pub fn datafile(mut self, path: &str) -> Self {
40 self.config.datafile = Some(path.into());
41 self
42 }
43
44 pub fn segment(mut self, path: &str) -> Self {
46 self.config.segment = Some(path.into());
47 self
48 }
49
50 pub fn index(mut self, path: &str) -> Self {
52 self.config.index = Some(path.into());
53 self
54 }
55}
56
57impl Builder for PersyBuilder {
58 type Config = PersyConfig;
59
60 fn build(self) -> Result<impl Service> {
61 let datafile_path = self.config.datafile.ok_or_else(|| {
62 Error::new(ErrorKind::ConfigInvalid, "datafile is required but not set")
63 .with_context("service", PERSY_SCHEME)
64 })?;
65
66 let segment_name = self.config.segment.ok_or_else(|| {
67 Error::new(ErrorKind::ConfigInvalid, "segment is required but not set")
68 .with_context("service", PERSY_SCHEME)
69 })?;
70
71 let segment = segment_name.clone();
72
73 let index_name = self.config.index.ok_or_else(|| {
74 Error::new(ErrorKind::ConfigInvalid, "index is required but not set")
75 .with_context("service", PERSY_SCHEME)
76 })?;
77
78 let index = index_name.clone();
79
80 let persy = persy::OpenOptions::new()
81 .create(true)
82 .prepare_with(move |p| init(p, &segment_name, &index_name))
83 .open(&datafile_path)
84 .map_err(|e| {
85 Error::new(ErrorKind::ConfigInvalid, "open db")
86 .with_context("service", PERSY_SCHEME)
87 .with_context("datafile", datafile_path.clone())
88 .set_source(e)
89 })?;
90
91 fn init(
93 persy: &persy::Persy,
94 segment_name: &str,
95 index_name: &str,
96 ) -> Result<(), Box<dyn std::error::Error>> {
97 let mut tx = persy.begin()?;
98
99 if !tx.exists_segment(segment_name)? {
100 tx.create_segment(segment_name)?;
101 }
102 if !tx.exists_index(index_name)? {
103 tx.create_index::<String, persy::PersyId>(index_name, persy::ValueMode::Replace)?;
104 }
105
106 let prepared = tx.prepare()?;
107 prepared.commit()?;
108
109 Ok(())
110 }
111
112 Ok(PersyBackend::new(PersyCore {
113 datafile: datafile_path,
114 segment,
115 index,
116 persy,
117 }))
118 }
119}
120
121#[derive(Clone, Debug)]
123pub struct PersyBackend {
124 pub(crate) core: Arc<PersyCore>,
125 pub(crate) root: String,
126 pub(crate) info: ServiceInfo,
127 pub(crate) capability: Capability,
128}
129
130impl PersyBackend {
131 pub fn new(core: PersyCore) -> Self {
132 let info = ServiceInfo::new(PERSY_SCHEME, "/", &core.datafile);
133 let capability = Capability {
134 read: true,
135 stat: true,
136 write: true,
137 write_can_empty: true,
138 delete: true,
139 ..Default::default()
140 };
141
142 Self {
143 core: Arc::new(core),
144 root: "/".to_string(),
145 info,
146 capability,
147 }
148 }
149}
150
151impl Service for PersyBackend {
152 type Reader = oio::StreamReader<PersyReader>;
153 type Writer = PersyWriter;
154 type Lister = ();
155 type Deleter = oio::OneShotDeleter<PersyDeleter>;
156 type Copier = ();
157
158 fn info(&self) -> ServiceInfo {
159 self.info.clone()
160 }
161
162 fn capability(&self) -> Capability {
163 self.capability
164 }
165
166 async fn create_dir(
167 &self,
168 _ctx: &OperationContext,
169 _path: &str,
170 _args: OpCreateDir,
171 ) -> Result<RpCreateDir> {
172 Err(Error::new(
173 ErrorKind::Unsupported,
174 "operation is not supported",
175 ))
176 }
177
178 async fn stat(&self, _ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
179 let p = build_abs_path(&self.root, path);
180
181 if p == build_abs_path(&self.root, "") {
182 Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
183 } else {
184 let bs = self.core.get(&p)?;
185 match bs {
186 Some(bs) => Ok(RpStat::new(
187 Metadata::new(EntryMode::FILE).with_content_length(bs.len() as u64),
188 )),
189 None => Err(Error::new(ErrorKind::NotFound, "kv not found in persy")),
190 }
191 }
192 }
193 fn read(&self, _ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
194 let output: oio::StreamReader<PersyReader> = {
195 Ok(oio::StreamReader::new(PersyReader::new(
196 self.clone(),
197 path,
198 args,
199 )))
200 }?;
201
202 Ok(output)
203 }
204
205 fn write(&self, _ctx: &OperationContext, path: &str, _: OpWrite) -> Result<Self::Writer> {
206 let output: PersyWriter = {
207 let p = build_abs_path(&self.root, path);
208 Ok(PersyWriter::new(self.core.clone(), p))
209 }?;
210
211 Ok(output)
212 }
213
214 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
215 let output: oio::OneShotDeleter<PersyDeleter> = {
216 Ok(oio::OneShotDeleter::new(PersyDeleter::new(
217 self.core.clone(),
218 self.root.clone(),
219 )))
220 }?;
221
222 Ok(output)
223 }
224
225 fn list(&self, _ctx: &OperationContext, _path: &str, _args: OpList) -> Result<Self::Lister> {
226 Err(Error::new(
227 ErrorKind::Unsupported,
228 "operation is not supported",
229 ))
230 }
231
232 fn copy(
233 &self,
234 _ctx: &OperationContext,
235 _from: &str,
236 _to: &str,
237 _args: OpCopy,
238 _opts: OpCopier,
239 ) -> Result<Self::Copier> {
240 Err(Error::new(
241 ErrorKind::Unsupported,
242 "operation is not supported",
243 ))
244 }
245
246 async fn rename(
247 &self,
248 _ctx: &OperationContext,
249 _from: &str,
250 _to: &str,
251 _args: OpRename,
252 ) -> Result<RpRename> {
253 Err(Error::new(
254 ErrorKind::Unsupported,
255 "operation is not supported",
256 ))
257 }
258
259 async fn presign(
260 &self,
261 _ctx: &OperationContext,
262 _path: &str,
263 _args: OpPresign,
264 ) -> Result<RpPresign> {
265 Err(Error::new(
266 ErrorKind::Unsupported,
267 "operation is not supported",
268 ))
269 }
270}