#[allow(clippy::all, clippy::pedantic, clippy::restriction, clippy::nursery)]
mod grammar;
mod parser;
use std::error::Error;
use std::fmt::{Display, Formatter};
use parser::Parser;
pub(crate) struct Rle {}
impl Rle {
pub const GROUP_START: char = '(';
pub const GROUP_END: char = ')';
pub const EOL: char = '|';
pub fn decode(input: &str, drop_spaces: bool) -> Result<String, RleSyntaxError> {
Parser::parse(input, drop_spaces)
}
pub fn encode(input: &str) -> Result<String, RleSyntaxError> {
if input.chars().any(|chr| chr.is_ascii_digit()) {
return Err(RleSyntaxError(RleSyntaxErrorKind::DigitsInEncoderInput));
}
Ok(input
.chars()
.map(|c| if c == '\n' { Self::EOL } else { c })
.map(Some)
.chain(std::iter::once(None))
.scan((0_usize, '\0'), |(n, c), elem| {
match elem {
Some(elem) if *n == 0 || *c == elem => {
*n += 1;
*c = elem;
Some(String::new())
}
Some(elem) => {
let run = if *n > 1 {
format!("{n}{c}")
} else {
format!("{c}")
};
*n = 1;
*c = elem;
Some(run)
}
None => {
Some(if *n > 1 {
format!("{n}{c}")
} else {
format!("{c}")
})
}
}
})
.collect())
}
}
#[derive(Debug, Clone)]
enum RleSyntaxErrorKind {
DecodingFailure { position: usize, input: String },
DigitsInEncoderInput,
}
#[derive(Debug, Clone)]
pub(crate) struct RleSyntaxError(RleSyntaxErrorKind);
impl Display for RleSyntaxError {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
match &self.0 {
RleSyntaxErrorKind::DecodingFailure { position, input } => {
write!(
f,
"run length decoding syntax error, check that all pairs of opened parens \
'(' are correctly closed with ')', at {position}, in \"{input}\""
)
}
RleSyntaxErrorKind::DigitsInEncoderInput => {
write!(f, "run length encoder input can't contain any digits")
}
}
}
}
impl Error for RleSyntaxError {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_rle_encodes_strings() -> Result<(), RleSyntaxError> {
for (input, output) in [
("aaaabbbbccdeee", "4a4b2cd3e"),
("aaaabbbbccdeeefghij", "4a4b2cd3efghij"),
("aaaabbbbccdeeef", "4a4b2cd3ef"),
("aaaabbfbbccdeee", "4a2bf2b2cd3e"),
("aaaabbfghijbbccdeee", "4a2bfghij2b2cd3e"),
(" aaaaaaaaaa ", " 10a3 "),
(" ", "3 "),
] {
assert_eq!(Rle::encode(input)?, output);
}
Ok(())
}
#[test]
fn when_rle_encoding_replaces_newline_with_rle_newline() -> Result<(), RleSyntaxError> {
let input = "aaaa\nbbbbccdeee";
let output = "4a|4b2cd3e";
assert_eq!(Rle::encode(input)?, output);
Ok(())
}
#[test]
fn it_refuses_to_encode_strings_having_any_digits() {
for input_string in ["sdfdfsg2dsfsdf", "0123456789"] {
assert!(Rle::encode(input_string).is_err());
}
}
}