1use std::fmt::Debug;
19use std::sync::Arc;
20
21use log::debug;
22use mea::rwlock::RwLock;
23
24use super::SEAFILE_SCHEME;
25use super::config::SeafileConfig;
26use super::core::SeafileCore;
27use super::core::SeafileSigner;
28use super::core::parse_dir_detail;
29use super::core::parse_file_detail;
30use super::deleter::SeafileDeleter;
31use super::lister::SeafileLister;
32use super::reader::*;
33use super::writer::SeafileWriter;
34use super::writer::SeafileWriters;
35use opendal_core::raw::*;
36use opendal_core::*;
37
38#[doc = include_str!("docs.md")]
40#[derive(Default)]
41pub struct SeafileBuilder {
42 pub(super) config: SeafileConfig,
43}
44
45impl Debug for SeafileBuilder {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 f.debug_struct("SeafileBuilder")
48 .field("config", &self.config)
49 .finish_non_exhaustive()
50 }
51}
52
53impl SeafileBuilder {
54 pub fn root(mut self, root: &str) -> Self {
58 self.config.root = if root.is_empty() {
59 None
60 } else {
61 Some(root.to_string())
62 };
63
64 self
65 }
66
67 pub fn endpoint(mut self, endpoint: &str) -> Self {
71 self.config.endpoint = if endpoint.is_empty() {
72 None
73 } else {
74 Some(endpoint.to_string())
75 };
76
77 self
78 }
79
80 pub fn username(mut self, username: &str) -> Self {
84 self.config.username = if username.is_empty() {
85 None
86 } else {
87 Some(username.to_string())
88 };
89
90 self
91 }
92
93 pub fn password(mut self, password: &str) -> Self {
97 self.config.password = if password.is_empty() {
98 None
99 } else {
100 Some(password.to_string())
101 };
102
103 self
104 }
105
106 pub fn repo_name(mut self, repo_name: &str) -> Self {
110 self.config.repo_name = repo_name.to_string();
111
112 self
113 }
114}
115
116impl Builder for SeafileBuilder {
117 type Config = SeafileConfig;
118
119 fn build(self) -> Result<impl Service> {
121 debug!("backend build started: {:?}", self);
122
123 let root = normalize_root(&self.config.root.clone().unwrap_or_default());
124 debug!("backend use root {}", root);
125
126 if self.config.repo_name.is_empty() {
128 return Err(Error::new(ErrorKind::ConfigInvalid, "repo_name is empty")
129 .with_operation("Builder::build")
130 .with_context("service", SEAFILE_SCHEME));
131 }
132
133 debug!("backend use repo_name {}", self.config.repo_name);
134
135 let endpoint = match &self.config.endpoint {
136 Some(endpoint) => Ok(endpoint.clone()),
137 None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty")
138 .with_operation("Builder::build")
139 .with_context("service", SEAFILE_SCHEME)),
140 }?;
141
142 let username = match &self.config.username {
143 Some(username) => Ok(username.clone()),
144 None => Err(Error::new(ErrorKind::ConfigInvalid, "username is empty")
145 .with_operation("Builder::build")
146 .with_context("service", SEAFILE_SCHEME)),
147 }?;
148
149 let password = match &self.config.password {
150 Some(password) => Ok(password.clone()),
151 None => Err(Error::new(ErrorKind::ConfigInvalid, "password is empty")
152 .with_operation("Builder::build")
153 .with_context("service", SEAFILE_SCHEME)),
154 }?;
155
156 Ok(SeafileBackend {
157 core: Arc::new(SeafileCore {
158 info: ServiceInfo::new(SEAFILE_SCHEME, &root, ""),
159 capability: Capability {
160 create_dir: true,
161 stat: true,
162
163 read: true,
164
165 write: true,
166 write_can_empty: true,
167
168 delete: true,
169
170 list: true,
171
172 shared: true,
173
174 ..Default::default()
175 },
176 root,
177 endpoint,
178 username,
179 password,
180 repo_name: self.config.repo_name.clone(),
181 signer: Arc::new(RwLock::new(SeafileSigner::default())),
182 }),
183 })
184 }
185}
186
187#[derive(Debug, Clone)]
189pub struct SeafileBackend {
190 pub(crate) core: Arc<SeafileCore>,
191}
192
193impl Service for SeafileBackend {
194 type Reader = oio::StreamReader<SeafileReader>;
195 type Writer = SeafileWriters;
196 type Lister = oio::PageLister<SeafileLister>;
197 type Deleter = oio::OneShotDeleter<SeafileDeleter>;
198 type Copier = ();
199
200 fn info(&self) -> ServiceInfo {
201 self.core.info.clone()
202 }
203
204 fn capability(&self) -> Capability {
205 self.core.capability
206 }
207
208 async fn create_dir(
209 &self,
210 ctx: &OperationContext,
211 path: &str,
212 _args: OpCreateDir,
213 ) -> Result<RpCreateDir> {
214 self.core.create_dir(ctx, path).await?;
215 Ok(RpCreateDir::default())
216 }
217
218 async fn stat(&self, ctx: &OperationContext, path: &str, _args: OpStat) -> Result<RpStat> {
219 if path == "/" {
220 return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
221 }
222
223 let metadata = if path.ends_with('/') {
224 let dir_detail = self.core.dir_detail(ctx, path).await?;
225 parse_dir_detail(dir_detail)
226 } else {
227 let file_detail = self.core.file_detail(ctx, path).await?;
228
229 parse_file_detail(file_detail)
230 };
231
232 metadata.map(RpStat::new)
233 }
234 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
235 let output: oio::StreamReader<SeafileReader> = {
236 Ok(oio::StreamReader::new(SeafileReader::new(
237 self.clone(),
238 ctx.clone(),
239 path,
240 args,
241 )))
242 }?;
243
244 Ok(output)
245 }
246
247 fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
248 let output: SeafileWriters = {
249 let w = SeafileWriter::new(self.core.clone(), ctx.clone(), args, path.to_string());
250 let w = oio::OneShotWriter::new(w);
251
252 Ok(w)
253 }?;
254
255 Ok(output)
256 }
257
258 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
259 let output: oio::OneShotDeleter<SeafileDeleter> = {
260 Ok(oio::OneShotDeleter::new(SeafileDeleter::new(
261 self.core.clone(),
262 ctx.clone(),
263 )))
264 }?;
265
266 Ok(output)
267 }
268
269 fn list(&self, ctx: &OperationContext, path: &str, _args: OpList) -> Result<Self::Lister> {
270 let output: oio::PageLister<SeafileLister> = {
271 let l = SeafileLister::new(self.core.clone(), ctx.clone(), path);
272 Ok(oio::PageLister::new(l))
273 }?;
274
275 Ok(output)
276 }
277
278 fn copy(
279 &self,
280 _ctx: &OperationContext,
281 _from: &str,
282 _to: &str,
283 _args: OpCopy,
284 _opts: OpCopier,
285 ) -> Result<Self::Copier> {
286 Err(Error::new(
287 ErrorKind::Unsupported,
288 "operation is not supported",
289 ))
290 }
291
292 async fn rename(
293 &self,
294 _ctx: &OperationContext,
295 _from: &str,
296 _to: &str,
297 _args: OpRename,
298 ) -> Result<RpRename> {
299 Err(Error::new(
300 ErrorKind::Unsupported,
301 "operation is not supported",
302 ))
303 }
304
305 async fn presign(
306 &self,
307 _ctx: &OperationContext,
308 _path: &str,
309 _args: OpPresign,
310 ) -> Result<RpPresign> {
311 Err(Error::new(
312 ErrorKind::Unsupported,
313 "operation is not supported",
314 ))
315 }
316}