1use std::fmt::Debug;
19use std::sync::Arc;
20
21use http::StatusCode;
22use log::debug;
23use prost::Message;
24
25use super::reader::*;
26use crate::IPFS_SCHEME;
27use crate::config::IpfsConfig;
28use crate::core::parse_error;
29use crate::core::{ErrorContext, IpfsCore};
30use crate::ipld::PBNode;
31use opendal_core::raw::*;
32use opendal_core::*;
33
34#[doc = include_str!("docs.md")]
36#[derive(Default)]
37pub struct IpfsBuilder {
38 pub(super) config: IpfsConfig,
39}
40
41impl Debug for IpfsBuilder {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.debug_struct("IpfsBuilder")
44 .field("config", &self.config)
45 .finish()
46 }
47}
48
49impl IpfsBuilder {
50 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 {
79 if !endpoint.is_empty() {
80 self.config.endpoint = Some(endpoint.trim_end_matches('/').to_string());
82 }
83
84 self
85 }
86}
87
88impl Builder for IpfsBuilder {
89 type Config = IpfsConfig;
90
91 fn build(self) -> Result<impl Service> {
92 debug!("backend build started: {:?}", self);
93
94 let root = normalize_root(&self.config.root.unwrap_or_default());
95 if !root.starts_with("/ipfs/") && !root.starts_with("/ipns/") {
96 return Err(Error::new(
97 ErrorKind::ConfigInvalid,
98 "root must start with /ipfs/ or /ipns/",
99 )
100 .with_context("service", IPFS_SCHEME)
101 .with_context("root", &root));
102 }
103 debug!("backend use root {root}");
104
105 let endpoint = match &self.config.endpoint {
106 Some(endpoint) => Ok(endpoint.clone()),
107 None => Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty")
108 .with_context("service", IPFS_SCHEME)
109 .with_context("root", &root)),
110 }?;
111 debug!("backend use endpoint {}", endpoint);
112
113 let info = ServiceInfo::new(IPFS_SCHEME, &root, "");
114 let capability = Capability {
115 stat: true,
116
117 read: true,
118 read_with_suffix: true,
119
120 list: true,
121
122 shared: true,
123
124 ..Default::default()
125 };
126
127 let accessor_info = info;
128 let core = Arc::new(IpfsCore {
129 info: accessor_info,
130 capability,
131 root,
132 endpoint,
133 });
134
135 Ok(IpfsBackend { core })
136 }
137}
138
139#[derive(Clone, Debug)]
141pub struct IpfsBackend {
142 pub(crate) core: Arc<IpfsCore>,
143}
144
145impl Service for IpfsBackend {
146 type Reader = oio::StreamReader<IpfsReader>;
147 type Writer = ();
148 type Lister = oio::PageLister<DirStream>;
149 type Deleter = ();
150 type Copier = ();
151 type Composer = ();
152
153 fn info(&self) -> ServiceInfo {
154 self.core.info.clone()
155 }
156
157 fn capability(&self) -> Capability {
158 self.core.capability
159 }
160
161 async fn create_dir(
162 &self,
163 _ctx: &OperationContext,
164 _path: &str,
165 _args: OpCreateDir,
166 ) -> Result<RpCreateDir> {
167 Err(Error::new(
168 ErrorKind::Unsupported,
169 "operation is not supported",
170 ))
171 }
172
173 async fn stat(&self, ctx: &OperationContext, path: &str, _: OpStat) -> Result<RpStat> {
174 let metadata = self.core.ipfs_stat(ctx, path).await?;
175 Ok(RpStat::new(metadata))
176 }
177
178 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
179 let output: oio::StreamReader<IpfsReader> = {
180 Ok(oio::StreamReader::new(IpfsReader::new(
181 self.clone(),
182 ctx.clone(),
183 path,
184 args,
185 )))
186 }?;
187
188 Ok(output)
189 }
190
191 fn write(&self, _ctx: &OperationContext, _path: &str, _args: OpWrite) -> Result<Self::Writer> {
192 Err(Error::new(
193 ErrorKind::Unsupported,
194 "operation is not supported",
195 ))
196 }
197
198 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
199 Err(Error::new(
200 ErrorKind::Unsupported,
201 "operation is not supported",
202 ))
203 }
204
205 fn list(&self, ctx: &OperationContext, path: &str, _: OpList) -> Result<Self::Lister> {
206 let output: oio::PageLister<DirStream> = {
207 let l = DirStream::new(self.core.clone(), ctx.clone(), path);
208 Ok(oio::PageLister::new(l))
209 }?;
210
211 Ok(output)
212 }
213
214 fn copy(
215 &self,
216 _ctx: &OperationContext,
217 _from: &str,
218 _to: &str,
219 _args: OpCopy,
220 ) -> Result<Self::Copier> {
221 Err(Error::new(
222 ErrorKind::Unsupported,
223 "operation is not supported",
224 ))
225 }
226
227 async fn rename(
228 &self,
229 _ctx: &OperationContext,
230 _from: &str,
231 _to: &str,
232 _args: OpRename,
233 ) -> Result<RpRename> {
234 Err(Error::new(
235 ErrorKind::Unsupported,
236 "operation is not supported",
237 ))
238 }
239
240 async fn presign(
241 &self,
242 _ctx: &OperationContext,
243 _path: &str,
244 _args: OpPresign,
245 ) -> Result<RpPresign> {
246 Err(Error::new(
247 ErrorKind::Unsupported,
248 "operation is not supported",
249 ))
250 }
251}
252
253pub struct DirStream {
254 core: Arc<IpfsCore>,
255 ctx: OperationContext,
256 path: String,
257}
258
259impl DirStream {
260 fn new(core: Arc<IpfsCore>, ctx: OperationContext, path: &str) -> Self {
261 Self {
262 core,
263 ctx,
264 path: path.to_string(),
265 }
266 }
267}
268
269impl oio::PageList for DirStream {
270 async fn next_page(&self, ctx: &mut oio::PageContext) -> Result<()> {
271 let resp = self.core.ipfs_list(&self.ctx, &self.path).await?;
272
273 if resp.status() != StatusCode::OK {
274 return Err(parse_error(
275 ErrorContext::new(ServiceOperation("List")),
276 resp,
277 ));
278 }
279
280 let bs = resp.into_body();
281 let pb_node = PBNode::decode(bs).map_err(|e| {
282 Error::new(ErrorKind::Unexpected, "deserialize protobuf from response").set_source(e)
283 })?;
284
285 let names = pb_node
286 .links
287 .into_iter()
288 .map(|v| v.name.unwrap())
289 .collect::<Vec<String>>();
290
291 for name in names {
292 let child = if self.path == "/" {
293 name
294 } else {
295 format!("{}{}", self.path, name)
296 };
297
298 let meta = self.core.ipfs_stat(&self.ctx, &child).await?;
299
300 let child = if meta.mode().is_dir() {
301 format!("{child}/")
302 } else {
303 child
304 };
305
306 ctx.entries.push_back(oio::Entry::new(&child, meta))
307 }
308
309 ctx.done = true;
310 Ok(())
311 }
312}