use self::super::{parse, ErroneousBodyPath, HrxError};
use self::super::output::write_archive;
use std::io::{Error as IoError, Write};
use self::super::util::boundary_str;
use linked_hash_map::LinkedHashMap;
use std::num::NonZeroUsize;
use std::borrow::Borrow;
use std::str::FromStr;
use std::fmt;
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct HrxArchive {
pub comment: Option<String>,
pub entries: LinkedHashMap<HrxPath, HrxEntry>,
pub(crate) boundary_length: NonZeroUsize,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct HrxEntry {
pub comment: Option<String>,
pub data: HrxEntryData,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub enum HrxEntryData {
File { body: Option<String>, },
Directory,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct HrxPath(pub(crate) String);
impl HrxArchive {
pub fn new(boundary_length: NonZeroUsize) -> HrxArchive {
HrxArchive {
comment: None,
entries: LinkedHashMap::new(),
boundary_length: boundary_length,
}
}
pub fn boundary_length(&self) -> NonZeroUsize {
self.boundary_length
}
pub fn set_boundary_length(&mut self, new_len: NonZeroUsize) -> Result<(), HrxError> {
self.validate_boundlen(new_len)?;
self.boundary_length = new_len;
Ok(())
}
pub fn validate_content(&self) -> Result<(), HrxError> {
self.validate_boundlen(self.boundary_length)
}
fn validate_boundlen(&self, len: NonZeroUsize) -> Result<(), HrxError> {
let bound = boundary_str(len);
let mut paths = vec![];
let _ = verify_opt(&self.comment, &bound).map_err(|_| paths.push(ErroneousBodyPath::RootComment));
for (pp, dt) in &self.entries {
let _ = verify_opt(&dt.comment, &bound).map_err(|_| paths.push(ErroneousBodyPath::EntryComment(pp.to_string())));
match dt.data {
HrxEntryData::File { ref body } => {
let _ = verify_opt(&body, &bound).map_err(|_| paths.push(ErroneousBodyPath::EntryData(pp.to_string())));
}
HrxEntryData::Directory => {}
}
}
if !paths.is_empty() {
Err(paths.into())
} else {
Ok(())
}
}
pub fn serialise<W: Write>(&self, into: &mut W) -> Result<(), Result<HrxError, IoError>> {
write_archive(&self, into)
}
}
fn verify_opt(which: &Option<String>, with: &str) -> Result<(), ()> {
if let Some(dt) = which.as_ref() {
if dt.find(with).is_some() {
return Err(());
}
}
Ok(())
}
impl FromStr for HrxArchive {
type Err = HrxError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let width = parse::discover_first_boundary_length(s).ok_or(HrxError::NoBoundary)?;
let (comment, entries, boundary_length) = parse::archive(s, width).map_err(parse::ParseError::from)?;
Ok(HrxArchive {
comment: comment,
entries: parse::reduce_raw_entries_and_validate_directory_tree(entries)?,
boundary_length: boundary_length,
})
}
}
impl HrxPath {
pub fn into_inner(self) -> String {
self.0
}
}
impl fmt::Display for HrxPath {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
fmt.write_str(&self.0)
}
}
impl FromStr for HrxPath {
type Err = HrxError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let parsed = parse::path(s, NonZeroUsize::new(1).unwrap()).map_err(parse::ParseError::from)?;
Ok(parsed)
}
}
impl Borrow<str> for HrxPath {
fn borrow(&self) -> &str {
&self.0
}
}
impl AsRef<str> for HrxPath {
fn as_ref(&self) -> &str {
&self.0
}
}