use core::convert::TryFrom;
use libipld::{cid, multibase, Cid};
use std::path::PathBuf;
pub fn block_path(mut base: PathBuf, cid: &Cid) -> PathBuf {
let key = if cid.version() == cid::Version::V1 {
cid.to_string()
} else {
Cid::new_v1(cid.codec(), cid.hash().to_owned()).to_string()
};
shard(&mut base, &key);
base.set_extension("data");
base
}
pub fn filestem_to_block_cid(file_stem: Option<&std::ffi::OsStr>) -> Option<Cid> {
file_stem.and_then(|stem| stem.to_str()).and_then(|s| {
let cid = Cid::try_from(s);
cid.ok()
})
}
pub fn pin_path(mut base: PathBuf, cid: &Cid) -> PathBuf {
let key: String = multibase::Base::Base32Lower.encode(cid.to_bytes());
shard(&mut base, &key);
base
}
pub fn filestem_to_pin_cid(file_stem: Option<&std::ffi::OsStr>) -> Option<Cid> {
file_stem.and_then(|stem| stem.to_str()).and_then(|s| {
let bytes = multibase::Base::Base32Lower.decode(s).ok()?;
let cid = Cid::try_from(bytes);
cid.ok()
})
}
fn shard(path: &mut PathBuf, key: &str) {
let start = key.len() - 3;
let shard = &key[start..start + 2];
assert_eq!(key[start + 2..].len(), 1);
path.push(shard);
path.push(key);
}
#[cfg(test)]
mod tests {
use super::shard;
use super::Cid;
use std::convert::TryFrom;
use std::path::{Path, PathBuf};
#[test]
fn cid_v0_to_pin_and_back() {
roundtrip_pin_path(
"QmTEn8ypAkbJXZUXCRHBorwF2jM8uTUW9yRLzrcQouSoD4",
"some_root",
"some_root/2w/ciqerskzqa5kvny63dm6byuerdsbkiadrnzlerphz2zdcpdvozuh2wy",
);
}
#[test]
fn cid_v1_to_pin_and_back() {
roundtrip_pin_path(
"bafybeicizfmyaovkw4pnrwpa4kcirzaveabyw4vsixt45mrrhr2xm2d5lm",
"some_root",
"some_root/5l/afybeicizfmyaovkw4pnrwpa4kcirzaveabyw4vsixt45mrrhr2xm2d5lm",
);
}
fn roundtrip_pin_path(cid: &str, base: &str, expected_path: &str) {
let cid = Cid::try_from(cid).unwrap();
let path = super::pin_path(PathBuf::from(base), &cid);
assert_eq!(path, Path::new(expected_path));
let parsed_cid = super::filestem_to_pin_cid(path.file_stem());
assert_eq!(parsed_cid, Some(cid));
}
#[test]
fn cid_to_block_path() {
let cid_v0 = "QmTEn8ypAkbJXZUXCRHBorwF2jM8uTUW9yRLzrcQouSoD4";
let cid_v0 = Cid::try_from(cid_v0).unwrap();
let cid_v1 = "bafybeicizfmyaovkw4pnrwpa4kcirzaveabyw4vsixt45mrrhr2xm2d5lm";
let cid_v1 = Cid::try_from(cid_v1).unwrap();
let base = PathBuf::from("another_root");
let cid_v0_path = super::block_path(base.clone(), &cid_v0);
let cid_v1_path = super::block_path(base, &cid_v1);
assert_eq!(cid_v0_path, cid_v1_path);
let expected =
"another_root/5l/bafybeicizfmyaovkw4pnrwpa4kcirzaveabyw4vsixt45mrrhr2xm2d5lm.data";
assert_eq!(cid_v1_path, Path::new(expected));
}
#[test]
fn block_path_to_cid() {
let cid_v1 = "bafybeicizfmyaovkw4pnrwpa4kcirzaveabyw4vsixt45mrrhr2xm2d5lm";
let cid_v1 = Cid::try_from(cid_v1).unwrap();
let path =
"another_root/5l/bafybeicizfmyaovkw4pnrwpa4kcirzaveabyw4vsixt45mrrhr2xm2d5lm.data";
let path = Path::new(path);
let parsed = super::filestem_to_block_cid(path.file_stem());
assert_eq!(parsed, Some(cid_v1));
}
#[test]
fn invalid_block_path_is_silently_ignored() {
let block_path = Path::new("another_root/ba/foobar.data");
assert_eq!(super::filestem_to_block_cid(block_path.file_stem()), None);
}
#[test]
fn invalid_pin_path_is_silently_ignored() {
let pin_path = Path::new("another_root/ca/foocar.recursive");
assert_eq!(super::filestem_to_block_cid(pin_path.file_stem()), None);
}
#[test]
fn shard_example() {
let mut path = PathBuf::from("some_root");
let key = "ABCDEFG";
shard(&mut path, key);
let expected = Path::new("some_root/EF/ABCDEFG");
assert_eq!(path, expected);
}
}