#![feature(iter_next_chunk)]
#![warn(missing_docs)]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod algorithm;
mod checksum;
mod generate;
mod manifest;
mod metadata;
mod payload;
mod read;
pub mod error {
pub use crate::checksum::ChecksumComputeError;
pub use crate::generate::GenerateError;
pub use crate::payload::PayloadError;
pub use crate::read::ReadError;
}
pub use algorithm::{Algorithm, ChecksumAlgorithm};
pub use checksum::Checksum;
use metadata::Metadata;
pub use payload::Payload;
#[derive(Debug, PartialEq)]
pub struct BagIt<'a, 'algo> {
path: std::path::PathBuf,
items: Vec<Payload<'a>>,
checksum_algorithm: &'algo Algorithm,
tags: Vec<Metadata>,
}
impl<'a, 'algo> BagIt<'a, 'algo> {
#[cfg(test)]
pub(crate) fn from_existing_items(
directory: impl AsRef<std::path::Path>,
items: Vec<Payload<'a>>,
checksum_algorithm: &'algo Algorithm,
tags: Vec<Metadata>,
) -> Result<Self, error::ReadError> {
Ok(Self {
path: directory.as_ref().to_path_buf(),
items,
checksum_algorithm,
tags,
})
}
pub fn path(&self) -> &std::path::Path {
&self.path
}
pub fn payload_items(&self) -> impl Iterator<Item = &Payload> {
self.items.iter()
}
fn manifest_name(&self) -> String {
format!("manifest-{}.txt", self.checksum_algorithm)
}
fn tagmanifest_name(&self) -> String {
format!("tagmanifest-{}.txt", self.checksum_algorithm)
}
}
#[cfg(test)]
mod test {
use crate::{metadata::Metadata, Algorithm, BagIt, ChecksumAlgorithm, Payload};
use sha2::Sha256;
#[tokio::test]
async fn generate_and_read_basic_bag_sha256() {
let temp_directory = async_tempfile::TempDir::new().await.unwrap();
let temp_directory = temp_directory.to_path_buf();
let algo = ChecksumAlgorithm::<Sha256>::new(Algorithm::Sha256);
{
let mut bag = BagIt::new_empty(&temp_directory, &algo);
let mut source_directory = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
source_directory.push("tests/sample-bag/data");
for file in [
"bagit.md",
"paper_bag.jpg",
"rfc8493.txt",
"sources.csv",
"totebag.jpg",
] {
bag.add_file::<Sha256>(source_directory.join(file))
.await
.unwrap();
}
assert_eq!(bag.finalize::<Sha256>().await, Ok(()));
}
{
let bag = BagIt::read_existing(&temp_directory, &algo).await.unwrap();
let expected = BagIt::from_existing_items(
temp_directory,
vec![
Payload::test_payload(
"data/bagit.md",
"eccdbbade12ba878af8f2140cb00c914f427405a987de2670e5c3014faf59f8e",
6302,
),
Payload::test_payload(
"data/paper_bag.jpg",
"2b22a8fd0dc46cbdc7a67b6cf588a03a8dd6f8ea23ce0b02e921ca5d79930bb2",
19895,
),
Payload::test_payload(
"data/rfc8493.txt",
"4964147d2e6e16442d4a6dbfbe68178a8f33c3e791c06d68a8b33f51ad821537",
48783,
),
Payload::test_payload(
"data/sources.csv",
"0fe3bd6e7c36aa2c979f3330037b220c5ca88ed0eabf16622202dc0b33c44e72",
369,
),
Payload::test_payload(
"data/totebag.jpg",
"38ff57167d746859f6383e80eb84ec0dd84de2ab1ed126ad317e73fbf502fb31",
10417,
),
],
algo.algorithm(),
vec![Metadata::PayloadOctetStreamSummary {
octet_count: 85766,
stream_count: 5,
}],
)
.unwrap();
assert_eq!(bag, expected);
}
}
}