diff --git a/unixfs/src/dir.rs b/unixfs/src/dir.rs index f607add2..622cb98f 100644 --- a/unixfs/src/dir.rs +++ b/unixfs/src/dir.rs @@ -1,5 +1,5 @@ -use crate::file::UnwrapBorrowedExt; use crate::pb::{FlatUnixFs, PBLink, PBNode, UnixFsReadFailed, UnixFsType}; +use crate::InvalidCidInLink; use cid::Cid; use std::borrow::Cow; use std::collections::VecDeque; @@ -74,22 +74,18 @@ pub fn resolve<'needle>( } }; - let mut matching = links.into_iter().enumerate().filter_map(|(i, link)| { - match link.Name.as_deref().unwrap_or_default() { - x if x == needle => Some((i, Cow::Borrowed(link.Hash.unwrap_borrowed_or_empty()))), + let mut matching = links.into_iter().enumerate() + .filter_map(|(i, link)| match link.Name.as_deref().unwrap_or_default() { + x if x == needle => Some((i, link)), _ => None, - } - }); + }); let first = matching.next(); if let Some((i, first)) = first { - let first = try_convert_cid(i, first.as_ref())?; + let first = try_convert_cid(i, first)?; match matching.next() { - Some((j, Cow::Borrowed(second))) => { - Err(MultipleMatchingLinks::from(((i, first), (j, second))).into()) - } - Some((_, Cow::Owned(_))) => unreachable!("never taken ownership of"), + Some((j, second)) => Err(MultipleMatchingLinks::from(((i, first), (j, second))).into()), None => Ok(MaybeResolved::Found(first)), } } else { @@ -97,13 +93,9 @@ pub fn resolve<'needle>( } } -fn try_convert_cid(nth: usize, hash: &[u8]) -> Result { - Cid::try_from(hash).map_err(|e| InvalidCidInLink { - nth, - raw: hash.to_vec(), - source: e, - hidden: (), - }) +fn try_convert_cid(nth: usize, link: PBLink<'_>) -> Result { + let hash = link.Hash.as_deref().unwrap_or_default(); + Cid::try_from(hash).map_err(|e| InvalidCidInLink::from((nth, link, e))) } pub enum MaybeResolved<'needle> { @@ -238,24 +230,21 @@ impl<'needle> ShardedLookup<'needle> { pub(crate) fn partition<'a>( iter: impl Iterator>, needle: &str, - work: &mut VecDeque, - ) -> Result, PartitioningError> { + work: &mut VecDeque) -> Result, PartitioningError> { let mut found = None; for (i, link) in iter.enumerate() { let name = link.Name.as_deref().unwrap_or_default(); if name.len() > 2 && &name[2..] == needle { - let hash = link.Hash.as_deref().unwrap_or_default(); - if let Some(first) = found.take() { - return Err(MultipleMatchingLinks::from((first, (i, hash))).into()); + return Err(MultipleMatchingLinks::from((first, (i, link))).into()); } else { - found = Some((i, try_convert_cid(i, hash)?)); + found = Some((i, try_convert_cid(i, link)?)); } } else if name.len() == 2 { // the magic number of two comes from the fanout (256) probably - let cid = try_convert_cid(i, link.Hash.unwrap_borrowed_or_empty())?; + let cid = try_convert_cid(i, link)?; work.push_back(cid); } else { // no match, not interesting for us @@ -381,16 +370,6 @@ impl fmt::Display for ShardError { impl std::error::Error for ShardError {} -/// A link could not be transformed into a Cid. -#[derive(Debug)] -pub struct InvalidCidInLink { - pub nth: usize, - pub raw: Vec, - pub source: cid::Error, - /// This is to deny creating these outside of the crate - hidden: (), -} - /// Multiple matching links were found: **at least two**. #[derive(Debug)] pub enum MultipleMatchingLinks { @@ -406,8 +385,8 @@ pub enum MultipleMatchingLinks { }, } -impl<'a> From<((usize, Cid), (usize, &'a [u8]))> for MultipleMatchingLinks { - fn from(((i, first), (j, second)): ((usize, Cid), (usize, &'a [u8]))) -> MultipleMatchingLinks { +impl<'a> From<((usize, Cid), (usize, PBLink<'a>))> for MultipleMatchingLinks { + fn from(((i, first), (j, second)): ((usize, Cid), (usize, PBLink<'a>))) -> MultipleMatchingLinks { match try_convert_cid(j, second) { Ok(second) => MultipleMatchingLinks::Two { first: (i, first), diff --git a/unixfs/src/file.rs b/unixfs/src/file.rs index bbd807b6..9196a328 100644 --- a/unixfs/src/file.rs +++ b/unixfs/src/file.rs @@ -3,6 +3,7 @@ ///! Most usable for walking UnixFS file trees provided by the `visit::IdleFileVisit` and ///! `visit::FileVisit` types. use crate::pb::{UnixFs, UnixFsReadFailed, UnixFsType}; +use crate::InvalidCidInLink; use std::borrow::Cow; use std::fmt; @@ -66,20 +67,9 @@ pub enum FileReadFailed { // This is the raw value instead of the enum by design not to expose the quick-protobuf types UnexpectedType(i32), /// Parsing failed - Read(quick_protobuf::Error), - /// Outer dag-pb node was parsed successfully but there was no data bytes for inner message. - EmptyPBNode, + Read(Option), /// Link could not be turned into Cid. - LinkInvalidCid { - /// The index of this link, from zero - nth: usize, - /// Hash which could not be turned into a Cid - hash: Vec, - /// Name of the link, most likely empty - name: Cow<'static, str>, - /// Error from the attempted conversion - cause: cid::Error, - }, + InvalidCid(InvalidCidInLink), } impl fmt::Display for FileReadFailed { @@ -94,15 +84,9 @@ impl fmt::Display for FileReadFailed { t, UnixFsType::from(*t) ), - Read(e) => write!(fmt, "reading failed: {}", e), - EmptyPBNode => write!(fmt, "reading failed: missing UnixFS message"), - LinkInvalidCid { - nth, name, cause, .. - } => write!( - fmt, - "failed to convert link #{} ({:?}) to Cid: {}", - nth, name, cause - ), + Read(Some(e)) => write!(fmt, "reading failed: {}", e), + Read(None) => write!(fmt, "reading failed: missing UnixFS message"), + InvalidCid(e) => write!(fmt, "{}", e), } } } @@ -111,7 +95,8 @@ impl std::error::Error for FileReadFailed { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { use FileReadFailed::*; match self { - LinkInvalidCid { cause, .. } => Some(cause), + InvalidCid(e) => Some(e), + Read(Some(e)) => Some(e), _ => None, } } @@ -121,9 +106,9 @@ impl From for FileReadFailed { fn from(e: UnixFsReadFailed) -> Self { use UnixFsReadFailed::*; match e { - InvalidDagPb(e) => FileReadFailed::Read(e), - InvalidUnixFs(e) => FileReadFailed::Read(e), - NoData => FileReadFailed::EmptyPBNode, + InvalidDagPb(e) => FileReadFailed::Read(Some(e)), + InvalidUnixFs(e) => FileReadFailed::Read(Some(e)), + NoData => FileReadFailed::Read(None), } } } diff --git a/unixfs/src/file/visit.rs b/unixfs/src/file/visit.rs index 3cf0cd5e..7cd9f7a3 100644 --- a/unixfs/src/file/visit.rs +++ b/unixfs/src/file/visit.rs @@ -1,11 +1,11 @@ use cid::Cid; -use std::borrow::Cow; use std::convert::TryFrom; use std::ops::Range; use crate::file::reader::{FileContent, FileReader, Traversal}; -use crate::file::{FileMetadata, FileReadFailed, UnwrapBorrowedExt}; +use crate::file::{FileMetadata, FileReadFailed}; use crate::pb::merkledag::PBLink; +use crate::InvalidCidInLink; /// IdleFileVisit represents a prepared file visit over a tree. The user has to know the CID and be /// able to get the block for the visit. @@ -162,20 +162,11 @@ fn to_pending( link: PBLink<'_>, range: Range, ) -> Result<(Cid, Range), FileReadFailed> { - let hash = link.Hash.unwrap_borrowed(); + let hash = link.Hash.as_deref().unwrap_or_default(); match Cid::try_from(hash) { Ok(cid) => Ok((cid, range)), - Err(e) => Err(FileReadFailed::LinkInvalidCid { - nth, - hash: hash.to_vec(), - name: match link.Name { - Some(Cow::Borrowed(x)) => Cow::Owned(String::from(x)), - Some(Cow::Owned(x)) => Cow::Owned(x), - None => Cow::Borrowed(""), - }, - cause: e, - }), + Err(e) => Err(FileReadFailed::InvalidCid(InvalidCidInLink::from((nth, link, e)))), } } diff --git a/unixfs/src/lib.rs b/unixfs/src/lib.rs index 0a0d9a61..d4e3f503 100644 --- a/unixfs/src/lib.rs +++ b/unixfs/src/lib.rs @@ -1,7 +1,64 @@ -#[warn(rust_2018_idioms, missing_docs)] +#![warn(rust_2018_idioms, missing_docs)] +//! ipfs-unixfs + +use std::borrow::Cow; +use std::fmt; + /// UnixFS file support. pub mod file; /// UnixFS directory support. pub mod dir; mod pb; + +/// A link could not be transformed into a Cid. +#[derive(Debug)] +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: cid::Error, + /// This is to deny creating these outside of the crate + hidden: (), +} + +impl<'a> From<(usize, pb::PBLink<'a>, cid::Error)> for InvalidCidInLink { + fn from((nth, link, source): (usize, pb::PBLink<'a>, 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, + hidden: (), + } + } +} + +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) + } +}