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
//! Machinery common to all codec implementations.

use crate::transcoder;
use crate::transcoder::TranscodeError;
use std::convert::Infallible;
use std::error::Error;
use std::io;
use std::io::BufRead;
use std::io::Write;

pub mod armenoid;
pub mod balkanoid;

/// The specification of a codec.
///
/// Codecs work line by line. As such, they require two method implementations:
/// [`compress_line`](Codec::compress_line) — to encode one line of input text, and
/// [`expand_line`](Codec::expand_line) — to perform the reverse operation.
///
/// Compression is not allowed to return an error; the thinking is that all
/// text should be compressible. Expansion, however, may return an error. It is possible
/// that the output of compression may have been mangled with, in which case expansion
/// is not possible.
pub trait Codec {
    /// The type of error returned during expansion.
    type ExpandError: Error;

    /// Compresses a line of text, returning its encoded representation as a [`String`].
    fn compress_line(&self, line: &str) -> String;

    /// Expands a line of text, returning its decoded representation as a [`String`].
    ///
    /// # Errors
    /// Expansion is not always possible, in which case an [`ExpandError`](Self::ExpandError)
    /// is returned.
    fn expand_line(&self, line: &str) -> Result<String, Self::ExpandError>;

    /// A helper method for compressing a series of lines from a given buffered reader,
    /// outputting the result into the given writer.
    ///
    /// # Errors
    /// [`io::Error`] if the reader or the writer encountered an I/O error.
    fn compress(&self, r: &mut impl BufRead, w: &mut impl Write) -> Result<(), io::Error> {
        let result: Result<(), TranscodeError<Infallible>> =
            transcoder::transcode(r, w, |_, line| Ok(self.compress_line(line)));
        result.map_err(|err| err.into_io_error().unwrap())
    }

    /// A helper method for expanding a series of lines from a given buffered reader,
    /// outputting the result into the given writer.
    ///
    /// # Errors
    /// [`TranscodeError`] if the reader or the writer encountered an I/O error, or
    /// if the codec was unable to expand a line.
    fn expand(
        &self,
        r: &mut impl BufRead,
        w: &mut impl Write,
    ) -> Result<(), TranscodeError<Self::ExpandError>> {
        transcoder::transcode(r, w, |_, line| self.expand_line(line))
    }
}

#[cfg(test)]
mod tests;