unixfs_v1/
lib.rs

1#![warn(rust_2018_idioms)]
2//! ipfs-unixfs: UnixFs tree support in Rust.
3//!
4//! The crate aims to provide a blockstore implementation independent of the UnixFs implementation by
5//! working on slices and not doing any IO operations.
6//!
7//! The main entry point for extracting information and/or data out of UnixFs trees is
8//! `ipfs_unixfs::walk::Walker`. To resolve `IpfsPath` segments over dag-pb nodes,
9//! `ipfs_unixfs::resolve` should be used.
10
11extern crate alloc;
12
13use alloc::borrow::Cow;
14use core::fmt;
15
16/// File support.
17pub mod file;
18
19/// Symlink creation support
20pub mod symlink;
21
22/// Directory and directory tree support
23pub mod dir;
24pub use dir::{resolve, LookupError, MaybeResolved, ResolveError};
25
26mod pb;
27pub use pb::{FlatUnixFs, PBLink, PBNode, UnixFs, UnixFsType};
28
29/// Support operations for the dag-pb, the outer shell of UnixFS
30pub mod dagpb;
31
32/// Support for walking over all UnixFs trees
33pub mod walk;
34
35#[cfg(test)]
36pub(crate) mod test_support;
37
38/// A link could not be transformed into a Cid.
39#[derive(Debug)]
40pub struct InvalidCidInLink {
41    /// The index of this link, from zero
42    pub nth: usize,
43    /// Hash which could not be turned into a `Cid`
44    pub hash: Cow<'static, [u8]>,
45    /// Name of the link, most likely empty when this originates from a file, most likely non-empty
46    /// for other kinds.
47    pub name: Cow<'static, str>,
48    /// Error from the attempted conversion
49    pub source: libipld::cid::Error,
50    /// This is to deny creating these outside of the crate
51    #[allow(dead_code)]
52    hidden: (),
53}
54
55impl<'a> From<(usize, pb::PBLink<'a>, libipld::cid::Error)> for InvalidCidInLink {
56    fn from((nth, link, source): (usize, pb::PBLink<'a>, libipld::cid::Error)) -> Self {
57        let hash = match link.Hash {
58            Some(Cow::Borrowed(x)) if !x.is_empty() => Cow::Owned(x.to_vec()),
59            Some(Cow::Borrowed(_)) | None => Cow::Borrowed(&[][..]),
60            Some(Cow::Owned(x)) => Cow::Owned(x),
61        };
62
63        let name = match link.Name {
64            Some(Cow::Borrowed(x)) if !x.is_empty() => Cow::Owned(x.to_string()),
65            Some(Cow::Borrowed(_)) | None => Cow::Borrowed(""),
66            Some(Cow::Owned(x)) => Cow::Owned(x),
67        };
68
69        InvalidCidInLink {
70            nth,
71            hash,
72            name,
73            source,
74            hidden: (),
75        }
76    }
77}
78
79impl fmt::Display for InvalidCidInLink {
80    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
81        write!(
82            fmt,
83            "failed to convert link #{} ({:?}) to Cid: {}",
84            self.nth, self.name, self.source
85        )
86    }
87}
88
89impl std::error::Error for InvalidCidInLink {
90    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
91        Some(&self.source)
92    }
93}
94
95/// Wrapper around the unexpected UnixFs node type, allowing access to querying what is known about
96/// the type.
97pub struct UnexpectedNodeType(i32);
98
99impl fmt::Debug for UnexpectedNodeType {
100    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
101        let converted = UnixFsType::from(self.0);
102        // the conversion defaults to Raw
103        if converted == UnixFsType::Raw && self.0 != 0 {
104            write!(fmt, "{} or <unknown>", self.0)
105        } else {
106            write!(fmt, "{} or {:?}", self.0, converted)
107        }
108    }
109}
110
111impl From<UnixFsType> for UnexpectedNodeType {
112    fn from(t: UnixFsType) -> UnexpectedNodeType {
113        UnexpectedNodeType(t.into())
114    }
115}
116
117impl UnexpectedNodeType {
118    /// Returns `true` if the type represents some directory
119    pub fn is_directory(&self) -> bool {
120        matches!(
121            UnixFsType::from(self.0),
122            UnixFsType::Directory | UnixFsType::HAMTShard
123        )
124    }
125
126    /// Returns `true` if the type represents a `File`
127    pub fn is_file(&self) -> bool {
128        matches!(UnixFsType::from(self.0), UnixFsType::File)
129    }
130}
131
132/// A container for the UnixFs metadata, which can be present at the root of the file, directory, or symlink trees.
133#[derive(Debug, Default, PartialEq, Eq, Clone)]
134pub struct Metadata {
135    mode: Option<u32>,
136    mtime: Option<(i64, u32)>,
137}
138
139impl Metadata {
140    /// Returns the full file mode, if one has been specified.
141    ///
142    /// The full file mode is originally read through `st_mode` field of `stat` struct defined in
143    /// `sys/stat.h` and its defining OpenGroup standard. The lowest 3 bytes correspond to read,
144    /// write, and execute rights per user, group, and other, while the 4th byte determines sticky bits,
145    /// set user id or set group id. The following two bytes correspond to the different file types, as
146    /// defined by the same OpenGroup standard:
147    /// https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_stat.h.html
148    pub fn mode(&self) -> Option<u32> {
149        self.mode
150    }
151
152    /// Returns the raw timestamp of last modification time, if specified.
153    ///
154    /// The timestamp is `(seconds, nanos)` - similar to `core::time::Duration`, with the exception of
155    /// allowing seconds to be negative. The seconds are calculated from `1970-01-01 00:00:00` or
156    /// the common "unix epoch".
157    pub fn mtime(&self) -> Option<(i64, u32)> {
158        self.mtime
159    }
160
161    /// Returns the mtime metadata as a `FileTime`. Enabled only in the `filetime` feature.
162    #[cfg(feature = "filetime")]
163    pub fn mtime_as_filetime(&self) -> Option<filetime::FileTime> {
164        self.mtime()
165            .map(|(seconds, nanos)| filetime::FileTime::from_unix_time(seconds, nanos))
166    }
167}
168
169impl<'a> From<&'a UnixFs<'_>> for Metadata {
170    fn from(data: &'a UnixFs<'_>) -> Self {
171        let mode = data.mode;
172        let mtime = data
173            .mtime
174            .clone()
175            .map(|ut| (ut.Seconds, ut.FractionalNanoseconds.unwrap_or(0)));
176
177        Metadata { mode, mtime }
178    }
179}