Compress Repeated Characters with Run-Length Encoding

Encode repeated characters with counts and decode the result back, rejecting zero counts and dangling numeric prefixes.

RustData Integrity

Code

snippet
fn encode(input: &str) -> String {
    let mut output = String::new();
    let mut characters = input.chars().peekable();
    while let Some(current) = characters.next() {
        let mut count = 1usize;
        while characters.peek() == Some(&current) {
            characters.next();
            count += 1;
        }
        if count > 1 {
            output.push_str(&count.to_string());
        }
        output.push(current);
    }
    output
}

fn decode(input: &str) -> Result<String, String> {
    let mut output = String::new();
    let mut digits = String::new();
    for character in input.chars() {
        if character.is_ascii_digit() {
            digits.push(character);
            continue;
        }
        let count = if digits.is_empty() {
            1
        } else {
            digits
                .parse::<usize>()
                .map_err(|_| format!("invalid count: {digits}"))?
        };
        if count == 0 {
            return Err("count must be positive".to_string());
        }
        output.extend(std::iter::repeat(character).take(count));
        digits.clear();
    }
    if !digits.is_empty() {
        return Err("trailing count without a character".to_string());
    }
    Ok(output)
}

fn main() {
    for sample in ["aaabccdddd", "abc", "wwwwwwwwwwww"] {
        let encoded = encode(sample);
        let decoded = decode(&encoded).expect("valid encoding");
        println!("{sample} -> {encoded} -> {decoded}");
        assert_eq!(decoded, sample);
    }
    assert_eq!(encode("aaabccdddd"), "3ab2c4d");
    assert!(decode("0a").is_err());
    assert!(decode("12").is_err());
}