1use std::fmt::Debug;
19use std::sync::Arc;
20
21use asyncband::once::OnceCell;
22use log::debug;
23
24use super::SEAFILE_SCHEME;
25use super::config::SeafileConfig;
26use super::core::SeafileCore;
27use super::core::parse_dir_detail;
28use super::core::parse_file_detail;
29use super::deleter::SeafileDeleter;
30use super::lister::SeafileLister;
31use super::reader::*;
32use super::writer::SeafileWriter;
33use super::writer::SeafileWriters;
34use opendal_core::raw::*;
35use opendal_core::*;
36
37#[doc = include_str!("docs.md")]
39#[derive(Default)]
40pub struct SeafileBuilder {
41 pub(super) config: SeafileConfig,
42}
43
44impl Debug for SeafileBuilder {
45 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
46 f.debug_struct("SeafileBuilder")
47 .field("config", &self.config)
48 .finish_non_exhaustive()
49 }
50}
51
52impl SeafileBuilder {
53 pub fn root(mut self, root: &str) -> Self {
57 self.config.root = if root.is_empty() {
58 None
59 } else {
60 Some(root.to_string())
61 };
62
63 self
64 }
65
66 pub fn endpoint(mut self, endpoint: &str) -> Self {
70 self.config.endpoint = if endpoint.is_empty() {
71 None
72 } else {
73 Some(endpoint.to_string())
74 };
75
76 self
77 }
78
79 pub fn username(mut self, username: &str) -> Self {
83 self.config.username = if username.is_empty() {
84 None
85 } else {
86 Some(username.to_string())
87 };
88
89 self
90 }
91
92 pub fn password(mut self, password: &str) -> Self {
96 self.config.password = if password.is_empty() {
97 None
98 } else {
99 Some(password.to_string())
100 };
101
102 self
103 }
104
105 pub fn repo_name(mut self, repo_name: &str) -> Self {
109 self.config.repo_name = repo_name.to_string();
110
111 self
112 }
113}
114
115impl Builder for SeafileBuilder {
116 type Config = SeafileConfig;
117
118 fn build(self) -> Result<impl Service> {
120 debug!("backend build started: {:?}", self);
121
122 let root = normalize_root(&self.config.root.clone().unwrap_or_default());
123 debug!("backend use root {}", root);
124
125 if self.config.repo_name.is_empty() {
127 return Err(Error::new(ErrorKind::ConfigInvalid, "repo_name is empty")
128 .with_operation("Builder::build")
129 .with_context("service", SEAFILE_SCHEME));
130 }
131
132 debug!("backend use repo_name {}", self.config.repo_name);
133
134 let endpoint = match &self.config.endpoint {
135 Some(endpoint) => Ok(endpoint.clone()),
136 None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty")
137 .with_operation("Builder::build")
138 .with_context("service", SEAFILE_SCHEME)),
139 }?;
140
141 let username = match &self.config.username {
142 Some(username) => Ok(username.clone()),
143 None => Err(Error::new(ErrorKind::ConfigInvalid, "username is empty")
144 .with_operation("Builder::build")
145 .with_context("service", SEAFILE_SCHEME)),
146 }?;
147
148 let password = match &self.config.password {
149 Some(password) => Ok(password.clone()),
150 None => Err(Error::new(ErrorKind::ConfigInvalid, "password is empty")
151 .with_operation("Builder::build")
152 .with_context("service", SEAFILE_SCHEME)),
153 }?;
154
155 Ok(SeafileBackend {
156 core: Arc::new(SeafileCore {
157 info: ServiceInfo::new(SEAFILE_SCHEME, &root, ""),
158 capability: Capability {
159 create_dir: true,
160 stat: true,
161
162 read: true,
163
164 write: true,
165 write_can_empty: true,
166
167 delete: true,
168
169 list: true,
170
171 shared: true,
172
173 ..Default::default()
174 },
175 root,
176 endpoint,
177 username,
178 password,
179 repo_name: self.config.repo_name.clone(),
180 auth_info: Arc::new(OnceCell::new()),
181 }),
182 })
183 }
184}
185
186#[derive(Debug, Clone)]
188pub struct SeafileBackend {
189 pub(crate) core: Arc<SeafileCore>,
190}
191
192impl Service for SeafileBackend {
193 type Reader = oio::StreamReader<SeafileReader>;
194 type Writer = SeafileWriters;
195 type Lister = oio::PageLister<SeafileLister>;
196 type Deleter = oio::OneShotDeleter<SeafileDeleter>;
197 type Copier = ();
198 type Composer = ();
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(MetadataBuilder::dir().build()));
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 ) -> Result<Self::Copier> {
285 Err(Error::new(
286 ErrorKind::Unsupported,
287 "operation is not supported",
288 ))
289 }
290
291 async fn rename(
292 &self,
293 _ctx: &OperationContext,
294 _from: &str,
295 _to: &str,
296 _args: OpRename,
297 ) -> Result<RpRename> {
298 Err(Error::new(
299 ErrorKind::Unsupported,
300 "operation is not supported",
301 ))
302 }
303
304 async fn presign(
305 &self,
306 _ctx: &OperationContext,
307 _path: &str,
308 _args: OpPresign,
309 ) -> Result<RpPresign> {
310 Err(Error::new(
311 ErrorKind::Unsupported,
312 "operation is not supported",
313 ))
314 }
315}