opendal_service_ipmfs/
backend.rs1use std::sync::Arc;
19
20use bytes::Buf;
21use http::StatusCode;
22use serde::Deserialize;
23
24use super::core::IpmfsCore;
25use super::core::parse_error;
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
170 fn info(&self) -> ServiceInfo {
171 self.core.info.clone()
172 }
173
174 fn capability(&self) -> Capability {
175 self.core.capability
176 }
177
178 async fn create_dir(
179 &self,
180 ctx: &OperationContext,
181 path: &str,
182 _: OpCreateDir,
183 ) -> Result<RpCreateDir> {
184 let resp = self.core.ipmfs_mkdir(ctx, path).await?;
185
186 let status = resp.status();
187
188 match status {
189 StatusCode::CREATED | StatusCode::OK => Ok(RpCreateDir::default()),
190 _ => Err(parse_error(resp)),
191 }
192 }
193
194 async fn stat(&self, ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
195 if path == "/" {
197 return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
198 }
199
200 let resp = self.core.ipmfs_stat(ctx, path).await?;
201
202 let status = resp.status();
203
204 match status {
205 StatusCode::OK => {
206 let bs = resp.into_body();
207
208 let res: IpfsStatResponse =
209 serde_json::from_reader(bs.reader()).map_err(new_json_deserialize_error)?;
210
211 let mode = match res.file_type.as_str() {
212 "file" => EntryMode::FILE,
213 "directory" => EntryMode::DIR,
214 _ => EntryMode::Unknown,
215 };
216
217 let mut meta = Metadata::new(mode);
218 meta.set_content_length(res.size);
219
220 Ok(RpStat::new(meta))
221 }
222 _ => Err(parse_error(resp)),
223 }
224 }
225 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
226 let output: oio::StreamReader<IpmfsReader> = {
227 Ok(oio::StreamReader::new(IpmfsReader::new(
228 self.clone(),
229 ctx.clone(),
230 path,
231 args,
232 )))
233 }?;
234
235 Ok(output)
236 }
237
238 fn write(&self, ctx: &OperationContext, path: &str, _: OpWrite) -> Result<Self::Writer> {
239 let output: oio::OneShotWriter<IpmfsWriter> = {
240 Ok(oio::OneShotWriter::new(IpmfsWriter::new(
241 self.core.clone(),
242 ctx.clone(),
243 path.to_string(),
244 )))
245 }?;
246
247 Ok(output)
248 }
249
250 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
251 let output: oio::OneShotDeleter<IpmfsDeleter> = {
252 Ok(oio::OneShotDeleter::new(IpmfsDeleter::new(
253 self.core.clone(),
254 ctx.clone(),
255 )))
256 }?;
257
258 Ok(output)
259 }
260
261 fn list(&self, ctx: &OperationContext, path: &str, _: OpList) -> Result<Self::Lister> {
262 let output: oio::PageLister<IpmfsLister> = {
263 let l = IpmfsLister::new(self.core.clone(), ctx.clone(), &self.core.root, path);
264 Ok(oio::PageLister::new(l))
265 }?;
266
267 Ok(output)
268 }
269
270 fn copy(
271 &self,
272 _ctx: &OperationContext,
273 _from: &str,
274 _to: &str,
275 _args: OpCopy,
276 _opts: OpCopier,
277 ) -> Result<Self::Copier> {
278 Err(Error::new(
279 ErrorKind::Unsupported,
280 "operation is not supported",
281 ))
282 }
283
284 async fn rename(
285 &self,
286 _ctx: &OperationContext,
287 _from: &str,
288 _to: &str,
289 _args: OpRename,
290 ) -> Result<RpRename> {
291 Err(Error::new(
292 ErrorKind::Unsupported,
293 "operation is not supported",
294 ))
295 }
296
297 async fn presign(
298 &self,
299 _ctx: &OperationContext,
300 _path: &str,
301 _args: OpPresign,
302 ) -> Result<RpPresign> {
303 Err(Error::new(
304 ErrorKind::Unsupported,
305 "operation is not supported",
306 ))
307 }
308}
309
310#[derive(Deserialize, Default, Debug)]
311#[serde(default)]
312struct IpfsStatResponse {
313 #[serde(rename = "Size")]
314 size: u64,
315 #[serde(rename = "Type")]
316 file_type: String,
317}