1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
use self::super::{HrxEntryData, HrxArchive, HrxError};
use std::io::{Error as IoError, Write};
use self::super::util::boundary_str;


/// `IoError` is absolute garbage when it comes to usability when it's a variant
enum CompoundError {
    Hrx(HrxError),
    Io(IoError),
}

impl From<HrxError> for CompoundError {
    fn from(hrx: HrxError) -> CompoundError {
        CompoundError::Hrx(hrx)
    }
}

impl From<IoError> for CompoundError {
    fn from(io: IoError) -> CompoundError {
        CompoundError::Io(io)
    }
}

impl From<CompoundError> for Result<HrxError, IoError> {
    fn from(ce: CompoundError) -> Result<HrxError, IoError> {
        match ce {
            CompoundError::Hrx(hrx) => Ok(hrx),
            CompoundError::Io(io) => Err(io),
        }
    }
}


pub fn write_archive<W: Write>(ar: &HrxArchive, into: &mut W) -> Result<(), Result<HrxError, IoError>> {
    write_archive_impl(ar, into)?;
    Ok(())
}

fn write_archive_impl<W: Write>(ar: &HrxArchive, into: &mut W) -> Result<(), CompoundError> {
    ar.validate_content()?;

    let bound = boundary_str(ar.boundary_length);
    let mut first_bound = true;
    let mut ending_newline = false;

    for (p, e) in &ar.entries {
        write_comment(&e.comment, &bound, &mut first_bound, into)?;

        write_bound(&bound, &mut first_bound, into)?;
        into.write_all(&[b' '])?;
        into.write_all(p.0.as_bytes())?;
        match e.data {
            HrxEntryData::File { body: None } => {
                ending_newline = true;
            }
            HrxEntryData::File { body: Some(ref body) } if body.is_empty() => {
                ending_newline = true;
            }
            HrxEntryData::File { body: Some(ref body) } => {
                into.write_all(&[b'\n'])?;
                into.write_all(body.as_bytes())?;

                ending_newline = false;
            }
            HrxEntryData::Directory => {
                into.write_all(&[b'/'])?;
                ending_newline = true;
            }
        }
    }

    if !write_comment(&ar.comment, &bound, &mut first_bound, into)? && ending_newline {
        into.write_all(&[b'\n'])?;
    }

    Ok(())
}

fn write_bound<W: Write>(bound: &str, first_bound: &mut bool, into: &mut W) -> Result<(), CompoundError> {
    if *first_bound {
        into.write_all(bound[1..].as_bytes())?;

        *first_bound = false;
    } else {
        into.write_all(bound.as_bytes())?;
    }

    Ok(())
}

fn write_comment<W: Write>(comment: &Option<String>, bound: &str, first_bound: &mut bool, into: &mut W) -> Result<bool, CompoundError> {
    if let Some(cmt) = comment.as_ref() {
        write_bound(bound, first_bound, into)?;
        into.write_all(&[b'\n'])?;
        into.write_all(cmt.as_bytes())?;

        Ok(true)
    } else {
        Ok(false)
    }
}