chksum3 0.5.0

Calculates an 8- or 32-bit little-endian checksum.
use std::fs::File;
use argh::FromArgs;
use std::io::stdin;
use std::path::PathBuf;
use std::io::prelude::*;
use std::num::Wrapping;
use byteorder::{ByteOrder, LittleEndian};

pub enum Bits {
    U32,
    U8
}

fn default_bits() -> Bits {
    Bits::U32
}

fn bits_from_str(_value: &str) -> Result<Bits, String> {
    return match _value {
        "32" => { Ok(Bits::U32) },
        "8" => { Ok(Bits::U8) },
        _ => { Err("value of argument 'bits' must be 8 or 32.".parse().unwrap()) }
    }
}

#[derive(FromArgs)]
/// Calculates 8- and 32-bit little endian checksums.
/// If no paths are specified, checksum is calculated from stdin.
pub struct Args {
    /// select between 8 or 32 bit sum
    #[argh(option, from_str_fn(bits_from_str), default = "default_bits()", short = 'b')]
    pub bits: Bits,

    /// input file(s)
    #[argh(positional)]
    pub paths: Vec<PathBuf>,
}

const BUF_LEN: usize = 4096;

pub fn run(args: Args) -> Result<(), &'static str> {
    if args.paths.is_empty() {
        return checksum_from_stdin(&args.bits);
    } else if args.paths.len() == 1 && args.paths[0].to_str().unwrap() == "-" {
        return checksum_from_stdin(&args.bits);
    } else {
        for path in args.paths {
            let mut file = File::open(&path).unwrap();

            let mut buffer = [0u8; BUF_LEN];

            match args.bits {
                Bits::U32 => {
                    let fsize = file.metadata().unwrap().len();
                    if fsize % 4 != 0 {
                        return Err("file length invalid, not a multiple of 4");
                    }

                    let mut c32 = Checksum32::new();

                    loop {
                        let read_count = file.read(&mut buffer).unwrap();
                        c32.write(&buffer[..read_count]);

                        if read_count != BUF_LEN {
                            break;
                        }
                    }

                    println!("{:08X} {}", c32.sum(), &path.to_str().unwrap())
                },
                Bits::U8 => {
                    let mut c8 = Checksum8::new();

                    loop {
                        let read_count = file.read(&mut buffer).unwrap();
                        c8.write(&buffer[..read_count]);

                        if read_count != BUF_LEN {
                            break;
                        }
                    }

                    println!("{:02X} {}", c8.sum(), &path.to_str().unwrap())
                }
            }
        }
    }

    return Ok(());
}

fn checksum_from_stdin(bits: &Bits) -> Result<(), &'static str> {
    let mut buf = Vec::new();

    match stdin().read_to_end(&mut buf) {
        Ok(_) => { }
        Err(err) => {panic!("Error: {}", err)}
    }

    match bits {
        Bits::U32 => {
            let fsize = buf.len();
            if fsize % 4 != 0 {
                return Err("file length invalid, not a multiple of 4");
            }

            let mut c32 = Checksum32::new();

            c32.write(&buf[..]);

            println!("{:08X} {}", c32.sum(), "-")
        },
        Bits::U8 => {
            let mut c8 = Checksum8::new();

            c8.write(&buf[..]);

            println!("{:02X} {}", c8.sum(), "-")
        }
    }

    return Ok(());
}

struct Checksum8 {
    state: Wrapping<u8>
}

impl Checksum8 {
    pub fn new() -> Self {
        Checksum8 {
            state: Wrapping(0u8)
        }
    }

    pub fn write(&mut self, bytes: &[u8]) {
        for x in bytes.iter() {
            self.state += x & 0xff;
        }
    }

    pub fn sum(&self) -> u8 {
        return self.state.0
    }
}

struct Checksum32 {
    state: Wrapping<u32>
}

impl Checksum32 {
    pub fn new() -> Self {
        Checksum32 {
            state: Wrapping(0u32)
        }
    }

    pub fn write(&mut self, bytes: &[u8]) {
        for x in (0..bytes.len()).step_by(4) {
            self.state += LittleEndian::read_u32(&bytes[x..x+4]);
        }
    }

    pub fn sum(&self) -> u32 {
        return self.state.0
    }
}