use std::io::Cursor;
use anyhow::{anyhow, Result};
use bytes::Bytes;
use cid::Cid;
use libipld::error::{InvalidMultihash, UnsupportedMultihash};
use multihash::{Code, MultihashDigest};
use crate::{codecs::Codec, parse_links, unixfs::dag_pb};
#[derive(Debug)]
pub struct LoadedCid {
pub data: Bytes,
pub source: Source,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Source {
Bitswap,
Http(String),
Store(&'static str),
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Block {
cid: Cid,
data: Bytes,
links: Vec<Cid>,
}
impl Block {
pub fn new(cid: Cid, data: Bytes, links: Vec<Cid>) -> Self {
Self { cid, data, links }
}
pub fn cid(&self) -> &Cid {
&self.cid
}
pub fn data(&self) -> &Bytes {
&self.data
}
pub fn links(&self) -> &[Cid] {
&self.links
}
pub fn raw_data_size(&self) -> Option<u64> {
let codec = Codec::try_from(self.cid.codec()).unwrap();
match codec {
Codec::Raw => Some(self.data.len() as u64),
_ => None,
}
}
pub fn validate(&self) -> Result<()> {
let code = self.cid.hash().code();
let mh = Code::try_from(code)
.map_err(|_| UnsupportedMultihash(code))?
.digest(&self.data);
if mh.digest() != self.cid.hash().digest() {
return Err(InvalidMultihash(mh.to_bytes()).into());
}
let expected_links = parse_links(&self.cid, &self.data)?;
let mut actual_links = self.links.clone();
actual_links.sort();
actual_links.dedup();
anyhow::ensure!(expected_links == actual_links, "links do not match");
Ok(())
}
pub fn into_parts(self) -> (Cid, Bytes, Vec<Cid>) {
(self.cid, self.data, self.links)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Link {
pub cid: Cid,
pub name: Option<String>,
pub tsize: Option<u64>,
}
impl Link {
pub fn as_ref(&self) -> LinkRef<'_> {
LinkRef {
cid: self.cid,
name: self.name.as_deref(),
tsize: self.tsize,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LinkRef<'a> {
pub cid: Cid,
pub name: Option<&'a str>,
pub tsize: Option<u64>,
}
impl LinkRef<'_> {
pub fn to_owned(&self) -> Link {
Link {
cid: self.cid,
name: self.name.map(|t| t.to_string()),
tsize: self.tsize,
}
}
}
#[derive(Debug)]
pub enum Links<'a> {
Raw,
RawNode(PbLinks<'a>),
Directory(PbLinks<'a>),
File(PbLinks<'a>),
Symlink(PbLinks<'a>),
HamtShard(PbLinks<'a>),
}
#[derive(Debug)]
pub struct PbLinks<'a> {
i: usize,
outer: &'a dag_pb::PbNode,
}
impl<'a> PbLinks<'a> {
pub fn new(outer: &'a dag_pb::PbNode) -> Self {
PbLinks { i: 0, outer }
}
}
impl<'a> Iterator for Links<'a> {
type Item = Result<LinkRef<'a>>;
fn next(&mut self) -> Option<Self::Item> {
match self {
Links::Raw => None,
Links::Directory(links)
| Links::RawNode(links)
| Links::File(links)
| Links::Symlink(links)
| Links::HamtShard(links) => links.next(),
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
match self {
Links::Raw => (0, Some(0)),
Links::Directory(links)
| Links::RawNode(links)
| Links::File(links)
| Links::Symlink(links)
| Links::HamtShard(links) => links.size_hint(),
}
}
}
impl<'a> Iterator for PbLinks<'a> {
type Item = Result<LinkRef<'a>>;
fn next(&mut self) -> Option<Self::Item> {
if self.i == self.outer.links.len() {
return None;
}
let l = &self.outer.links[self.i];
self.i += 1;
let res = l
.hash
.as_ref()
.ok_or_else(|| anyhow!("missing link"))
.and_then(|c| {
Ok(LinkRef {
cid: Cid::read_bytes(Cursor::new(c))?,
name: l.name.as_deref(),
tsize: l.tsize,
})
});
Some(res)
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.outer.links.len(), Some(self.outer.links.len()))
}
}