<commit_before>extern crate ar;
extern crate clap;
extern crate libflate;
extern crate tar;
extern crate tempfile;
extern crate time as crates_time;
extern crate xz2;
extern crate zip;

use std::fs;
use std::io;
use std::time;

use std::rc::Rc;
use std::vec::Vec;

use clap::{Arg, App};

use libflate::gzip;
use tempfile::tempfile;

// magic:
use std::io::Seek;
use std::io::Write;

#[derive(Debug)]
struct Node<T> {
    next: Option<Rc<Node<T>>>,
    value: T,
}

impl<T: Clone> Node<T> {
    fn head(obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: None,
            value: obj,
        })
    }

    fn plus(what: &Rc<Node<T>>, obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: Some(what.clone()),
            value: obj
        })
    }

    fn to_vec(&self) -> Vec<T> {
        let mut ret = Vec::new();
        let mut val = self;
        loop {
            ret.push(val.value.clone());
            if let Some(ref next) = val.next {
                val = next;
            } else {
                break;
            }
        }
        ret.reverse();
        ret
    }
}

struct OutputTo {
    path: Rc<Node<String>>,
    size: u64,
    atime: u64,
    mtime: u64,
    ctime: u64,
    btime: u64,
}

impl OutputTo {
    fn warn(&self, msg: String) {
        writeln!(io::stderr(), "TODO: {}", msg).expect("stderr");
    }

    fn raw<F: io::Read + ?Sized>(&self, mut file: &mut F) -> io::Result<()> {
        let stdout = io::stdout();
        let mut stdout = stdout.lock();
        writeln!(stdout, "{:?} {} {} {} {} {}",
            self.path.to_vec(),
            self.size,
            self.atime, self.mtime, self.ctime, self.btime
        )?;

        io::copy(&mut file, &mut stdout).and_then(move |written|
            if written != self.size {
                Err(io::Error::new(
                    io::ErrorKind::UnexpectedEof,
                    format!("expecting to write {} but wrote {}", self.size, written)))
            } else {
                Ok(())
            })
    }

    fn from_file(path: &str, fd: &fs::File) -> io::Result<OutputTo> {
        let meta = fd.metadata()?;
        Ok(OutputTo {
            path: Node::head(path.to_string()),
            size: meta.len(),
            atime: meta.accessed().map(simple_time_sys)?,
            mtime: meta.modified().map(simple_time_sys)?,
            ctime: simple_time_ctime(&meta),
            btime: simple_time_btime(&meta)?,
        })
    }

    fn with_path(&self, path: &str) -> OutputTo {
        OutputTo {
            path: Node::plus(&self.path, path.to_string()),
            size: 0,
            atime: 0,
            mtime: 0,
            ctime: 0,
            btime: 0,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum FileType {
    GZip,
    Zip,
    Tar,
    Ar,
    BZip2,
    Xz,
    Other,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Status {
    Done,
    Rewind,
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end+1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn simple_time(dur: time::Duration) -> u64 {
    dur.as_secs().checked_mul(1_000_000_000)
        .map_or(0, |nanos| nanos + dur.subsec_nanos() as u64)
}

fn simple_time_sys(val: time::SystemTime) -> u64 {
    val.duration_since(time::UNIX_EPOCH).map(simple_time).unwrap_or(0)
}

fn simple_time_tm(val: crates_time::Tm) -> u64 {
    let timespec = val.to_timespec();
    simple_time(time::Duration::new(timespec.sec as u64, timespec.nsec as u32))
}

fn simple_time_btime(val: &fs::Metadata) -> io::Result<u64> {
    match val.created() {
        Ok(time) => Ok(simple_time_sys(time)),
        // "Other" is how "unsupported" is represented here; ew.
        Err(ref e) if e.kind() == io::ErrorKind::Other => Ok(0),
        Err(other) => Err(other),
    }
}

// TODO: I really feel this should be exposed by Rust, without cfg.
fn simple_time_ctime(val: &fs::Metadata) -> u64 {
    #[cfg(linux)] {
        use std::os::linux::fs::MetadataExt;
        let ctime: i64 = val.st_ctime();
        if ctime <= 0 {
            0
        } else {
            ctime as u64
        }
    }

    #[cfg(not(linux))] {
        0
    }
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

fn identify<'a, T: io::BufRead + ?Sized>(fd: &mut T) -> io::Result<FileType> {
    let header = fd.fill_buf()?;
    if header.len() >= 20
        && 0x1f == header[0] && 0x8b == header[1] {
        Ok(FileType::GZip)
   } else if header.len() >= 152
        && b'P' == header[0] && b'K' == header[1]
        && 0x03 == header[2] && 0x04 == header[3] {
        Ok(FileType::Zip)
    } else if header.len() > 257 + 10
        && b'u' == header[257] && b's' == header[258]
        && b't' == header[259] && b'a' == header[260]
        && b'r' == header[261]
        && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
            (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
        Ok(FileType::Tar)
    } else if header.len() > 8
        && b'!' == header[0] && b'<' == header[1]
        && b'a' == header[2] && b'r' == header[3]
        && b'c' == header[4] && b'h' == header[5]
        && b'>' == header[6] && b'\n' == header[7] {
        Ok(FileType::Ar)
    } else if header.len() > 40
        && b'B' == header[0] && b'Z' == header[1]
        && b'h' == header[2] && 0x31 == header[3]
        && 0x41 == header[4] && 0x59 == header[5]
        && 0x26 == header[6] {
        Ok(FileType::BZip2)
    } else if header.len() > 6
        && 0xfd == header[0] && b'7' == header[1]
        && b'z' == header[2] && b'X' == header[3]
        && b'Z' == header[4] && 0 == header[5] {
        Ok(FileType::Xz)
    } else if is_probably_tar(header) {
        Ok(FileType::Tar)
    } else {
        Ok(FileType::Other)
    }
}

trait Tee {
    fn normal(&mut self) -> &mut io::BufRead;
    fn abort(&mut self) -> io::Result<&mut io::BufRead>;
}

struct TempFileTee<'a> {
    fp: Box<io::BufRead + 'a>,
    tmp: fs::File,
}

impl<'a> TempFileTee<'a> {
    fn new<U: 'a + io::Read>(from: U) -> io::Result<TempFileTee<'a>> {
        Ok(TempFileTee {
            fp: Box::new(io::BufReader::new(from)),
            tmp: tempfile()?
        })
    }
}

impl<'a> Tee for TempFileTee<'a> {
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }
    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        unimplemented!();
    }
}

struct BufReaderTee<T> {
    fp: Box<T>,
}

impl<T> BufReaderTee<T> {
    // TODO: return type?
    fn new<U: io::Read>(from: U) -> BufReaderTee<io::BufReader<U>> {
        BufReaderTee {
            fp: Box::new(io::BufReader::new(from))
        }
    }
}

impl<T> Tee for BufReaderTee<T>
    where T: io::BufRead + io::Seek
{
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }

    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        self.fp.seek(io::SeekFrom::Start(0))?;
        Ok(self.normal())
    }
}

fn count_bytes<'a, R: io::Read>(mut fd: &mut R) -> io::Result<u64> {
    let mut block = [0u8; 4096];
    let mut count = 0u64;
    loop {
        let found = fd.read(&mut block[..])?;
        if 0 == found {
            break;
        }
        count += found as u64;
    }
    Ok(count)
}

fn unpack<'a>(mut fd: Box<Tee + 'a>, output: &OutputTo) -> io::Result<Status> {
    match identify(fd.normal())? {
        FileType::GZip => {
            let process_decoded = match gzip::Decoder::new(fd.normal()) {
                Ok(dec) => Ok(unpack(Box::new(TempFileTee::new(dec)?), output)?),
                Err(e) => Err(e),
            };

            match process_decoded {
                Ok(status) => Ok(status),
                Err(e) => {
                    output.warn(format!(
                        "thought we could unpack '{:?}' but we couldn't: {}",
                        output.path, e));
                    output.raw(fd.abort()?)?;
                    Ok(Status::Done)
                }
            }
        },
//        FileType::Xz => {
//            unpack_or_raw(fd, output,
//                |fd| Ok(Box::new(TempFileTee::new(xz2::bufread::XzDecoder::new(fd)))))
//        },
        FileType::Ar if output.path.value.ends_with(".deb") => {
            let mut decoder = ar::Archive::new(fd.normal());
            while let Some(entry) = decoder.next_entry() {
                let entry = entry?;
                let new_output = output.with_path(entry.header().identifier());
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(Status::Done)
        },
        FileType::Tar => {
            let mut decoder = tar::Archive::new(fd.normal());
            for entry in decoder.entries()? {
                let entry = entry?;
                let new_output = output.with_path(entry.path()?.to_str().expect("valid utf-8"));
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(Status::Done)
        },
        FileType::Zip => {
            let mut temp = tempfile()?;
            io::copy(fd.normal(), &mut temp)?;
            let mut zip = zip::ZipArchive::new(temp)?;

            for i in 0..zip.len() {

                // well, this has gone rather poorly
                let mut new_output = output.with_path(zip.by_index(i)?.name());

                let file_result = {
                    let entry = zip.by_index(i)?;

                    new_output.size = entry.size();
                    new_output.mtime = simple_time_tm(entry.last_modified());
                    let reader = Box::new(TempFileTee::new(entry)?);
                    unpack(reader, &new_output)?
                };
                match file_result {
                    Status::Done => (),
                    Status::Rewind => {
                        // TODO: update output? ? handling? ???
                        new_output.raw(&mut zip.by_index(i)?)?
                    },
                };
            }

            Ok(Status::Done)
        },

        // TODO: unimplemented!()
        _ => {
            Ok(Status::Rewind)
        },
    }
}

fn output_raw(file: &mut fs::File, output: &OutputTo) -> io::Result<()> {
    file.seek(io::SeekFrom::Start(0))?;
    output.raw(file)?;
    Ok(())
}

fn process_real_path(path: &str) -> io::Result<()> {
    let mut file = fs::File::open(path).unwrap();
    let output = OutputTo::from_file(
        path,
        &file
    )?;

    let attempt = {
//        let read = BufReaderTee::new::<BufReaderTee<io::BufReader<fs::File>>>(file);
        let read = TempFileTee::new(&file)?;
        unpack(Box::new(read), &output)
    };

    match attempt {
        Ok(Status::Done) => Ok(()),
        Ok(Status::Rewind) => output_raw(&mut file, &output),
        Err(e) => Err(e)
    }
}

fn real_main() -> u8 {

    let matches = App::new("contentin")
                    .arg(Arg::with_name("to-tar")
                         .long("to-tar")
                         .help("emit a tar file")
                         .required(true))
                    .arg(Arg::with_name("INPUT")
                         .required(true)
                         .multiple(true))
                    .get_matches();

    for path in matches.values_of("INPUT").unwrap() {
        if let Err(e) = process_real_path(path) {
            if let Err(_) = writeln!(io::stderr(), "fatal: processing '{}': {}", path, e) {
                return 6;
            }

            return 4;
        }
    }
    return 0;
}

fn main() {
    std::process::exit(real_main() as i32)
}
<commit_msg>can't work out the lifetimes for that inline<commit_after>extern crate ar;
extern crate clap;
extern crate libflate;
extern crate tar;
extern crate tempfile;
extern crate time as crates_time;
extern crate xz2;
extern crate zip;

use std::fs;
use std::io;
use std::time;

use std::rc::Rc;
use std::vec::Vec;

use clap::{Arg, App};

use libflate::gzip;
use tempfile::tempfile;

// magic:
use std::io::Seek;
use std::io::Write;

#[derive(Debug)]
struct Node<T> {
    next: Option<Rc<Node<T>>>,
    value: T,
}

impl<T: Clone> Node<T> {
    fn head(obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: None,
            value: obj,
        })
    }

    fn plus(what: &Rc<Node<T>>, obj: T) -> Rc<Node<T>> {
        Rc::new(Node {
            next: Some(what.clone()),
            value: obj
        })
    }

    fn to_vec(&self) -> Vec<T> {
        let mut ret = Vec::new();
        let mut val = self;
        loop {
            ret.push(val.value.clone());
            if let Some(ref next) = val.next {
                val = next;
            } else {
                break;
            }
        }
        ret.reverse();
        ret
    }
}

struct OutputTo {
    path: Rc<Node<String>>,
    size: u64,
    atime: u64,
    mtime: u64,
    ctime: u64,
    btime: u64,
}

impl OutputTo {
    fn warn(&self, msg: String) {
        writeln!(io::stderr(), "TODO: {}", msg).expect("stderr");
    }

    fn raw<F: io::Read + ?Sized>(&self, mut file: &mut F) -> io::Result<()> {
        let stdout = io::stdout();
        let mut stdout = stdout.lock();
        writeln!(stdout, "{:?} {} {} {} {} {}",
            self.path.to_vec(),
            self.size,
            self.atime, self.mtime, self.ctime, self.btime
        )?;

        io::copy(&mut file, &mut stdout).and_then(move |written|
            if written != self.size {
                Err(io::Error::new(
                    io::ErrorKind::UnexpectedEof,
                    format!("expecting to write {} but wrote {}", self.size, written)))
            } else {
                Ok(())
            })
    }

    fn from_file(path: &str, fd: &fs::File) -> io::Result<OutputTo> {
        let meta = fd.metadata()?;
        Ok(OutputTo {
            path: Node::head(path.to_string()),
            size: meta.len(),
            atime: meta.accessed().map(simple_time_sys)?,
            mtime: meta.modified().map(simple_time_sys)?,
            ctime: simple_time_ctime(&meta),
            btime: simple_time_btime(&meta)?,
        })
    }

    fn with_path(&self, path: &str) -> OutputTo {
        OutputTo {
            path: Node::plus(&self.path, path.to_string()),
            size: 0,
            atime: 0,
            mtime: 0,
            ctime: 0,
            btime: 0,
        }
    }
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum FileType {
    GZip,
    Zip,
    Tar,
    Ar,
    BZip2,
    Xz,
    Other,
}

#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum Status {
    Done,
    Rewind,
}

fn read_octal(bytes: &[u8]) -> Option<u32> {
    let mut start = 0;
    while start < bytes.len() && b' ' == bytes[start] {
        start += 1;
    }

    let mut end = bytes.len() - 1;
    while end > start && (b' ' == bytes[end] || 0 == bytes[end]) {
        end -= 1;
    }

    if let Ok(string) = std::str::from_utf8(&bytes[start..(end+1)]) {
        if let Ok(val) = u32::from_str_radix(string, 8) {
            return Some(val);
        }
    }
    None
}

fn simple_time(dur: time::Duration) -> u64 {
    dur.as_secs().checked_mul(1_000_000_000)
        .map_or(0, |nanos| nanos + dur.subsec_nanos() as u64)
}

fn simple_time_sys(val: time::SystemTime) -> u64 {
    val.duration_since(time::UNIX_EPOCH).map(simple_time).unwrap_or(0)
}

fn simple_time_tm(val: crates_time::Tm) -> u64 {
    let timespec = val.to_timespec();
    simple_time(time::Duration::new(timespec.sec as u64, timespec.nsec as u32))
}

fn simple_time_btime(val: &fs::Metadata) -> io::Result<u64> {
    match val.created() {
        Ok(time) => Ok(simple_time_sys(time)),
        // "Other" is how "unsupported" is represented here; ew.
        Err(ref e) if e.kind() == io::ErrorKind::Other => Ok(0),
        Err(other) => Err(other),
    }
}

// TODO: I really feel this should be exposed by Rust, without cfg.
fn simple_time_ctime(val: &fs::Metadata) -> u64 {
    #[cfg(linux)] {
        use std::os::linux::fs::MetadataExt;
        let ctime: i64 = val.st_ctime();
        if ctime <= 0 {
            0
        } else {
            ctime as u64
        }
    }

    #[cfg(not(linux))] {
        0
    }
}

fn is_probably_tar(header: &[u8]) -> bool {
    if header.len() < 512 {
        return false;
    }

    if let Some(checksum) = read_octal(&header[148..156]) {
        let mut sum: u32 = (b' ' as u32) * 8;
        for i in 0..148 {
            sum += header[i] as u32;
        }
        for i in 156..512 {
            sum += header[i] as u32;
        }

        if checksum == sum {
            return true;
        }
    }

    return false;
}

fn identify<'a, T: io::BufRead + ?Sized>(fd: &mut T) -> io::Result<FileType> {
    let header = fd.fill_buf()?;
    if header.len() >= 20
        && 0x1f == header[0] && 0x8b == header[1] {
        Ok(FileType::GZip)
   } else if header.len() >= 152
        && b'P' == header[0] && b'K' == header[1]
        && 0x03 == header[2] && 0x04 == header[3] {
        Ok(FileType::Zip)
    } else if header.len() > 257 + 10
        && b'u' == header[257] && b's' == header[258]
        && b't' == header[259] && b'a' == header[260]
        && b'r' == header[261]
        && (
            (0 == header[262] && b'0' == header[263] && b'0' == header[264]) ||
            (b' ' == header[262] && b' ' == header[263] && 0 == header[264])
        ) {
        Ok(FileType::Tar)
    } else if header.len() > 8
        && b'!' == header[0] && b'<' == header[1]
        && b'a' == header[2] && b'r' == header[3]
        && b'c' == header[4] && b'h' == header[5]
        && b'>' == header[6] && b'\n' == header[7] {
        Ok(FileType::Ar)
    } else if header.len() > 40
        && b'B' == header[0] && b'Z' == header[1]
        && b'h' == header[2] && 0x31 == header[3]
        && 0x41 == header[4] && 0x59 == header[5]
        && 0x26 == header[6] {
        Ok(FileType::BZip2)
    } else if header.len() > 6
        && 0xfd == header[0] && b'7' == header[1]
        && b'z' == header[2] && b'X' == header[3]
        && b'Z' == header[4] && 0 == header[5] {
        Ok(FileType::Xz)
    } else if is_probably_tar(header) {
        Ok(FileType::Tar)
    } else {
        Ok(FileType::Other)
    }
}

trait Tee {
    fn normal(&mut self) -> &mut io::BufRead;
    fn abort(&mut self) -> io::Result<&mut io::BufRead>;
}

struct TempFileTee<'a> {
    fp: Box<io::BufRead + 'a>,
    tmp: fs::File,
}

impl<'a> TempFileTee<'a> {
    fn new<U: 'a + io::Read>(from: U) -> io::Result<TempFileTee<'a>> {
        Ok(TempFileTee {
            fp: Box::new(io::BufReader::new(from)),
            tmp: tempfile()?
        })
    }
}

impl<'a> Tee for TempFileTee<'a> {
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }
    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        unimplemented!();
    }
}

struct BufReaderTee<T> {
    fp: Box<T>,
}

impl<T> BufReaderTee<T> {
    // TODO: return type?
    fn new<U: io::Read>(from: U) -> BufReaderTee<io::BufReader<U>> {
        BufReaderTee {
            fp: Box::new(io::BufReader::new(from))
        }
    }
}

impl<T> Tee for BufReaderTee<T>
    where T: io::BufRead + io::Seek
{
    fn normal(&mut self) -> &mut io::BufRead {
        &mut self.fp
    }

    fn abort(&mut self) -> io::Result<&mut io::BufRead> {
        self.fp.seek(io::SeekFrom::Start(0))?;
        Ok(self.normal())
    }
}

fn count_bytes<'a, R: io::Read>(mut fd: &mut R) -> io::Result<u64> {
    let mut block = [0u8; 4096];
    let mut count = 0u64;
    loop {
        let found = fd.read(&mut block[..])?;
        if 0 == found {
            break;
        }
        count += found as u64;
    }
    Ok(count)
}

fn unpack_or_raw<'a, 'b, F, T: 'b + io::Read>(mut fd: Box<Tee + 'a>, output: &OutputTo, f: F) -> io::Result<Status>
where F: FnOnce(&'b mut io::BufRead) -> io::Result<T>
{
    let process_decoded = match f(fd.normal()) {
        Ok(dec) => Ok(unpack(Box::new(TempFileTee::new(dec)?), output)?),
        Err(e) => Err(e),
    };

    match process_decoded {
        Ok(status) => Ok(status),
        Err(e) => {
            output.warn(format!(
                "thought we could unpack '{:?}' but we couldn't: {}",
                output.path, e));
            output.raw(fd.abort()?)?;
            Ok(Status::Done)
        }
    }
}

fn unpack<'a>(mut fd: Box<Tee + 'a>, output: &OutputTo) -> io::Result<Status> {
    match identify(fd.normal())? {
        FileType::GZip => {
            unpack_or_raw(fd, output, move |han| gzip::Decoder::new(han))
        },
//        FileType::Xz => {
//            unpack_or_raw(fd, output,
//                |fd| Ok(Box::new(TempFileTee::new(xz2::bufread::XzDecoder::new(fd)))))
//        },
        FileType::Ar if output.path.value.ends_with(".deb") => {
            let mut decoder = ar::Archive::new(fd.normal());
            while let Some(entry) = decoder.next_entry() {
                let entry = entry?;
                let new_output = output.with_path(entry.header().identifier());
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(Status::Done)
        },
        FileType::Tar => {
            let mut decoder = tar::Archive::new(fd.normal());
            for entry in decoder.entries()? {
                let entry = entry?;
                let new_output = output.with_path(entry.path()?.to_str().expect("valid utf-8"));
                unpack(Box::new(TempFileTee::new(entry)?), &new_output)?;
            }
            Ok(Status::Done)
        },
        FileType::Zip => {
            let mut temp = tempfile()?;
            io::copy(fd.normal(), &mut temp)?;
            let mut zip = zip::ZipArchive::new(temp)?;

            for i in 0..zip.len() {

                // well, this has gone rather poorly
                let mut new_output = output.with_path(zip.by_index(i)?.name());

                let file_result = {
                    let entry = zip.by_index(i)?;

                    new_output.size = entry.size();
                    new_output.mtime = simple_time_tm(entry.last_modified());
                    let reader = Box::new(TempFileTee::new(entry)?);
                    unpack(reader, &new_output)?
                };
                match file_result {
                    Status::Done => (),
                    Status::Rewind => {
                        // TODO: update output? ? handling? ???
                        new_output.raw(&mut zip.by_index(i)?)?
                    },
                };
            }

            Ok(Status::Done)
        },

        // TODO: unimplemented!()
        _ => {
            Ok(Status::Rewind)
        },
    }
}

fn output_raw(file: &mut fs::File, output: &OutputTo) -> io::Result<()> {
    file.seek(io::SeekFrom::Start(0))?;
    output.raw(file)?;
    Ok(())
}

fn process_real_path(path: &str) -> io::Result<()> {
    let mut file = fs::File::open(path).unwrap();
    let output = OutputTo::from_file(
        path,
        &file
    )?;

    let attempt = {
//        let read = BufReaderTee::new::<BufReaderTee<io::BufReader<fs::File>>>(file);
        let read = TempFileTee::new(&file)?;
        unpack(Box::new(read), &output)
    };

    match attempt {
        Ok(Status::Done) => Ok(()),
        Ok(Status::Rewind) => output_raw(&mut file, &output),
        Err(e) => Err(e)
    }
}

fn real_main() -> u8 {

    let matches = App::new("contentin")
                    .arg(Arg::with_name("to-tar")
                         .long("to-tar")
                         .help("emit a tar file")
                         .required(true))
                    .arg(Arg::with_name("INPUT")
                         .required(true)
                         .multiple(true))
                    .get_matches();

    for path in matches.values_of("INPUT").unwrap() {
        if let Err(e) = process_real_path(path) {
            if let Err(_) = writeln!(io::stderr(), "fatal: processing '{}': {}", path, e) {
                return 6;
            }

            return 4;
        }
    }
    return 0;
}

fn main() {
    std::process::exit(real_main() as i32)
}
<|endoftext|>


<commit_before>#[macro_use(handler)]
extern crate chatbot;
extern crate getopts;

use std::env;

use chatbot::Chatbot;
use chatbot::adapter::CliAdapter;
use chatbot::adapter::SlackAdapter;
use chatbot::handler::GithubIssueLinker;

use getopts::Options;
use getopts::ParsingStyle;

#[allow(dead_code)]
fn main() {

    let args = env::args().collect::<Vec<String>>();
    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::StopAtFirstFree);
    opts.optopt("a", "adapter", "Chat Adapter to use", "slack|cli");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };

    let adapter_name = matches.opt_str("a").unwrap_or("cli".to_owned());

    let mut bot = Chatbot::new();

    // Add adapter based on command line argument
    match adapter_name.as_ref() {
        "slack" => bot.add_adapter(SlackAdapter::new()),
        "cli" => bot.add_adapter(CliAdapter::new()),
        _ => panic!("Unexpected adapter name. Use 'cli' or 'slack'.")
    };

    let ping = handler!("PingHandler", r"ping", |_, _| { Some("pong".to_owned()) });

    let robot_name = "Mr. T";
    let trout = handler!("TroutSlap", r"slap (?P<user>.+)", move |matches, _| {
        match matches.name("user") {
            Some(user) => {
                Some(format!("{} slaps {} around a bit with a large trout",
                             robot_name, user))
            },
            None => None
        }
    });

    bot.add_handler(ping);
    bot.add_handler(trout);
    bot.add_handler(GithubIssueLinker::new());

    bot.run();
}
<commit_msg>Add echo handler to test executable<commit_after>#[macro_use(handler)]
extern crate chatbot;
extern crate getopts;

use std::env;

use chatbot::Chatbot;
use chatbot::adapter::CliAdapter;
use chatbot::adapter::SlackAdapter;
use chatbot::handler::GithubIssueLinker;

use getopts::Options;
use getopts::ParsingStyle;

#[allow(dead_code)]
fn main() {

    let args = env::args().collect::<Vec<String>>();
    let mut opts = Options::new();
    opts.parsing_style(ParsingStyle::StopAtFirstFree);
    opts.optopt("a", "adapter", "Chat Adapter to use", "slack|cli");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(f) => panic!(f.to_string())
    };

    let adapter_name = matches.opt_str("a").unwrap_or("cli".to_owned());

    let mut bot = Chatbot::new();

    // Add adapter based on command line argument
    match adapter_name.as_ref() {
        "slack" => bot.add_adapter(SlackAdapter::new()),
        "cli" => bot.add_adapter(CliAdapter::new()),
        _ => panic!("Unexpected adapter name. Use 'cli' or 'slack'.")
    };

    let ping = handler!("PingHandler", r"ping", |_, _| { Some("pong".to_owned()) });

    let robot_name = "Mr. T";
    let trout = handler!("TroutSlap", r"slap (?P<user>.+)", move |matches, _| {
        match matches.name("user") {
            Some(user) => {
                Some(format!("{} slaps {} around a bit with a large trout",
                             robot_name, user))
            },
            None => None
        }
    });

    let echo = handler!("EchoHandler", r"echo (?P<msg>.+)", |matches, _| {
        matches.name("msg").map(|msg| { msg.to_owned() })
    });

    bot.add_handler(ping);
    bot.add_handler(trout);
    bot.add_handler(echo);
    bot.add_handler(GithubIssueLinker::new());

    bot.run();
}
<|endoftext|>


<commit_before>#[derive(Clone, Copy, Debug)]
enum PBType {
    Integer64,
    String,
}

#[derive(Clone, Debug)]
struct PBField {
    name: String,
    tag: u8,
    field_type: PBType,
}

#[derive(Clone, Debug)]
struct PBMessage {
    name: String,
    fields: Vec<PBField>,
}

#[derive(Clone, Copy, Debug)]
enum PBWireType {
    VarInt,
    Fixed64,
    LengthDelim,
    Fixed32,
    Reserved,
}

struct TagValue<'a> {
    tag: u8,
    wire_type: PBWireType,
    value: &'a [u8],
}

#[derive(Clone)]
struct ParserPosition<'a> {
    slice: &'a [u8],
    tag: u8,
    wire_type: PBWireType,
}

trait InitParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a>;
}

impl<'a> InitParserPosition<'a> for ParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a> {
        ParserPosition {
            slice: msg_slice,
            tag: 0,
            wire_type: PBWireType::Reserved,
        }
    }
}

static IDX2WIRE_TYPE: &'static [PBWireType; 6] = &[PBWireType::VarInt,
                                                   PBWireType::Fixed64,
                                                   PBWireType::LengthDelim,
                                                   PBWireType::Reserved,
                                                   PBWireType::Reserved,
                                                   PBWireType::Fixed32];
#[derive(Clone, Copy, Debug)]
enum PBParseError {
    UnknownWireType(usize),
    BadVarInt,
}

fn PBParseNext<'a>(pos: ParserPosition<'a>)
                   -> Result<(TagValue<'a>, ParserPosition<'a>), PBParseError> {
    let mut p = pos.clone();

    p.tag = p.slice[0] >> 3;
    let wire_type_idx = (p.slice[0] & 7) as usize;
    if wire_type_idx >= IDX2WIRE_TYPE.len() {
        return Err(PBParseError::UnknownWireType(wire_type_idx));
    }
    p.wire_type = IDX2WIRE_TYPE[wire_type_idx];

    let mut value = match p.wire_type {
        PBWireType::LengthDelim => {
            let offset = p.slice[1] as usize;
            let start = 2;
            let end = 2 + offset;
            let x = &p.slice[start..end];
            p.slice = &p.slice[end..];
            x
        }
        PBWireType::VarInt => {
            let mut end: Option<usize> = None;
            for i in 1..p.slice.len() {
                if p.slice[i] >> 7 != 1 {
                    end = Some(i + 1);
                }
            }
            match end {
                Some(end_idx) => {
                    let x = &p.slice[1..end_idx];
                    p.slice = &p.slice[end_idx..];
                    x
                }
                None => return Err(PBParseError::BadVarInt),
            }

        }
        _ => &[],
    };
    Ok((TagValue {
            value: value,
            tag: p.tag,
            wire_type: p.wire_type,
        },
        p.clone()))
}

use std::str;

#[derive(Debug)]
enum ReprError {
    InvalidWireAndSchemaType,
    Utf8Error(std::str::Utf8Error),
}

fn StringRepr<'a>(wire_type: PBWireType,
                  schema_type: PBType,
                  bytes: &'a [u8])
                  -> Result<String, ReprError> {
    match (wire_type, schema_type) {
        (PBWireType::LengthDelim, PBType::String) => {
            let x = str::from_utf8(bytes);
            match x {
                Ok(y) => Ok(String::from(y)),
                Err(y) => Err(ReprError::Utf8Error(y)),
            }
        }
        (PBWireType::VarInt, PBType::Integer64) => {
            match IntegerRepr(wire_type, schema_type, bytes) {
                Ok(i) => Ok(String::from(format!("{}", i))),
                Err(e) => Err(e),
            }
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn IntegerRepr<'a>(wire_type: PBWireType,
                   schema_type: PBType,
                   bytes: &'a [u8])
                   -> Result<i64, ReprError> {

    extern crate num;
    match (wire_type, schema_type) {
        (PBWireType::VarInt, PBType::Integer64) => {
            let mut pos: u8 = 0;
            let mut res: i64 = 0;
            for b in bytes {
                // I'm assuming I have an endieness problem here?
                for i in 0..7 {
                    let bit_shift = 6 - i;
                    let pow_add = pos + 6 - i;
                    if ((b >> (6 - i)) & 1u8) == 1u8 {
                        res += num::pow(2, (pow_add) as usize);
                    }
                }
                pos += 7;
            }
            Ok(res)
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn main() {
    /*
     * Sample ProtocolBuffer message for schema:
     *
     *     message Test2 {
     *       required string a = 1;
     *       required string b = 2;
     *       required int c = 3
     *     }
     *
     * with a = test, b = testing
     *
     * @see https://developers.google.com/protocol-buffers/docs/encoding
     */
    let msg = vec![0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x0a, 0x04, 0x74, 0x65,
                   0x73, 0x74, 0x18, 0xac, 0x02];
    let msg_slice = msg.as_slice();

    let (mut x, mut pp) = PBParseNext(ParserPosition::init(msg_slice)).unwrap();

    print!("Tag   = {} {:?}\n", x.tag, x.wire_type);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    let (x, pp) = PBParseNext(pp).unwrap();

    print!("Tag   = {} {:?}\n", x.tag, x.wire_type);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    let (x, pp) = PBParseNext(pp).unwrap();

    print!("Tag   = {} {:?}\n", x.tag, x.wire_type);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::Integer64, x.value).unwrap());

}
<commit_msg>Move wiretype parser to own function<commit_after>#[derive(Clone, Copy, Debug)]
enum PBType {
    Integer64,
    String,
}

#[derive(Clone, Debug)]
struct PBField {
    name: String,
    tag: u8,
    field_type: PBType,
}

#[derive(Clone, Debug)]
struct PBMessage {
    name: String,
    fields: Vec<PBField>,
}

#[derive(Clone, Copy, Debug)]
enum PBWireType {
    VarInt,
    Fixed64,
    LengthDelim,
    Fixed32,
    Reserved,
}

struct TagValue<'a> {
    tag: u8,
    wire_type: PBWireType,
    value: &'a [u8],
}

#[derive(Clone)]
struct ParserPosition<'a> {
    slice: &'a [u8],
}

trait InitParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a>;
}

impl<'a> InitParserPosition<'a> for ParserPosition<'a> {
    fn init(msg_slice: &'a [u8]) -> ParserPosition<'a> {
        ParserPosition { slice: msg_slice }
    }
}

static IDX2WIRE_TYPE: &'static [PBWireType; 6] = &[PBWireType::VarInt,
                                                   PBWireType::Fixed64,
                                                   PBWireType::LengthDelim,
                                                   PBWireType::Reserved,
                                                   PBWireType::Reserved,
                                                   PBWireType::Fixed32];
#[derive(Clone, Copy, Debug)]
enum PBParseError {
    UnknownWireType(usize),
    BadVarInt,
    WireTypeNotHandled,
}

fn ParseWireType<'a>(wire_type: PBWireType,
                     pos: ParserPosition<'a>)
                     -> Result<(&'a [u8], ParserPosition<'a>), PBParseError> {
    let mut p = pos.clone();
    match wire_type {
        PBWireType::LengthDelim => {
            let offset = p.slice[1] as usize;
            let start = 2;
            let end = 2 + offset;
            let x = &p.slice[start..end];
            p.slice = &p.slice[end..];
            Ok((x, p))
        }
        PBWireType::VarInt => {
            let mut end: Option<usize> = None;
            for i in 1..p.slice.len() {
                if p.slice[i] >> 7 != 1 {
                    end = Some(i + 1);
                }
            }
            match end {
                Some(end_idx) => {
                    let x = &p.slice[1..end_idx];
                    p.slice = &p.slice[end_idx..];
                    Ok((x, p))
                }
                None => Err(PBParseError::BadVarInt),
            }

        }
        _ => Err(PBParseError::WireTypeNotHandled),
    }
}

fn PBParseNext<'a>(pos: ParserPosition<'a>)
                   -> Result<(TagValue<'a>, ParserPosition<'a>), PBParseError> {
    let mut p = pos.clone();

    let tag = p.slice[0] >> 3;
    let wire_type_idx = (p.slice[0] & 7) as usize;
    if wire_type_idx >= IDX2WIRE_TYPE.len() {
        return Err(PBParseError::UnknownWireType(wire_type_idx));
    }
    let wire_type = IDX2WIRE_TYPE[wire_type_idx];
    let (value, p) = try!(ParseWireType(wire_type, p));
    Ok((TagValue {
            value: value,
            tag: tag,
            wire_type: wire_type,
        },
        p.clone()))
}

use std::str;

#[derive(Debug)]
enum ReprError {
    InvalidWireAndSchemaType,
    Utf8Error(std::str::Utf8Error),
}

fn StringRepr<'a>(wire_type: PBWireType,
                  schema_type: PBType,
                  bytes: &'a [u8])
                  -> Result<String, ReprError> {
    match (wire_type, schema_type) {
        (PBWireType::LengthDelim, PBType::String) => {
            let x = str::from_utf8(bytes);
            match x {
                Ok(y) => Ok(String::from(y)),
                Err(y) => Err(ReprError::Utf8Error(y)),
            }
        }
        (PBWireType::VarInt, PBType::Integer64) => {
            match IntegerRepr(wire_type, schema_type, bytes) {
                Ok(i) => Ok(String::from(format!("{}", i))),
                Err(e) => Err(e),
            }
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn IntegerRepr<'a>(wire_type: PBWireType,
                   schema_type: PBType,
                   bytes: &'a [u8])
                   -> Result<i64, ReprError> {

    extern crate num;
    match (wire_type, schema_type) {
        (PBWireType::VarInt, PBType::Integer64) => {
            let mut pos: u8 = 0;
            let mut res: i64 = 0;
            for b in bytes {
                // I'm assuming I have an endieness problem here?
                for i in 0..7 {
                    let bit_shift = 6 - i;
                    let pow_add = pos + 6 - i;
                    if ((b >> (6 - i)) & 1u8) == 1u8 {
                        res += num::pow(2, (pow_add) as usize);
                    }
                }
                pos += 7;
            }
            Ok(res)
        }
        _ => Err(ReprError::InvalidWireAndSchemaType),
    }
}

fn main() {
    /*
     * Sample ProtocolBuffer message for schema:
     *
     *     message Test2 {
     *       required string a = 1;
     *       required string b = 2;
     *       required int c = 3
     *     }
     *
     * with a = test, b = testing
     *
     * @see https://developers.google.com/protocol-buffers/docs/encoding
     */
    let msg = vec![0x12, 0x07, 0x74, 0x65, 0x73, 0x74, 0x69, 0x6e, 0x67, 0x0a, 0x04, 0x74, 0x65,
                   0x73, 0x74, 0x18, 0xac, 0x02];
    let msg_slice = msg.as_slice();

    let (mut x, mut pp) = PBParseNext(ParserPosition::init(msg_slice)).unwrap();

    print!("Tag   = {} {:?}\n", x.tag, x.wire_type);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    let (x, pp) = PBParseNext(pp).unwrap();

    print!("Tag   = {} {:?}\n", x.tag, x.wire_type);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::String, x.value).unwrap());

    let (x, pp) = PBParseNext(pp).unwrap();

    print!("Tag   = {} {:?}\n", x.tag, x.wire_type);
    print!("Value = {}\n",
           StringRepr(x.wire_type, PBType::Integer64, x.value).unwrap());

}
<|endoftext|>


<commit_before>use std::io;
use std::io::Write;

fn main() {
    let prompt = ":";
    loop {
        print!("{}", prompt);
        io::stdout().flush().unwrap();
        let mut line = String::new();
        match io::stdin().read_line(&mut line) {
            Ok(_) => (),
            Err(_) => {
                println!("?");
                continue
            }
        }
        println!("You typed in {}", line.trim());
    }
}
<commit_msg>Allocate a new line for everytime through the loop<commit_after>use std::io;

enum Mode {
    Command,
    Input,
}

fn command_line(line: &String, mode: &mut Mode) {
    match line.trim() {
        "a" => *mode = Mode::Input,
        "q" => std::process::exit(0),
        _ => println!("Command: {}", line),
    }
}

fn input_line(line: &String, mode: &mut Mode) {
    match line.trim() {
        "." => *mode = Mode::Command,
        _ => println!("Input: {}", line),
    }
}

fn main() {
    let mut mode = Mode::Command;
    loop {
        let mut line = String::new();
        match io::stdin().read_line(&mut line) {
            Ok(_) => (),
            Err(_) => {
                println!("?");
                continue
            },
        }
        match mode {
            Mode::Command => command_line(&line, &mut mode),
            Mode::Input => input_line(&line, &mut mode),
        }
    }
}
<|endoftext|>


<commit_before>extern crate rustwlc;
#[macro_use]
extern crate wayland_client;

extern crate tempfile;

extern crate byteorder;
extern crate image;

use std::env;
use std::process::exit;
use std::io::{BufReader};
use image::{load, ImageFormat};
//use gdk_sys;

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 {
        println!("Please supply either a file path or a color (written in hex)");
        exit(1);
    }
    let input = &args[1];
    // TODO Don't hard code this, get it from the server...
    let output = unsafe {rustwlc::WlcOutput::dummy(0)};
    if let Ok(color) = input.parse::<u32>() {
        let color = Color::from_u32(color);
        generate_solid_background(color, output);
    } else {
        generate_image_background(input.clone(), output);
    }
}

use std::mem::transmute;
use std::os::unix::io::AsRawFd;
use std::io::{Read, Write};
use std::fs::File;
use std::path::PathBuf;

//use gdk_pixbuf::{Pixbuf, InterpType};

use wayland_client::wayland::get_display;
use wayland_client::wayland::compositor::{WlCompositor, WlSurface};
use wayland_client::wayland::shell::{WlShellSurface, WlShell};
use wayland_client::wayland::shm::{WlBuffer, WlShm, WlShmFormat};
use wayland_client::wayland::seat::{WlSeat, WlPointerEvent};
use wayland_client::cursor::load_theme;
use wayland_client::{EventIterator, Proxy};

use rustwlc::WlcOutput;

use byteorder::{NativeEndian, WriteBytesExt};

wayland_env!(WaylandEnv,
             compositor: WlCompositor,
             shell: WlShell,
             shm: WlShm,
             seat: WlSeat
);

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
/// Holds the bytes to represent a colored background.
/// To be written into a wayland surface.
pub struct Color(pub [u8; 4]);

impl Color {
    /// Generate a new color out of a u32.
    /// E.G: 0xFFFFFF
    pub fn from_u32(color: u32) -> Self {
        unsafe { Color(transmute(color)) }
    }

    pub fn as_u32(&self) -> u32 {
        unsafe { transmute(self.0)}
    }
}

/// Given a solid color, writes bytes associated with that color to
/// a special Wayland surface which is then rendered as a background for Way Cooler.
pub fn generate_solid_background(color: Color, _output: WlcOutput) {
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    let mut tmp = tempfile::tempfile().ok().expect("Unable to create a tempfile.");

    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...
    //let resolution = output.get_resolution()
    //    .expect("Couldn't get output resolution");
    //let (width, height) = (resolution.w as i32, resolution.h as i32);
    let width = 800; let height = 600;
    let size = (width * height) as i32;

    // Write in the color coding to the surface
    for _ in 0..size {
        unsafe {
            tmp.write_u32::<NativeEndian>(transmute(color.0))
                .expect("Could not write to file")
        }
    }
    tmp.flush()
        .expect("Could not flush buffer");

    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(tmp.as_raw_fd(), size);
    let buffer = pool.create_buffer(0, width, height, width, WlShmFormat::Argb8888);
    // Tell Way Cooler not to set put this in the tree, treat as background
    shell_surface.set_class("Background".into());
    shell_surface.set_title(format!("0x{:x}", color.as_u32()));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);

    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
}

fn weird_math(num: u8, third_num: u32) -> u8 {
    let big_num = num as u32;
    ((big_num * third_num) / 255) as u8
}

#[test]
fn test_weird_math() {
    assert_eq!(weird_math(10, 254), 9);
    assert_eq!(weird_math(2, 255), 2);
    assert_eq!(weird_math(255, 500), 500);
}

/// Given the data from an image, writes it to a special Wayland surface
/// which is then rendered as a background for Way Cooler.
pub fn generate_image_background(path: String, _output: WlcOutput) {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let image_reader = BufReader::new(image_file);
    let image = load(image_reader, ImageFormat::PNG)
        .expect("Image was not a png file!");
    let mut image = image.to_rgba();
    let width = image.width();
    let height = image.height();
    let size = width * height;
    // TODO Split this into its own function
    let mut out_file = File::create("out.bin").unwrap();
    {
        let pixels = image.enumerate_pixels_mut();
        for (_x, _y, pixel) in pixels {
            let alpha = pixel[3] as u32;
            pixel[0] = weird_math(pixel[0], alpha);
            pixel[1] = weird_math(pixel[1], alpha);
            pixel[2] = weird_math(pixel[2], alpha);

            let tmp = pixel[2];
            pixel[2] = pixel[0];
            pixel[0] = tmp;
        }
    }
    let vec = image.into_vec();
    out_file.write_all(&*vec).unwrap();
    let tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    tmp.set_len(size as u64).expect("Could not truncate length of file");

/*    //let pix_buf = Pixbuf::new_from_file(path.as_str())
    //    .expect("Could not read the file");
    //let image_file = File::open(path.clone())
    let image_file = ::std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create_new(false)
        .open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let raw_fd = image_file.as_raw_fd();
    let map = Mmap::open(&image_file, memmap::Protection::ReadWrite)
        .expect("Could not memory map the temp file");
    let background_surface = ImageSurface::create_from_png(image_file)
        .expect("could not create surface from png");
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...

    let width = background_surface.get_width();
    let height = background_surface.get_height();
    let stride = background_surface.get_stride();
    let size: i32 = stride * height;
    println!("Size: {}, mmap len: {}", size, map.len());
    //let size = map.len() as i32;
    //let tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    //tmp.set_len(size as u64).expect("Could not truncate length of file");
    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(raw_fd, size);
    let buffer = pool.create_buffer(0, width, height, stride, WlShmFormat::Argb8888);
    // Tell Way Cooler not to put this in the tree, treat as background
    // TODO Make this less hacky by actually giving way cooler access to this thing...
    shell_surface.set_class("Background".into());
    // TODO Actually give it the path or something idk
    shell_surface.set_title(format!("Image background yay"));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);
    surface.commit();
    surface.set_buffer_scale(1);
    surface.damage(0, 0, width, height);

    println!("{:?}", unsafe{map.as_slice()});
    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
    */
}

#[allow(dead_code)]
fn read_image_data(path: PathBuf) -> Vec<u32> {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    // Most common screen size
    let mut buffer: Vec<u32> = Vec::with_capacity(1366 * 768);
    // TODO, while clever looking, is Ok(byte) here correct?
    let mut bytes = image_file.bytes();
    'outer: loop {
        let result: u32;
        let mut data_chunk: [u8; 4] = [0; 4];
        for i in 0..3 {
            if let Some(Ok(next_chunk)) = bytes.next() {
                data_chunk[i] = next_chunk;
            } else {
                break 'outer;
            }
        }
        /*let data_chunk: Vec<u8> = next_chunk
            .flat_map(|maybe_bytes| maybe_bytes.into_iter())
            .collect();*/
        result = unsafe {::std::mem::transmute(data_chunk)};
        println!("{:X}", result);
        buffer.push(result);
    }
    buffer
}

/// Main loop for rendering backgrounds.
/// Need to keep the surface alive, and update it if the
/// user wants to change the background.
#[allow(unused_variables)]
fn main_background_loop(compositor: &WlCompositor, shell: &WlShell, shm: &WlShm,
                        seat: &WlSeat,
                        surface: WlSurface, shell_surface: WlShellSurface,
                        buffer: WlBuffer, mut event_iter: EventIterator) {
    use wayland_client::wayland::WaylandProtocolEvent;
    use wayland_client::Event;
    println!("Entering main loop");
    // For now just loop and do nothing
    // Eventually need to query the background state and update
    let cursor_surface = compositor.create_surface();
    let mut pointer = seat.get_pointer();
    let cursor_theme = load_theme(None, 16, shm);
    let maybe_cursor = cursor_theme.get_cursor("default");
    // TODO Uncomment
    /*if maybe_cursor.is_none() {
        println!("Could not load cursor theme properly, cannot load background");
        println!("Please consult the developers about this issue with your distro version, this is a known issue");
        return;
    }
    let cursor = maybe_cursor.unwrap();
    let cursor_buffer = cursor.frame_buffer(0).expect("Couldn't get frame_buffer");
    cursor_surface.attach(Some(&*cursor_buffer), 0, 0);
     */
    pointer.set_event_iterator(&event_iter);
    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
    surface.commit();
    event_iter.sync_roundtrip().unwrap();
    loop {
        for event in &mut event_iter {
            match event {
                Event::Wayland(wayland_event) => {
                    match wayland_event {
                        WaylandProtocolEvent::WlPointer(id, pointer_event) => {
                            match pointer_event {
                                WlPointerEvent::Enter(serial, surface, surface_x, surface_y) => {
                                    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
                                },
                                _ => {
                                }
                            }
                        },
                        _ => {/* unhandled events */}
                    }
                }
                _ => { /* unhandled events */ }
            }
        }
        event_iter.dispatch().expect("Connection with the compositor was lost.");
    }
}
<commit_msg>Made background self sufficient. It works!<commit_after>extern crate rustwlc;
#[macro_use]
extern crate wayland_client;

extern crate tempfile;

extern crate byteorder;
extern crate image;

use std::env;
use std::process::exit;
use std::io::{BufReader};
use image::{load, ImageFormat};
//use gdk_sys;

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 {
        println!("Please supply either a file path or a color (written in hex)");
        exit(1);
    }
    let input = &args[1];
    // TODO Don't hard code this, get it from the server...
    let output = unsafe {rustwlc::WlcOutput::dummy(0)};
    if let Ok(color) = input.parse::<u32>() {
        let color = Color::from_u32(color);
        generate_solid_background(color, output);
    } else {
        generate_image_background(input.clone(), output);
    }
}

use std::mem::transmute;
use std::os::unix::io::AsRawFd;
use std::io::{Read, Write};
use std::fs::File;
use std::path::PathBuf;

//use gdk_pixbuf::{Pixbuf, InterpType};

use wayland_client::wayland::get_display;
use wayland_client::wayland::compositor::{WlCompositor, WlSurface};
use wayland_client::wayland::shell::{WlShellSurface, WlShell};
use wayland_client::wayland::shm::{WlBuffer, WlShm, WlShmFormat};
use wayland_client::wayland::seat::{WlSeat, WlPointerEvent};
use wayland_client::cursor::load_theme;
use wayland_client::{EventIterator, Proxy};

use rustwlc::WlcOutput;

use byteorder::{NativeEndian, WriteBytesExt};

wayland_env!(WaylandEnv,
             compositor: WlCompositor,
             shell: WlShell,
             shm: WlShm,
             seat: WlSeat
);

#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
/// Holds the bytes to represent a colored background.
/// To be written into a wayland surface.
pub struct Color(pub [u8; 4]);

impl Color {
    /// Generate a new color out of a u32.
    /// E.G: 0xFFFFFF
    pub fn from_u32(color: u32) -> Self {
        unsafe { Color(transmute(color)) }
    }

    pub fn as_u32(&self) -> u32 {
        unsafe { transmute(self.0)}
    }
}

/// Given a solid color, writes bytes associated with that color to
/// a special Wayland surface which is then rendered as a background for Way Cooler.
pub fn generate_solid_background(color: Color, _output: WlcOutput) {
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    let mut tmp = tempfile::tempfile().ok().expect("Unable to create a tempfile.");

    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...
    //let resolution = output.get_resolution()
    //    .expect("Couldn't get output resolution");
    //let (width, height) = (resolution.w as i32, resolution.h as i32);
    let width = 800; let height = 600;
    let size = (width * height) as i32;

    // Write in the color coding to the surface
    for _ in 0..size {
        unsafe {
            tmp.write_u32::<NativeEndian>(transmute(color.0))
                .expect("Could not write to file")
        }
    }
    tmp.flush()
        .expect("Could not flush buffer");

    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(tmp.as_raw_fd(), size);
    let buffer = pool.create_buffer(0, width, height, width, WlShmFormat::Argb8888);
    // Tell Way Cooler not to set put this in the tree, treat as background
    shell_surface.set_class("Background".into());
    shell_surface.set_title(format!("0x{:x}", color.as_u32()));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);

    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
}

fn weird_math(num: u8, third_num: u32) -> u8 {
    let big_num = num as u32;
    ((big_num * third_num) / 255) as u8
}

#[test]
fn test_weird_math() {
    assert_eq!(weird_math(10, 254), 9);
    assert_eq!(weird_math(2, 255), 2);
    assert_eq!(weird_math(255, 500), 500);
}

/// Given the data from an image, writes it to a special Wayland surface
/// which is then rendered as a background for Way Cooler.
pub fn generate_image_background(path: String, _output: WlcOutput) {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let image_reader = BufReader::new(image_file);
    let image = load(image_reader, ImageFormat::PNG)
        .expect("Image was not a png file!");
    let mut image = image.to_rgba();
    let width = image.width();
    let height = image.height();
    let stride = width * 4;
    let size = stride * height;
    // TODO Split this into its own function
    {
        let pixels = image.enumerate_pixels_mut();
        for (_x, _y, pixel) in pixels {
            let alpha = pixel[3] as u32;
            pixel[0] = weird_math(pixel[0], alpha);
            pixel[1] = weird_math(pixel[1], alpha);
            pixel[2] = weird_math(pixel[2], alpha);

            let tmp = pixel[2];
            pixel[2] = pixel[0];
            pixel[0] = tmp;
        }
    }
    let vec = image.into_vec();
    let mut tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    tmp.set_len(size as u64).expect("Could not truncate length of file");
    tmp.write_all(&*vec).unwrap();

    // TODO I want this shit outta here
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);

    let pool = shm.create_pool(tmp.as_raw_fd(), size as i32);
    let buffer = pool.create_buffer(0, width as i32, height as i32, stride as i32, WlShmFormat::Argb8888);
    // Tell Way Cooler not to put this in the tree, treat as background
    // TODO Make this less hacky by actually giving way cooler access to this thing...
    shell_surface.set_class("Background".into());
    // TODO Actually give it the path or something idk
    shell_surface.set_title(format!("Image background yay"));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);
    surface.commit();
    surface.set_buffer_scale(1);
    surface.damage(0, 0, width as i32, height as i32);
    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);

/*    //let pix_buf = Pixbuf::new_from_file(path.as_str())
    //    .expect("Could not read the file");
    //let image_file = File::open(path.clone())
    let image_file = ::std::fs::OpenOptions::new()
        .read(true)
        .write(true)
        .create_new(false)
        .open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    let raw_fd = image_file.as_raw_fd();
    let map = Mmap::open(&image_file, memmap::Protection::ReadWrite)
        .expect("Could not memory map the temp file");
    let background_surface = ImageSurface::create_from_png(image_file)
        .expect("could not create surface from png");
    // Get shortcuts to the globals.
    let (display, iter) = get_display()
        .expect("Unable to connect to a wayland compositor");
    let (env, evt_iter) = WaylandEnv::init(display, iter);
    let compositor = env.compositor.as_ref().map(|o| &o.0).unwrap();
    let shell = env.shell.as_ref().map(|o| &o.0).unwrap();
    let shm = env.shm.as_ref().map(|o| &o.0).unwrap();
    let seat = env.seat.as_ref().map(|o| &o.0).unwrap();

    // Create the surface we are going to write into
    let surface = compositor.create_surface();
    let shell_surface = shell.get_shell_surface(&surface);
    // Calculate how big the buffer needs to be from the output resolution
    // TODO Get the output size from way cooler somehow...

    let width = background_surface.get_width();
    let height = background_surface.get_height();
    let stride = background_surface.get_stride();
    let size: i32 = stride * height;
    println!("Size: {}, mmap len: {}", size, map.len());
    //let size = map.len() as i32;
    //let tmp = tempfile::NamedTempFile::new().expect("Unable to create a tempfile.");
    //tmp.set_len(size as u64).expect("Could not truncate length of file");
    // Create the buffer that is mem-mapped to the temp file descriptor
    let pool = shm.create_pool(raw_fd, size);
    let buffer = pool.create_buffer(0, width, height, stride, WlShmFormat::Argb8888);
    // Tell Way Cooler not to put this in the tree, treat as background
    // TODO Make this less hacky by actually giving way cooler access to this thing...
    shell_surface.set_class("Background".into());
    // TODO Actually give it the path or something idk
    shell_surface.set_title(format!("Image background yay"));

    // Attach the buffer to the surface
    surface.attach(Some(&buffer), 0, 0);
    surface.commit();
    surface.set_buffer_scale(1);
    surface.damage(0, 0, width, height);

    println!("{:?}", unsafe{map.as_slice()});
    main_background_loop(compositor, shell, shm, seat, surface,
                         shell_surface, buffer, evt_iter);
    */
}

#[allow(dead_code)]
fn read_image_data(path: PathBuf) -> Vec<u32> {
    let image_file = File::open(path.clone())
        .unwrap_or_else(|_| {
            println!("Could not open \"{:?}\"", path);
            panic!("Could not open image file");
        });
    // Most common screen size
    let mut buffer: Vec<u32> = Vec::with_capacity(1366 * 768);
    // TODO, while clever looking, is Ok(byte) here correct?
    let mut bytes = image_file.bytes();
    'outer: loop {
        let result: u32;
        let mut data_chunk: [u8; 4] = [0; 4];
        for i in 0..3 {
            if let Some(Ok(next_chunk)) = bytes.next() {
                data_chunk[i] = next_chunk;
            } else {
                break 'outer;
            }
        }
        /*let data_chunk: Vec<u8> = next_chunk
            .flat_map(|maybe_bytes| maybe_bytes.into_iter())
            .collect();*/
        result = unsafe {::std::mem::transmute(data_chunk)};
        println!("{:X}", result);
        buffer.push(result);
    }
    buffer
}

/// Main loop for rendering backgrounds.
/// Need to keep the surface alive, and update it if the
/// user wants to change the background.
#[allow(unused_variables)]
fn main_background_loop(compositor: &WlCompositor, shell: &WlShell, shm: &WlShm,
                        seat: &WlSeat,
                        surface: WlSurface, shell_surface: WlShellSurface,
                        buffer: WlBuffer, mut event_iter: EventIterator) {
    use wayland_client::wayland::WaylandProtocolEvent;
    use wayland_client::Event;
    println!("Entering main loop");
    // For now just loop and do nothing
    // Eventually need to query the background state and update
    let cursor_surface = compositor.create_surface();
    let mut pointer = seat.get_pointer();
    let cursor_theme = load_theme(None, 16, shm);
    let maybe_cursor = cursor_theme.get_cursor("default");
    // TODO Uncomment
    /*if maybe_cursor.is_none() {
        println!("Could not load cursor theme properly, cannot load background");
        println!("Please consult the developers about this issue with your distro version, this is a known issue");
        return;
    }
    let cursor = maybe_cursor.unwrap();
    let cursor_buffer = cursor.frame_buffer(0).expect("Couldn't get frame_buffer");
    cursor_surface.attach(Some(&*cursor_buffer), 0, 0);
     */
    pointer.set_event_iterator(&event_iter);
    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
    surface.commit();
    event_iter.sync_roundtrip().unwrap();
    loop {
        for event in &mut event_iter {
            match event {
                Event::Wayland(wayland_event) => {
                    match wayland_event {
                        WaylandProtocolEvent::WlPointer(id, pointer_event) => {
                            match pointer_event {
                                WlPointerEvent::Enter(serial, surface, surface_x, surface_y) => {
                                    pointer.set_cursor(0, Some(&cursor_surface), 0, 0);
                                },
                                _ => {
                                }
                            }
                        },
                        _ => {/* unhandled events */}
                    }
                }
                _ => { /* unhandled events */ }
            }
        }
        event_iter.dispatch().expect("Connection with the compositor was lost.");
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::os::linux::fs::MetadataExt;

use clap::{App, Arg, ArgMatches};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(Arg::with_name("summary").short("s").long("summary").help(
            "Print out summary information after duplicates",
        ))
        .get_matches()
}

fn collect_paths(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut length_paths = HashMap::new();
    let spinner = ["|", "/", "-", "\\"];

    for (idx, entry) in WalkDir::new(path).into_iter().enumerate() {
        eprint!("\rBuilding file list {}", spinner[idx % spinner.len()]);
        let entry = match entry {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = match entry.metadata() {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        length_paths
            .entry(metadata.len())
            .or_insert(Vec::new())
            .push(path);
    }
    eprint!("\r");
    length_paths
}

fn main() {
    let matches = parse_args();

    let mut stats: Stats = Default::default();

    let files = collect_paths(matches.value_of("DIR").unwrap(), &mut stats);

    for f_paths in files.values().filter(|x| x.len() > 1) {

        // Avoid hardlinks to same inode
        let mut hardlinks = Vec::new();
        let mut di = HashSet::new();
        for f_path in f_paths {
            let metadata = match f_path.metadata() {
                Ok(m) => m,
                Err(err) => {
                    eprintln!("dupr: {}", err);
                    continue;
                }
            };
            let dev_inode = (metadata.st_dev(), metadata.st_ino());
            if !di.contains(&dev_inode) {
                hardlinks.push(f_path);
                di.insert(dev_inode);
            }
        }

        let mut hashes = HashMap::new();
        for f_path in hardlinks {
            hashes
                .entry(hash_file(&f_path))
                .or_insert(Vec::new())
                .push(f_path);
        }

        for (hash, paths) in hashes.iter().filter(|&(_, v)| v.len() > 1) {
            stats.duplicate_count += paths.len() as u64;
            for p in paths {
                println!("{:016x} - {}", hash, p.display());
            }
        }
    }

    if matches.is_present("summary") {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<commit_msg>Clean up hardlink detection<commit_after>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::collections::HashSet;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::os::linux::fs::MetadataExt;

use clap::{App, Arg, ArgMatches};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(Arg::with_name("summary").short("s").long("summary").help(
            "Print out summary information after duplicates",
        ))
        .get_matches()
}

fn collect_paths(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut length_paths = HashMap::new();
    let spinner = ["|", "/", "-", "\\"];

    for (idx, entry) in WalkDir::new(path).into_iter().enumerate() {
        eprint!("\rBuilding file list {}", spinner[idx % spinner.len()]);
        let entry = match entry {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = match entry.metadata() {
            Ok(e) => e,
            Err(err) => {
                eprintln!("dupr: {}", err);
                continue;
            }
        };

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        length_paths
            .entry(metadata.len())
            .or_insert(Vec::new())
            .push(path);
    }
    eprint!("\r");
    length_paths
}

fn main() {
    let matches = parse_args();

    let mut stats: Stats = Default::default();

    let files = collect_paths(matches.value_of("DIR").unwrap(), &mut stats);

    let mut len_hash_path = HashMap::new();

    // iterate over length buckets when no. files > 1
    for (len, paths) in files.iter().filter(|&(_, v)| v.len() > 1) {

        let mut hardlinks = HashSet::new();

        for path in paths {
            // only save if not a hardlink to existing file
            let metadata = match path.metadata() {
                Ok(m) => m,
                Err(err) => {
                    eprintln!("dupr: {}", err);
                    continue;
                }
            };

            let dev_inode = (metadata.st_dev(), metadata.st_ino());

            if hardlinks.contains(&dev_inode) {
                continue;
            }

            hardlinks.insert(dev_inode);

            len_hash_path
                .entry((len, hash_file(&path)))
                .or_insert(Vec::new())
                .push(path);
        }
    }

    for paths in len_hash_path.values().filter(|p| p.len() > 1) {
        for path in paths {
            println!("{}", path.display());
        }
        println!();
    }

    if matches.is_present("summary") {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate capnp;
extern crate capnp_rpc;
extern crate ramp;

use std::sync::mpsc::channel;
use std::thread;
use std::sync::{RwLock, Arc};

use ramp::Database;

use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;

// this is generated automatically as part of the build process
// see build.rs
pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}
use ramp_capnp::{ramp_interface};

type DB = RwLock<Database>;

struct RampServer {
    db:DB
}

impl RampServer {
    fn new() -> RampServer {
        // create a new Database, wrap in RwLock and Arc
        let db = RwLock::new(Database::new());
        RampServer{db:db}
    }
}

impl ramp_interface::Server for RampServer {
    fn prepare(&mut self, mut context: ramp_interface::PrepareContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let value = params.get_value().unwrap();

            let deps = {
                let target = params.get_dependencies().unwrap();
                let size = target.len();
                let mut entries = Vec::with_capacity(size as usize);
                for i in 0..size {
                    entries.push(target.get(i).unwrap().to_string());
                }
                entries
            };

            // let deps = params.get_dependencies().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap(); // hold lock till prepare is done
            db.prepare(key.to_string(), value.to_string(), deps, timestamp);
        }

        context.done();
    }

    fn commit(&mut self, mut context: ramp_interface::CommitContext) {
        {
            let (params, mut results) = context.get();
            let ts = params.get_timestamp();
            let mut db = self.db.write().unwrap();
            db.commit(ts);

        }

        context.done();
    }

    fn get(&mut self, mut context: ramp_interface::GetContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();

            let mut db = self.db.write().unwrap();
            let version = db.get(key.to_string());

            let r = results.init_result();

        }
        context.done();
    }

    fn get_version(&mut self, mut context: ramp_interface::GetVersionContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap();
            let version = db.get_version(key.to_string(), timestamp);

            match version {
                Some(v) => {
                    let mut r = results.init_result();
                    let mut ver = r.init_version();
                    ver.set_value(&v.value);
                    //v.set_timestamp(version.times)

                },
                None => {
                    let mut r = results.init_result();
                    r.set_none(());
                }
            };
        }

        context.done();
    }
}

fn main() {
    println!("Hello, world!");

    let rpc_server = EzRpcServer::new("127.0.0.1:6000").unwrap();

    let ramp = Box::new(RampServer::new());
    let ramp_server = Box::new(ramp_interface::ServerDispatch { server : ramp });
    rpc_server.serve(ramp_server);

    println!("Goodbye forever.");
}
<commit_msg>set timestamp on result<commit_after>#[macro_use]
extern crate capnp;
extern crate capnp_rpc;
extern crate ramp;

use std::sync::mpsc::channel;
use std::thread;
use std::sync::{RwLock, Arc};

use ramp::Database;

use capnp::capability::{Server};
use capnp_rpc::ez_rpc::EzRpcServer;

// this is generated automatically as part of the build process
// see build.rs
pub mod ramp_capnp {
  include!(concat!(env!("OUT_DIR"), "/ramp_capnp.rs"));
}
use ramp_capnp::{ramp_interface};

type DB = RwLock<Database>;

struct RampServer {
    db:DB
}

impl RampServer {
    fn new() -> RampServer {
        // create a new Database, wrap in RwLock and Arc
        let db = RwLock::new(Database::new());
        RampServer{db:db}
    }
}

impl ramp_interface::Server for RampServer {
    fn prepare(&mut self, mut context: ramp_interface::PrepareContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let value = params.get_value().unwrap();

            let deps = {
                let target = params.get_dependencies().unwrap();
                let size = target.len();
                let mut entries = Vec::with_capacity(size as usize);
                for i in 0..size {
                    entries.push(target.get(i).unwrap().to_string());
                }
                entries
            };

            // let deps = params.get_dependencies().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap(); // hold lock till prepare is done
            db.prepare(key.to_string(), value.to_string(), deps, timestamp);
        }

        context.done();
    }

    fn commit(&mut self, mut context: ramp_interface::CommitContext) {
        {
            let (params, mut results) = context.get();
            let ts = params.get_timestamp();
            let mut db = self.db.write().unwrap();
            db.commit(ts);

        }

        context.done();
    }

    fn get(&mut self, mut context: ramp_interface::GetContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();

            let mut db = self.db.write().unwrap();
            let version = db.get(key.to_string());

            let r = results.init_result();

        }
        context.done();
    }

    fn get_version(&mut self, mut context: ramp_interface::GetVersionContext) {
        {
            let (params, mut results) = context.get();
            let key = params.get_key().unwrap();
            let timestamp = params.get_timestamp();

            let mut db = self.db.write().unwrap();
            let version = db.get_version(key.to_string(), timestamp);

            match version {
                Some(v) => {
                    let mut r = results.init_result();
                    let mut ver = r.init_version();
                    ver.set_value(&v.value);
                    ver.set_timestamp(v.timestamp);

                },
                None => {
                    let mut r = results.init_result();
                    r.set_none(());
                }
            };
        }

        context.done();
    }
}

fn main() {
    println!("Hello, world!");

    let rpc_server = EzRpcServer::new("127.0.0.1:6000").unwrap();

    let ramp = Box::new(RampServer::new());
    let ramp_server = Box::new(ramp_interface::ServerDispatch { server : ramp });
    rpc_server.serve(ramp_server);

    println!("Goodbye forever.");
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate aggregated_stats;

#[macro_use]
extern crate clap;

extern crate env_logger;
#[macro_use]
extern crate log;

extern crate prometheus;
extern crate hyper;

mod analyzer;
mod args;
mod filter;
mod log_parser;
use log_parser::log_events::*;
mod render;
mod request_response_matcher;
mod http_handler;
mod result;

fn main() {
    env_logger::init().expect("Failed to initialize logging.");

    let args = args::parse_args(env::args()).expect("Failed to parse arguments.");

    let result = run(&args);

    let mut stream;
    let mut renderers: Vec<Box<render::Renderer>>;
    renderers = vec![];

    renderers.push(Box::new(render::TerminalRenderer::new()));

    if args.graphite_server.is_some() {
        stream = TcpStream::connect((args.graphite_server.as_ref().unwrap().as_str(),
                                     args.graphite_port.unwrap()))
            .expect("Could not connect to the Graphite server");

        renderers.push(Box::new(render::graphite::GraphiteRenderer::new(UTC::now(),
                                                                        args.graphite_prefix
                                                                            .clone(),
                                                                        &mut stream)));
    }

    for mut renderer in renderers {
        renderer.render(result.clone());
    }

    if args.prometheus_listen.is_some() {
        let binding_address = args.prometheus_listen.clone().unwrap();
        http_handler::listen_http(args, &binding_address);
    }
}

fn run(args: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).expect("Failed to open file.")),
    };

    let reader = io::BufReader::new(input);

    let mut events_iterator = reader.lines()
        .map(parse_event)
        .filter(|event| event.is_ok())
        .map(|event| event.unwrap());

    let pairs_iterator =
        request_response_matcher::RequestResponsePairIterator::new(&mut events_iterator)
            .filter(|pair| filter::matches_filter(pair, &args.conditions));

    analyzer::analyze_iterator(pairs_iterator)
}

fn parse_event(line: Result<String, std::io::Error>) -> Result<LogEvent, &'static str> {
    match line {
        Ok(line) => log_parser::parse_line(&line),
        Err(_) => Err("Failed to read line."),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_run() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 2);

        let timing = result.timing.unwrap();
        assert_eq!(timing.min, 7);
        assert_eq!(timing.max, 10);

        assert!(result.error.is_some());
    }

    #[test]
    fn test_run_ignore_broken_lines() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/broken.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 1);
    }

    #[test]
    fn test_parse_event() {
        let lines = vec![Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] -> GET \
                                          /content/some/page.html HTTP/1.1")),
                         Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html 7ms"))];

        let result: Vec<Result<LogEvent, &str>> = lines.into_iter().map(parse_event).collect();
        assert_eq!(result.len(), 2);
    }
}
<commit_msg>Enable InfluxDB renderer usage<commit_after>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate aggregated_stats;

#[macro_use]
extern crate clap;

extern crate env_logger;
#[macro_use]
extern crate log;

extern crate prometheus;
extern crate hyper;

mod analyzer;
mod args;
mod filter;
mod log_parser;
use log_parser::log_events::*;
mod render;
mod request_response_matcher;
mod http_handler;
mod result;

fn main() {
    env_logger::init().expect("Failed to initialize logging.");

    let args = args::parse_args(env::args()).expect("Failed to parse arguments.");

    let result = run(&args);

    let mut stream;
    let mut renderers: Vec<Box<render::Renderer>>;
    renderers = vec![];

    renderers.push(Box::new(render::TerminalRenderer::new()));

    if args.graphite_server.is_some() {
        stream = TcpStream::connect((args.graphite_server.as_ref().unwrap().as_str(),
                                     args.graphite_port.unwrap()))
            .expect("Could not connect to the Graphite server");

        renderers.push(Box::new(render::graphite::GraphiteRenderer::new(UTC::now(),
                                                                        args.graphite_prefix
                                                                            .clone(),
                                                                        &mut stream)));
    }

    if args.influxdb_write_url.is_some() {
        renderers.push(Box::new(render::influxdb::InfluxDbRenderer::new(args.influxdb_write_url
            .clone()
            .unwrap())));
    }

    for mut renderer in renderers {
        renderer.render(result.clone());
    }

    if args.prometheus_listen.is_some() {
        let binding_address = args.prometheus_listen.clone().unwrap();
        http_handler::listen_http(args, &binding_address);
    }
}

fn run(args: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).expect("Failed to open file.")),
    };

    let reader = io::BufReader::new(input);

    let mut events_iterator = reader.lines()
        .map(parse_event)
        .filter(|event| event.is_ok())
        .map(|event| event.unwrap());

    let pairs_iterator =
        request_response_matcher::RequestResponsePairIterator::new(&mut events_iterator)
            .filter(|pair| filter::matches_filter(pair, &args.conditions));

    analyzer::analyze_iterator(pairs_iterator)
}

fn parse_event(line: Result<String, std::io::Error>) -> Result<LogEvent, &'static str> {
    match line {
        Ok(line) => log_parser::parse_line(&line),
        Err(_) => Err("Failed to read line."),
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_run() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 2);

        let timing = result.timing.unwrap();
        assert_eq!(timing.min, 7);
        assert_eq!(timing.max, 10);

        assert!(result.error.is_some());
    }

    #[test]
    fn test_run_ignore_broken_lines() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/broken.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
        };

        let result = run(&args);
        assert_eq!(result.count, 1);
    }

    #[test]
    fn test_parse_event() {
        let lines = vec![Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] -> GET \
                                          /content/some/page.html HTTP/1.1")),
                         Ok(String::from("08/Apr/2016:09:57:47 +0200 [001] <- 200 text/html 7ms"))];

        let result: Vec<Result<LogEvent, &str>> = lines.into_iter().map(parse_event).collect();
        assert_eq!(result.len(), 2);
    }
}
<|endoftext|>