commit 0d5bb2ccf50e0818596c23ddd4419d73875f4a48
parent 755845681c413745261faf953320c806e4e35aa7
Author: lash <dev@holbrook.no>
Date: Tue, 26 Jul 2022 18:19:22 +0000
WIP module documentation
Diffstat:
4 files changed, 108 insertions(+), 23 deletions(-)
diff --git a/src/digest.rs b/src/digest.rs
@@ -8,6 +8,7 @@ use sha2::{
use log::error;
+/// Encapsulations of supported digests for digest data.
pub enum RecordDigest {
Sha512(Vec<u8>),
Sha256(Vec<u8>),
@@ -17,6 +18,9 @@ pub enum RecordDigest {
+/// Create a [RecordDigest::Sha512](RecordDigest::Sha512) instance from the raw digest data.
+///
+/// Will fail if digest has incorrect length.
pub fn from_vec(v: Vec<u8>) -> Result<RecordDigest, ParseError> {
let sz = Sha512::output_size();
if v.len() != sz {
@@ -25,6 +29,13 @@ pub fn from_vec(v: Vec<u8>) -> Result<RecordDigest, ParseError> {
Ok(RecordDigest::Sha512(v))
}
+/// Create a [RecordDigest](RecordDigest) instance corresponding to the URN digest scheme.
+///
+/// Valid URN schemes and their corresponding enumerated values are:
+///
+/// * `sha512` -> [RecordDigest::Sha512](RecordDigest::Sha512])
+/// * `sha256` -> [RecordDigest::Sha256](RecordDigest::Sha256])
+/// * `bzz` -> [RecordDigest::SwarmHash](RecordDigest::SwarmHash])
pub fn from_urn(urn: &str) -> Result<RecordDigest, ParseError> {
let mut v = urn.split(":");
let r = match v.next() {
diff --git a/src/error.rs b/src/error.rs
@@ -1,2 +1,3 @@
+/// Used for any parsing error for any supported format.
#[derive(Debug)]
pub struct ParseError;
diff --git a/src/meta.rs b/src/meta.rs
@@ -43,114 +43,147 @@ use log::{
debug,
};
+/// Date elements as d/m/Y tuple.
pub type PublishDate = (u8, u8, u32);
+/// Alias for file name (basename).
pub type FileName = String;
+/// Alias for absolute file path.
pub type FilePath = String;
+/// Represents the full metadata for a media file.
pub struct MetaData {
+ /// The Dublin Core vocabulary parts of the metadata.
dc: DCMetaData,
+ /// The digest of the file that the metadata is keyed to.
digest: digest::RecordDigest,
+ /// Optional local filename, e.g. to use for HTTP `Content-Disposition` header, rename matching files to client's original name, etc.
local_name: Option<FileName>,
- comment: String,
+ /// Publication date of the content that the media represents.
publish_date: PublishDate,
- retrieval_timestamp: u32,
}
-pub fn check_xattr() {
+//pub fn check_xattr() {
-}
+//}
+/// Generates the native `sha512` digest of a file.
+///
+/// # Arguments
+///
+/// * `filepath` - Absolute path to file to calculate digest for.
pub fn digest_from_path(filepath: &path::Path) -> Vec<u8> {
- let mut h = Sha512::new();
- let st = metadata(filepath).unwrap();
- let bs: u64 = st.st_blksize();
- let sz: u64 = st.st_size();
- let mut b: Vec<u8> = vec!(0; bs as usize);
- let mut f = File::open(filepath).unwrap();
- let mut i: usize = 0;
- while i < sz as usize {
- let c = f.read(&mut b).unwrap();
- h.update(&b[..c]);
- i += c;
- }
- h.finalize().to_vec()
+ let mut h = Sha512::new();
+ let st = metadata(filepath).unwrap();
+ let bs: u64 = st.st_blksize();
+ let sz: u64 = st.st_size();
+ let mut b: Vec<u8> = vec!(0; bs as usize);
+ let mut f = File::open(filepath).unwrap();
+ let mut i: usize = 0;
+ while i < sz as usize {
+ let c = f.read(&mut b).unwrap();
+ h.update(&b[..c]);
+ i += c;
}
+ h.finalize().to_vec()
+}
+
impl MetaData {
- pub fn new(title: &str, author: &str, typ: EntryType, digest: Vec<u8>, filename: Option<FileName>) -> MetaData {
- let dc = DCMetaData::new(title, author, typ);
+ /// Create a new MetaData instance with basic data.
+ ///
+ /// # Arguments
+ ///
+ /// * `title` - Maps to the [DCMetaData::title] field.
+ /// * `author` - Maps to the [DCMetaData::author] field.
+ /// * `entry_type` - Maps to the [DCMetaData::typ] field.
+ /// * `digest` - The digest of the media file.
+ /// * `filename` - The client's optional local file name for the media.
+ pub fn new(title: &str, author: &str, entry_type: EntryType, digest: Vec<u8>, filename: Option<FileName>) -> MetaData {
+ let dc = DCMetaData::new(title, author, entry_type);
let mut m = MetaData{
dc: dc,
digest: digest::RecordDigest::Empty,
- comment: String::new(),
local_name: filename,
publish_date: (0, 0, 0),
- retrieval_timestamp: 0,
};
m.set_fingerprint(digest);
m
}
+ /// Create an empty MetaData instance.
pub fn empty() -> MetaData {
let dc = DCMetaData::new("", "", EntryType::Unknown(String::new()));
MetaData{
dc: dc,
digest: digest::RecordDigest::Empty,
- comment: String::new(),
//local_name: filepath.to_str().unwrap().to_string(),
local_name: None,
publish_date: (0, 0, 0),
- retrieval_timestamp: 0,
}
}
+ /// Set the [DCMetaData::title](DCMetaData::title) value.
pub fn set_title(&mut self, title: &str) {
self.dc.title = String::from(title);
}
+ /// Set the [DCMetaData::author](DCMetaData::author) value.
pub fn set_author(&mut self, author: &str) {
self.dc.author = String::from(author);
}
+ /// Set the digest as [digest::RecordDigest::Sha512](digest::RecordDigest::Sha512) instance of the provided
+ /// fingerprint.
pub fn set_fingerprint(&mut self, fingerprint: Vec<u8>) {
self.digest = digest::from_vec(fingerprint).unwrap();
}
+ /// Set the digest from the given URN string.
+ ///
+ /// The URN must specify a valid supported [digest](digest::from_urn) scheme.
pub fn set_fingerprint_urn(&mut self, urn: &str) {
self.digest = digest::from_urn(urn).unwrap();
}
+ /// Returns the current [DCMetaData::title](DCMetaData::title) value.
pub fn title(&self) -> String {
self.dc.title.clone()
}
+ /// Returns the current [DCMetaData::author](DCMetaData::author) value.
pub fn author(&self) -> String {
self.dc.author.clone()
}
+ /// Set the [DCMetaData::typ](DCMetaData::typ) value.
pub fn set_typ(&mut self, typ: &str) {
self.dc.typ = EntryType::from_str(typ).unwrap();
}
+ /// Returns the current [DCMetaData::typ](DCMetaData::typ) value.
pub fn typ(&self) -> EntryType {
self.dc.typ.clone()
}
+ /// Set the current [DCMetaData::subject](DCMetaData::subject) value.
pub fn set_subject(&mut self, v: &str) {
self.dc.subject = Some(String::from(v));
}
+ /// Returns the current [DCMetaData::subject](DCMetaData::subject) value.
pub fn subject(&self) -> Option<String> {
return self.dc.subject.clone();
}
+ /// Set the current [DCMetaData::mime](DCMetaData::mime) value.
pub fn set_mime(&mut self, m: Mime) {
self.dc.mime = Some(m);
}
+ /// Set the current [DCMetaData::mime](DCMetaData::mime) value from the given MIME identifier string.
pub fn set_mime_str(&mut self, s: &str) {
match Mime::from_str(s) {
Ok(v) => {
@@ -162,19 +195,23 @@ impl MetaData {
};
}
+ /// Returns the current [DCMetaData::mime](DCMetaData::mime) value.
pub fn mime(&self) -> Option<Mime> {
self.dc.mime.clone()
}
+ /// Set the current [DCMetaData::language](DCMetaData::language) value.
pub fn set_language(&mut self, s: &str) {
let v = s.parse().unwrap();
self.dc.language = Some(v);
}
+ /// Returns the current [DCMetaData::language](DCMetaData::language) value.
pub fn language(&self) -> Option<LanguageIdentifier> {
self.dc.language.clone()
}
+ /// Returns the digest value of the media as a hex-encoded string.
pub fn fingerprint(&self) -> String {
match &self.digest {
digest::RecordDigest::Empty => {
@@ -192,6 +229,7 @@ impl MetaData {
}
}
+ /// Instantiate metadata from the extended attributes of the file in `filepath`.
pub fn from_xattr(filepath: &path::Path) -> MetaData {
let mut title: String = String::new();
@@ -283,6 +321,9 @@ impl MetaData {
}
+ /// Applies the metadata as extended file attributes of the file in `filepath`.
+ ///
+ ///
pub fn to_xattr(&self, filepath: &path::Path) -> Result<(), std::io::Error> {
let filename = filepath.file_name()
.unwrap()
@@ -363,6 +404,7 @@ impl MetaData {
}
#[cfg(feature = "magic")]
+ /// Automatically detect media type of file in `path`.
pub fn set_mime_magic(&mut self, path: &path::Path) {
if self.mime() == None {
let mime = tree_magic::from_filepath(path);
@@ -371,6 +413,9 @@ impl MetaData {
}
}
+ /// Parse metadata from simplified metadata format contained in file in `path`.
+ ///
+ /// see [MetaData::from_file](MetaData::from_file)
pub fn from_path(p: &path::Path) -> Result<MetaData, std::io::Error> {
let f = File::open(&p).unwrap();
debug!("openning {}", p.display());
@@ -378,6 +423,9 @@ impl MetaData {
Ok(m)
}
+ /// Parse metadata from simplified metadata format contained in the given file instance `f`.
+ ///
+ /// TODO: describe format.
pub fn from_file(f: File) -> Result<MetaData, std::io::Error> {
let mut m = MetaData::empty();
//let f = File::open(path).unwrap();
diff --git a/src/rdf.rs b/src/rdf.rs
@@ -45,11 +45,23 @@ use crate::dc::{
};
#[derive(Debug)]
+/// Error states when processing RDF data.
pub enum RdfError {
+ /// Invalid URN string or digest scheme.
UrnError(UrnError),
+ /// Hash does not match hash in current [crate::meta::MetaData](crate::meta::MetaData)
+ /// instance.
HashMismatchError,
}
+/// Write metadata entry in the native rdf-turtle format.
+///
+/// On success, returns the number of bytes written.
+///
+/// # Arguments
+///
+/// * `entry` - metadata to write.
+/// * `w` - writer implementation providing the destination.
pub fn write(entry: &MetaData, w: impl Write) -> Result<usize, std::io::Error> {
let mut tfmt = TurtleFormatter::new(w);
@@ -186,6 +198,13 @@ fn handle_parse_match(metadata: &mut MetaData, triple: Triple) -> Result<(), Rdf
Ok(())
}
+/// Read one or more metadata entries from the rdf-turtle source.
+///
+/// Will return `ParseError` if any of the records are invalid.
+///
+/// # Arguments
+///
+/// * `r` - reader implementation providing the source.
pub fn read_all(r: impl Read) -> Result<Vec<MetaData>, ParseError> {
let mut rr: Vec<MetaData> = vec!();
let bf = BufReader::new(r);
@@ -218,6 +237,12 @@ pub fn read_all(r: impl Read) -> Result<Vec<MetaData>, ParseError> {
}
Ok(rr)
}
+
+/// Read a single metadata entry from the rdf-turtle source.
+///
+/// # Arguments
+///
+/// * `r` - reader implementation providing the source.
pub fn read(r: impl Read) -> MetaData {
let mut rr: Vec<MetaData> = vec!();
let mut metadata = MetaData::empty();