1use std::sync::Arc;
19
20use bytes::Buf;
21use http::StatusCode;
22use serde::Deserialize;
23
24use super::core::parse_error;
25use super::core::{ErrorContext, IpmfsCore};
26use super::deleter::IpmfsDeleter;
27use super::lister::IpmfsLister;
28use super::reader::*;
29use super::writer::IpmfsWriter;
30use opendal_core::raw::*;
31use opendal_core::*;
32
33#[doc = include_str!("docs.md")]
35use std::fmt::Debug;
36
37use log::debug;
38
39use super::IPMFS_SCHEME;
40use super::config::IpmfsConfig;
41
42#[derive(Default)]
80pub struct IpmfsBuilder {
81 pub(super) config: IpmfsConfig,
82}
83
84impl Debug for IpmfsBuilder {
85 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86 f.debug_struct("IpmfsBuilder")
87 .field("config", &self.config)
88 .finish_non_exhaustive()
89 }
90}
91
92impl IpmfsBuilder {
93 pub fn root(mut self, root: &str) -> Self {
95 self.config.root = if root.is_empty() {
96 None
97 } else {
98 Some(root.to_string())
99 };
100
101 self
102 }
103
104 pub fn endpoint(mut self, endpoint: &str) -> Self {
108 self.config.endpoint = if endpoint.is_empty() {
109 None
110 } else {
111 Some(endpoint.to_string())
112 };
113 self
114 }
115}
116
117impl Builder for IpmfsBuilder {
118 type Config = IpmfsConfig;
119
120 fn build(self) -> Result<impl Service> {
121 let root = normalize_root(&self.config.root.unwrap_or_default());
122 debug!("backend use root {root}");
123
124 let endpoint = self
125 .config
126 .endpoint
127 .clone()
128 .unwrap_or_else(|| "http://localhost:5001".to_string());
129
130 let info = ServiceInfo::new(IPMFS_SCHEME, &root, "");
131 let capability = Capability {
132 stat: true,
133
134 read: true,
135
136 write: true,
137 delete: true,
138
139 list: true,
140
141 shared: true,
142
143 ..Default::default()
144 };
145
146 let accessor_info = info;
147 let core = Arc::new(IpmfsCore {
148 info: accessor_info,
149 capability,
150 root: root.to_string(),
151 endpoint: endpoint.to_string(),
152 });
153
154 Ok(IpmfsBackend { core })
155 }
156}
157
158#[derive(Clone, Debug)]
159pub struct IpmfsBackend {
160 pub core: Arc<IpmfsCore>,
161}
162
163impl Service for IpmfsBackend {
164 type Reader = oio::StreamReader<IpmfsReader>;
165 type Writer = oio::OneShotWriter<IpmfsWriter>;
166 type Lister = oio::PageLister<IpmfsLister>;
167 type Deleter = oio::OneShotDeleter<IpmfsDeleter>;
168 type Copier = ();
169 type Composer = ();
170
171 fn info(&self) -> ServiceInfo {
172 self.core.info.clone()
173 }
174
175 fn capability(&self) -> Capability {
176 self.core.capability
177 }
178
179 async fn create_dir(
180 &self,
181 ctx: &OperationContext,
182 path: &str,
183 _: OpCreateDir,
184 ) -> Result<RpCreateDir> {
185 let resp = self.core.ipmfs_mkdir(ctx, path).await?;
186
187 let status = resp.status();
188
189 match status {
190 StatusCode::CREATED | StatusCode::OK => Ok(RpCreateDir::default()),
191 _ => Err(parse_error(
192 ErrorContext::new(ServiceOperation("FilesMkdir")),
193 resp,
194 )),
195 }
196 }
197
198 async fn stat(&self, ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
199 if path == "/" {
201 return Ok(RpStat::new(MetadataBuilder::dir().build()));
202 }
203
204 let resp = self.core.ipmfs_stat(ctx, path).await?;
205
206 let status = resp.status();
207
208 match status {
209 StatusCode::OK => {
210 let bs = resp.into_body();
211
212 let res: IpfsStatResponse =
213 serde_json::from_reader(bs.reader()).map_err(new_json_deserialize_error)?;
214
215 let mode = match res.file_type.as_str() {
216 "file" => EntryMode::FILE,
217 "directory" => EntryMode::DIR,
218 _ => EntryMode::Unknown,
219 };
220
221 let meta = match mode {
222 EntryMode::FILE => MetadataBuilder::file(res.size),
223 EntryMode::DIR => MetadataBuilder::dir(),
224 EntryMode::Unknown => MetadataBuilder::unknown(),
225 };
226
227 Ok(RpStat::new(meta.build()))
228 }
229 _ => Err(parse_error(
230 ErrorContext::new(ServiceOperation("FilesStat")),
231 resp,
232 )),
233 }
234 }
235 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
236 let output: oio::StreamReader<IpmfsReader> = {
237 Ok(oio::StreamReader::new(IpmfsReader::new(
238 self.clone(),
239 ctx.clone(),
240 path,
241 args,
242 )))
243 }?;
244
245 Ok(output)
246 }
247
248 fn write(&self, ctx: &OperationContext, path: &str, _: OpWrite) -> Result<Self::Writer> {
249 let output: oio::OneShotWriter<IpmfsWriter> = {
250 Ok(oio::OneShotWriter::new(IpmfsWriter::new(
251 self.core.clone(),
252 ctx.clone(),
253 path.to_string(),
254 )))
255 }?;
256
257 Ok(output)
258 }
259
260 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
261 let output: oio::OneShotDeleter<IpmfsDeleter> = {
262 Ok(oio::OneShotDeleter::new(IpmfsDeleter::new(
263 self.core.clone(),
264 ctx.clone(),
265 )))
266 }?;
267
268 Ok(output)
269 }
270
271 fn list(&self, ctx: &OperationContext, path: &str, _: OpList) -> Result<Self::Lister> {
272 let output: oio::PageLister<IpmfsLister> = {
273 let l = IpmfsLister::new(self.core.clone(), ctx.clone(), &self.core.root, path);
274 Ok(oio::PageLister::new(l))
275 }?;
276
277 Ok(output)
278 }
279
280 fn copy(
281 &self,
282 _ctx: &OperationContext,
283 _from: &str,
284 _to: &str,
285 _args: OpCopy,
286 ) -> Result<Self::Copier> {
287 Err(Error::new(
288 ErrorKind::Unsupported,
289 "operation is not supported",
290 ))
291 }
292
293 async fn rename(
294 &self,
295 _ctx: &OperationContext,
296 _from: &str,
297 _to: &str,
298 _args: OpRename,
299 ) -> Result<RpRename> {
300 Err(Error::new(
301 ErrorKind::Unsupported,
302 "operation is not supported",
303 ))
304 }
305
306 async fn presign(
307 &self,
308 _ctx: &OperationContext,
309 _path: &str,
310 _args: OpPresign,
311 ) -> Result<RpPresign> {
312 Err(Error::new(
313 ErrorKind::Unsupported,
314 "operation is not supported",
315 ))
316 }
317}
318
319#[derive(Deserialize, Default, Debug)]
320#[serde(default)]
321struct IpfsStatResponse {
322 #[serde(rename = "Size")]
323 size: u64,
324 #[serde(rename = "Type")]
325 file_type: String,
326}