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