<commit_before>#![recursion_limit = "1024"]

mod errors {
    use error_chain::error_chain;

    error_chain! {
        foreign_links {
            Fmt(::std::fmt::Error);
            Io(::std::io::Error);
        }
    }
}

use blake2_rfc::blake2b::Blake2b;
use docopt::Docopt;
use error_chain::{bail, quick_main};
use errors::*;
use serde::Deserialize;
use std::fmt::Write;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read};
use std::path::Path;
use std::process;

const BUFFER_SIZE: usize = 8 * 1024;
const USAGE: &'static str = "
Print or check BLAKE2 (512-bit) checksums.
With no FILE, or when FILE is -, read standard input.

Usage:
  b2sum [options] [<filename>]...
  b2sum (-h | --help)
  b2sum --version

Options:
  -c, --check           read BLAKE2 sums from the FILEs and check them
  -l, --length=BITS     digest length in bits; must not exceed the maximum for the
                        blake2 algorithm and must be a multiple of 8 [default: 512]
      --tag             create a BSD-style checksum

The following five options are useful only when verifying checksums:
      --ignore-missing  don't fail or report status for missing files
      --quiet           don't print OK for each successfully verified file
      --status          don't output anything, status code shows success
      --strict          exit non-zero for improperly formatted checksum lines
  -w, --warn            warn about improperly formatted checksum lines

  -h, --help            display this help and exit
      --version         output version information and exit

The sums are computed as described in RFC 7693.  When checking, the input
should be a former output of this program.  The default mode is to print
a line with checksum and name for each FILE.
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_filename: Vec<String>,
    flag_check: bool,
    flag_ignore_missing: bool,
    flag_length: usize,
    flag_quiet: bool,
    flag_status: bool,
    flag_strict: bool,
    flag_tag: bool,
    flag_version: bool,
    flag_warn: bool,
}

fn print_version() -> ! {
    let version = env!("CARGO_PKG_VERSION");
    println!("b2sum-rs {}", version);
    process::exit(0)
}

fn hash_reader<R: Read>(length: usize, mut reader: R) -> Result<String> {
    let mut digest = Blake2b::new(length);
    let mut buffer = [0; BUFFER_SIZE];

    loop {
        match reader.read(&mut buffer) {
            Ok(0) => break,
            Ok(c) => digest.update(&buffer[..c]),
            Err(e) => bail!(e),
        }
    }

    let output = digest.finalize();
    let mut result = String::with_capacity(length * 2);
    for &b in output.as_bytes() {
        write!(&mut result, "{:02x}", b)?;
    }

    Ok(result)
}

fn hash_file<P: AsRef<Path>>(length: usize, path: P) -> Result<String> {
    let file = File::open(path)?;
    hash_reader(length, file)
}

fn split_check_line(line: &str) -> Result<(&str, &str)> {
    let hash_length = line.chars()
        .position(|c| !c.is_digit(16))
        .unwrap_or(0);
    if hash_length < 2 || hash_length % 2 != 0 || hash_length > 128 {
        bail!("Invalid hash length: {}", hash_length);
    }

    let hash = &line[0..hash_length];
    let line = &line[hash_length..];
    if line.len() < 3 {
        bail!("Malformed line");
    }

    let filename = &line[2..];

    Ok((hash, filename))
}

fn check_input<R: BufRead>(args: &Args, check_filename: &str, reader: R) -> Result<bool> {
    let print_result = !(args.flag_quiet || args.flag_status);
    let mut errors = false;

    for (i, line) in reader.lines().enumerate() {
        let line = line?;
        let line = line.trim();
        if line.starts_with('#') {
            continue;
        }

        let (hash, filename) = match split_check_line(line) {
            Ok((hash, filename)) => (hash, filename),
            Err(e) => {
                if args.flag_strict {
                    errors = true;
                }

                if args.flag_warn {
                    println!("{}:{}: {}", check_filename, i + 1, e.description())
                }

                continue;
            }
        };

        let length = hash.len() / 2;
        let calculated_hash = match hash_file(length, filename) {
            Ok(h) => h,
            Err(Error(ErrorKind::Io(ref e), _)) if e.kind() == io::ErrorKind::NotFound && args.flag_ignore_missing => continue,
            Err(e) => {
                errors = true;
                if !args.flag_status {
                    println!("{}: FAILED {}", filename, e.description());
                }

                continue;
            }
        };

        let matched = hash == calculated_hash;
        if !matched {
            errors = true;
        }

        if print_result {
            print!("{}: ", filename);
            if matched {
                println!("OK");
            } else {
                println!("FAILED");

            }
        }
    }

    Ok(errors)
}

fn check_args(args: Args) -> Result<i32> {
    let filename = args.arg_filename[0].as_str();
    let errors = if filename == "-" {
        let stdin = io::stdin();
        let reader = stdin.lock();
        check_input(&args, filename, reader)?
    } else {
        let file = File::open(filename)?;
        let reader = BufReader::new(file);
        check_input(&args, filename, reader)?
    };

    let code = if errors { 1 } else { 0 };
    Ok(code)
}

fn hash_args(args: Args) -> Result<i32> {
    let length = args.flag_length / 8;
    for filename in args.arg_filename {
        let hash = if filename == "-" {
            let stdin = io::stdin();
            hash_reader(length, stdin)?
        } else {
            hash_file(length, &filename)?
        };

        if args.flag_tag {
            print!("BLAKE2b");
            if args.flag_length < 512 {
                print!("-{}", args.flag_length);
            }

            println!(" ({}) = {}", filename, hash);
        } else {
            println!("{}  {}", hash, filename);
        }
    }

    Ok(0)
}

quick_main!(run);

fn run() -> Result<i32> {
    let mut args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());

    if args.flag_version {
        print_version();
    }

    if args.arg_filename.is_empty() {
        args.arg_filename.push("-".to_string());
    }

    if args.flag_check {
        check_args(args)
    } else {
        hash_args(args)
    }
}

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

    #[test]
    fn split_check_line_with_valid_line() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534  test";
        let (hash, filename) = split_check_line(line).unwrap();
        assert_eq!("c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534",
                   hash);
        assert_eq!("test", filename);
    }

    #[test]
    fn split_check_line_with_truncated_line() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534 ";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Malformed line", result.description());
    }

    #[test]
    fn split_check_line_with_missing_filename() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534  ";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Malformed line", result.description());
    }

    #[test]
    fn split_check_line_with_too_small_hash() {
        let line = "c  test";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Invalid hash length: 1", result.description());
    }

    #[test]
    fn split_check_line_with_too_long_hash() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea2353400  test";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Invalid hash length: 130", result.description());
    }

    #[test]
    fn split_check_line_with_non_even_hash() {
        let line = "c0ae0  test";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Invalid hash length: 5", result.description());
    }

    #[test]
    fn test_hash_formatting() {
        let expected = "7ea59e7a000ec003846b6607dfd5f9217b681dc1a81b0789b464c3995105d93083f7f0a86fca01a1bed27e9f9303ae58d01746e3b20443480bea56198e65bfc5";
        assert_eq!(expected, hash_reader(64, "hi\n".as_bytes()).unwrap());
    }
}
<commit_msg>Fix formatting with rustfmt<commit_after>#![recursion_limit = "1024"]

mod errors {
    use error_chain::error_chain;

    error_chain! {
        foreign_links {
            Fmt(::std::fmt::Error);
            Io(::std::io::Error);
        }
    }
}

use blake2_rfc::blake2b::Blake2b;
use docopt::Docopt;
use error_chain::{bail, quick_main};
use errors::*;
use serde::Deserialize;
use std::fmt::Write;
use std::fs::File;
use std::io::{self, BufRead, BufReader, Read};
use std::path::Path;
use std::process;

const BUFFER_SIZE: usize = 8 * 1024;
const USAGE: &'static str = "
Print or check BLAKE2 (512-bit) checksums.
With no FILE, or when FILE is -, read standard input.

Usage:
  b2sum [options] [<filename>]...
  b2sum (-h | --help)
  b2sum --version

Options:
  -c, --check           read BLAKE2 sums from the FILEs and check them
  -l, --length=BITS     digest length in bits; must not exceed the maximum for the
                        blake2 algorithm and must be a multiple of 8 [default: 512]
      --tag             create a BSD-style checksum

The following five options are useful only when verifying checksums:
      --ignore-missing  don't fail or report status for missing files
      --quiet           don't print OK for each successfully verified file
      --status          don't output anything, status code shows success
      --strict          exit non-zero for improperly formatted checksum lines
  -w, --warn            warn about improperly formatted checksum lines

  -h, --help            display this help and exit
      --version         output version information and exit

The sums are computed as described in RFC 7693.  When checking, the input
should be a former output of this program.  The default mode is to print
a line with checksum and name for each FILE.
";

#[derive(Debug, Deserialize)]
struct Args {
    arg_filename: Vec<String>,
    flag_check: bool,
    flag_ignore_missing: bool,
    flag_length: usize,
    flag_quiet: bool,
    flag_status: bool,
    flag_strict: bool,
    flag_tag: bool,
    flag_version: bool,
    flag_warn: bool,
}

fn print_version() -> ! {
    let version = env!("CARGO_PKG_VERSION");
    println!("b2sum-rs {}", version);
    process::exit(0)
}

fn hash_reader<R: Read>(length: usize, mut reader: R) -> Result<String> {
    let mut digest = Blake2b::new(length);
    let mut buffer = [0; BUFFER_SIZE];

    loop {
        match reader.read(&mut buffer) {
            Ok(0) => break,
            Ok(c) => digest.update(&buffer[..c]),
            Err(e) => bail!(e),
        }
    }

    let output = digest.finalize();
    let mut result = String::with_capacity(length * 2);
    for &b in output.as_bytes() {
        write!(&mut result, "{:02x}", b)?;
    }

    Ok(result)
}

fn hash_file<P: AsRef<Path>>(length: usize, path: P) -> Result<String> {
    let file = File::open(path)?;
    hash_reader(length, file)
}

fn split_check_line(line: &str) -> Result<(&str, &str)> {
    let hash_length = line.chars().position(|c| !c.is_digit(16)).unwrap_or(0);
    if hash_length < 2 || hash_length % 2 != 0 || hash_length > 128 {
        bail!("Invalid hash length: {}", hash_length);
    }

    let hash = &line[0..hash_length];
    let line = &line[hash_length..];
    if line.len() < 3 {
        bail!("Malformed line");
    }

    let filename = &line[2..];

    Ok((hash, filename))
}

fn check_input<R: BufRead>(args: &Args, check_filename: &str, reader: R) -> Result<bool> {
    let print_result = !(args.flag_quiet || args.flag_status);
    let mut errors = false;

    for (i, line) in reader.lines().enumerate() {
        let line = line?;
        let line = line.trim();
        if line.starts_with('#') {
            continue;
        }

        let (hash, filename) = match split_check_line(line) {
            Ok((hash, filename)) => (hash, filename),
            Err(e) => {
                if args.flag_strict {
                    errors = true;
                }

                if args.flag_warn {
                    println!("{}:{}: {}", check_filename, i + 1, e.description())
                }

                continue;
            }
        };

        let length = hash.len() / 2;
        let calculated_hash = match hash_file(length, filename) {
            Ok(h) => h,
            Err(Error(ErrorKind::Io(ref e), _)) if e.kind() == io::ErrorKind::NotFound && args.flag_ignore_missing => continue,
            Err(e) => {
                errors = true;
                if !args.flag_status {
                    println!("{}: FAILED {}", filename, e.description());
                }

                continue;
            }
        };

        let matched = hash == calculated_hash;
        if !matched {
            errors = true;
        }

        if print_result {
            print!("{}: ", filename);
            if matched {
                println!("OK");
            } else {
                println!("FAILED");
            }
        }
    }

    Ok(errors)
}

fn check_args(args: Args) -> Result<i32> {
    let filename = args.arg_filename[0].as_str();
    let errors = if filename == "-" {
        let stdin = io::stdin();
        let reader = stdin.lock();
        check_input(&args, filename, reader)?
    } else {
        let file = File::open(filename)?;
        let reader = BufReader::new(file);
        check_input(&args, filename, reader)?
    };

    let code = if errors { 1 } else { 0 };
    Ok(code)
}

fn hash_args(args: Args) -> Result<i32> {
    let length = args.flag_length / 8;
    for filename in args.arg_filename {
        let hash = if filename == "-" {
            let stdin = io::stdin();
            hash_reader(length, stdin)?
        } else {
            hash_file(length, &filename)?
        };

        if args.flag_tag {
            print!("BLAKE2b");
            if args.flag_length < 512 {
                print!("-{}", args.flag_length);
            }

            println!(" ({}) = {}", filename, hash);
        } else {
            println!("{}  {}", hash, filename);
        }
    }

    Ok(0)
}

quick_main!(run);

fn run() -> Result<i32> {
    let mut args: Args = Docopt::new(USAGE).and_then(|d| d.deserialize()).unwrap_or_else(|e| e.exit());

    if args.flag_version {
        print_version();
    }

    if args.arg_filename.is_empty() {
        args.arg_filename.push("-".to_string());
    }

    if args.flag_check {
        check_args(args)
    } else {
        hash_args(args)
    }
}

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

    #[test]
    fn split_check_line_with_valid_line() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534  test";
        let (hash, filename) = split_check_line(line).unwrap();
        assert_eq!(
            "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534",
            hash
        );
        assert_eq!("test", filename);
    }

    #[test]
    fn split_check_line_with_truncated_line() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534 ";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Malformed line", result.description());
    }

    #[test]
    fn split_check_line_with_missing_filename() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea23534  ";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Malformed line", result.description());
    }

    #[test]
    fn split_check_line_with_too_small_hash() {
        let line = "c  test";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Invalid hash length: 1", result.description());
    }

    #[test]
    fn split_check_line_with_too_long_hash() {
        let line = "c0ae24f806df19d850565b234bc37afd5035e7536388290db9413c98578394313f38b093143ecfbc208425d54b9bfef0d9917a9e93910f7914a97e73fea2353400  test";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Invalid hash length: 130", result.description());
    }

    #[test]
    fn split_check_line_with_non_even_hash() {
        let line = "c0ae0  test";
        let result = split_check_line(line).unwrap_err();
        assert_eq!("Invalid hash length: 5", result.description());
    }

    #[test]
    fn test_hash_formatting() {
        let expected = "7ea59e7a000ec003846b6607dfd5f9217b681dc1a81b0789b464c3995105d93083f7f0a86fca01a1bed27e9f9303ae58d01746e3b20443480bea56198e65bfc5";
        assert_eq!(expected, hash_reader(64, "hi\n".as_bytes()).unwrap());
    }
}
<|endoftext|>


<commit_before>#![feature(phase)]
#![feature(if_let)]

#[phase(plugin)]
extern crate rustecs_macros;

extern crate rustecs;
extern crate serialize;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;
use std::default::Default;
use std::rand;
use std::rand::Rng;
use std::num::abs;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
};
use piston::graphics::{
    AddBorder,
    AddRectangle,
    AddEllipse,
    AddColor,
    Context,
    Draw,
};
use piston::event::{
    PressEvent,
    ReleaseEvent,
    Event,
    Render,
    Update,
    Input,
};
use piston::input::{
    keyboard,
    Keyboard,
    Button,
};
use rustecs::{
    Entities,
    Components,
};

static WINDOW_W: f64 = 800.0;
static WINDOW_H: f64 = 600.0;

fn draw_system(event: &Event,
              gl: &mut Gl,
              positions: &mut Components<Position>,
              shapes: &mut Components<Shape>,
              colors: &mut Components<Color>) {
    if let &Render(args) = event {
        let w = args.width as f64;
        let h = args.height as f64;
        gl.viewport(0, 0, w as i32, h as i32);
        let c = Context::abs(w, h);
        // Clear background.
        c.rgb(0.2, 0.2, 0.2).draw(gl);

        for (eid, pos) in positions.iter_mut() {
          let mut shape = Point;
          let mut border = None;
          if shapes.contains_key(eid) {
            shape = shapes.get_mut(eid).varient;
            border = shapes.get_mut(eid).border;
          }
          let (r, g, b) = if colors.contains_key(eid) {
              (colors.get_mut(eid).r, colors.get_mut(eid).g, colors.get_mut(eid).b)
          } else {
              (1.0, 1.0, 1.0)
          };
          let drawing = c.rgb(r, g, b);
          match (shape, border) {
              (Point, None)    => drawing.rect(1.0, 1.0, w, h).draw(gl),
              (Point, Some(b)) => drawing.rect(1.0, 1.0, w, h).border_radius(b).draw(gl),

              (Circle(rad), None)    => drawing.ellipse(pos.x, pos.y, rad, rad).draw(gl),
              (Circle(rad), Some(b)) => drawing.ellipse(pos.x, pos.y, rad, rad).border_radius(b).draw(gl),

              (Square(w,h), None)    => drawing.rect(pos.x, pos.y, w, h).draw(gl),
              (Square(w,h), Some(b)) => drawing.rect(pos.x, pos.y, w, h).border_radius(b).draw(gl),
          };
        }
    }
}

fn shimmer_system(shimmers: &mut Components<Shimmer>,
                 colors: &mut Components<Color>) {
    for (eid, _) in shimmers.iter_mut() {
        if !colors.contains_key(eid) {
            continue;
        }
        let color = colors.get_mut(eid);
        let ref mut rng = rand::task_rng();
        color.r = rng.gen_range(0.5, 1.0);
        color.g = rng.gen_range(0.5, 1.0);
        color.b = rng.gen_range(0.5, 1.0);
    }
}

fn move_system(event: &Event,
              positions: &mut Components<Position>,
              shapes: &mut Components<Shape>,
              clamps: &mut Components<WindowClamp>,
              velocities: &mut Components<Velocity>) {
    if let &Update(args) = event {
        let dt = args.dt;
        for (eid, position) in positions.iter_mut() {
            if !velocities.contains_key(eid) {
                continue;
            }

            // If we have both a position and a velocity, integrate.
            let velocity = velocities.get_mut(eid);
            position.x += velocity.x * dt;
            position.y += velocity.y * dt;

            if !clamps.contains_key(eid) {
              continue;
            }
            let (w, h) = match shapes.find(eid) {
                Some(&s) =>
                  match s.varient {
                    Circle(r) => (r, r),
                    Square(w,h) => (w, h),
                    Point => (0.0, 0.0)
                  },
                None => (0.0, 0.0)
            };

            let clamp = clamps.get_mut(eid);
            let velocity_mult = match clamp.varient {
              Bounce => -1.0,
              Stop => 0.0,
              _ => 1.0
            };
            match clamp.varient {
              Bounce | Stop => {
                if position.x + w > WINDOW_W {
                  position.x = WINDOW_W - w;
                  velocity.x *= velocity_mult;
                } else if position.x < 0.0 {
                  position.x = 0.0;
                  velocity.x *= velocity_mult;
                }
                if position.y + h > WINDOW_H {
                  position.y = WINDOW_H - h;
                  velocity.y *= velocity_mult;
                } else if position.y < 0.0 {
                  position.y = 0.0;
                  velocity.y *= velocity_mult;
                }
              },
              Remove => {
                if position.x > WINDOW_W
                || position.x + w < 0.0 {
                  println!("Should delete {}", eid);
                }
                if position.y > WINDOW_H
                || position.y + h < 0.0 {
                  println!("Should delete {}", eid);
                }
              }
            }
        }
    }
}

fn control_system(event: &Event,
              controllers: &mut Components<PlayerController>,
              velocities: &mut Components<Velocity>) {

    const PADDLE_V: f64 = 800.0;

    for (eid, controller) in controllers.iter_mut() {
        event.press(|button| {
            if button == Keyboard(controller.up) {
                velocities.get_mut(eid).y = -PADDLE_V;
            } else if button == Keyboard(controller.down) {
                velocities.get_mut(eid).y = PADDLE_V;
            }
        });
        event.release(|button| {
            if button == Keyboard(controller.up)
            || button == Keyboard(controller.down) {
                velocities.get_mut(eid).y = 0.0;
            }
        });
    }
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub enum ClampVarient {
    Bounce,
    Stop,
    Remove // Acts when item leaves window.
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct WindowClamp {
    varient: ClampVarient
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct PlayerController {
    up: keyboard::Key,
    down: keyboard::Key
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Position {
    x: f64,
    y: f64
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shimmer;

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Color {
    r: f32,
    g: f32,
    b: f32
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub enum ShapeVarient {
    Point,
    Circle(f64), // radius
    Square(f64, f64), // width, height
}

impl Default for ShapeVarient {
    fn default() -> ShapeVarient { Point }
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shape {
    varient: ShapeVarient,
    border: Option<f64>
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Velocity {
    x: f64,
    y: f64
}

world! {
    World,
    components Position, Shape, Velocity, Color, Shimmer, PlayerController,
        WindowClamp;
}

fn make_ball() -> Entity {
    const BALL_R: f64 = 20.0;
    let shape = Circle(BALL_R);
    let x = (WINDOW_W - BALL_R) / 2.0;
    let y = (WINDOW_H - BALL_R) / 2.0;

    fn random_vel() -> f64 {
        let ref mut rng = rand::task_rng();
        rng.gen_range(80.0, 100.0) * if rng.gen() { 1.0 } else { -1.0 }
    }

    Entity::new()
        .with_shimmer(Shimmer)
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_velocity(
            Velocity {
                x: random_vel(),
                y: random_vel()
            })
        .with_shape(
            Shape {
                varient: shape,
                border: None
            })
        .with_color(
            Color{
                r: 1.0,
                g: 0.5,
                b: 0.2
            })
        .with_window_clamp(
            WindowClamp {
               varient: Bounce
            })
}

fn make_player(p1: bool) -> Entity {
    const FROM_WALL: f64 = 20.0;
    const PADDLE_W: f64 = 20.0;
    const PADDLE_H: f64 = 150.0;
    let x = if p1 {
        FROM_WALL
    } else {
        WINDOW_W - FROM_WALL - PADDLE_W
    };
    let y = (WINDOW_H - PADDLE_H) / 2.0;
    Entity::new()
        .with_player_controller(
            PlayerController {
                up: if p1 { keyboard::W } else { keyboard::I },
                down: if p1 { keyboard::S } else { keyboard::K }
            })
        .with_velocity(Velocity { x: 0.0, y: 0.0 } )
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_shape(
            Shape {
                varient: Square(PADDLE_W, PADDLE_H),
                border: None
            })
        .with_color(
            Color{
                r: 0.5,
                g: 0.2,
                b: 0.8
            })
        .with_window_clamp(
            WindowClamp {
               varient: Stop
            })
}

fn main() {
    let opengl = piston::shader_version::opengl::OpenGL_3_2;
    let mut window = WindowSDL2::new(
        opengl,
        WindowSettings {
            title: "Pong".to_string(),
            size: [WINDOW_W as u32, WINDOW_H as u32],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut gl = Gl::new(opengl);
    let mut world = World::new();

    world.add(make_player(true));
    world.add(make_player(false));
    world.add(make_ball());

    let event_settings = EventSettings {
        updates_per_second: 120,
        max_frames_per_second: 60,
    };

    for e in EventIterator::new(&mut window, &event_settings) {
        control_system(&e, &mut world.player_controllers, &mut world.velocities);
        move_system(&e, &mut world.positions, &mut world.shapes,
                    &mut world.window_clamps, &mut world.velocities);
        shimmer_system(&mut world.shimmers, &mut world.colors);
        draw_system(&e, &mut gl, &mut world.positions, &mut world.shapes, &mut world.colors);
    }
}
<commit_msg>Pass world to 'make' commands. Hmm not sure which I like better but thought this would be good for make commands that want to add multiple entities to the world in one go.<commit_after>#![feature(phase)]
#![feature(if_let)]

#[phase(plugin)]
extern crate rustecs_macros;

extern crate rustecs;
extern crate serialize;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;
use std::default::Default;
use std::rand;
use std::rand::Rng;
use std::num::abs;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
};
use piston::graphics::{
    AddBorder,
    AddRectangle,
    AddEllipse,
    AddColor,
    Context,
    Draw,
};
use piston::event::{
    PressEvent,
    ReleaseEvent,
    Event,
    Render,
    Update,
    Input,
};
use piston::input::{
    keyboard,
    Keyboard,
    Button,
};
use rustecs::{
    Entities,
    Components,
};

static WINDOW_W: f64 = 800.0;
static WINDOW_H: f64 = 600.0;

fn draw_system(event: &Event,
              gl: &mut Gl,
              positions: &mut Components<Position>,
              shapes: &mut Components<Shape>,
              colors: &mut Components<Color>) {
    if let &Render(args) = event {
        let w = args.width as f64;
        let h = args.height as f64;
        gl.viewport(0, 0, w as i32, h as i32);
        let c = Context::abs(w, h);
        // Clear background.
        c.rgb(0.2, 0.2, 0.2).draw(gl);
        c.rgb(0.7, 0.7, 0.7).rect(0.0, 0.0, WINDOW_W, WINDOW_H).border_radius(1.0).draw(gl);

        for (eid, pos) in positions.iter_mut() {
          let mut shape = Point;
          let mut border = None;
          if shapes.contains_key(eid) {
            shape = shapes.get_mut(eid).varient;
            border = shapes.get_mut(eid).border;
          }
          let (r, g, b) = if colors.contains_key(eid) {
              (colors.get_mut(eid).r, colors.get_mut(eid).g, colors.get_mut(eid).b)
          } else {
              (1.0, 1.0, 1.0)
          };
          let drawing = c.rgb(r, g, b);
          match (shape, border) {
              (Point, None)    => drawing.rect(1.0, 1.0, w, h).draw(gl),
              (Point, Some(b)) => drawing.rect(1.0, 1.0, w, h).border_radius(b).draw(gl),

              (Circle(rad), None)    => drawing.ellipse(pos.x, pos.y, rad, rad).draw(gl),
              (Circle(rad), Some(b)) => drawing.ellipse(pos.x, pos.y, rad, rad).border_radius(b).draw(gl),

              (Square(w,h), None)    => drawing.rect(pos.x, pos.y, w, h).draw(gl),
              (Square(w,h), Some(b)) => drawing.rect(pos.x, pos.y, w, h).border_radius(b).draw(gl),
          };
        }
    }
}

fn shimmer_system(shimmers: &mut Components<Shimmer>,
                 colors: &mut Components<Color>) {
    for (eid, _) in shimmers.iter_mut() {
        if !colors.contains_key(eid) {
            continue;
        }
        let color = colors.get_mut(eid);
        let ref mut rng = rand::task_rng();
        color.r = rng.gen_range(0.5, 1.0);
        color.g = rng.gen_range(0.5, 1.0);
        color.b = rng.gen_range(0.5, 1.0);
    }
}

fn move_system(event: &Event,
              positions: &mut Components<Position>,
              shapes: &mut Components<Shape>,
              clamps: &mut Components<WindowClamp>,
              velocities: &mut Components<Velocity>) {
    if let &Update(args) = event {
        let dt = args.dt;
        for (eid, position) in positions.iter_mut() {
            if !velocities.contains_key(eid) {
                continue;
            }

            // If we have both a position and a velocity, integrate.
            let velocity = velocities.get_mut(eid);
            position.x += velocity.x * dt;
            position.y += velocity.y * dt;

            if !clamps.contains_key(eid) {
              continue;
            }
            let (w, h) = match shapes.find(eid) {
                Some(&s) =>
                  match s.varient {
                    Circle(r) => (r, r),
                    Square(w,h) => (w, h),
                    Point => (0.0, 0.0)
                  },
                None => (0.0, 0.0)
            };

            let clamp = clamps.get_mut(eid);
            let velocity_mult = match clamp.varient {
              Bounce => -1.0,
              Stop => 0.0,
              _ => 1.0
            };
            match clamp.varient {
              Bounce | Stop => {
                if position.x + w > WINDOW_W {
                  position.x = WINDOW_W - w;
                  velocity.x *= velocity_mult;
                } else if position.x < 0.0 {
                  position.x = 0.0;
                  velocity.x *= velocity_mult;
                }
                if position.y + h > WINDOW_H {
                  position.y = WINDOW_H - h;
                  velocity.y *= velocity_mult;
                } else if position.y < 0.0 {
                  position.y = 0.0;
                  velocity.y *= velocity_mult;
                }
              },
              Remove => {
                if position.x > WINDOW_W
                || position.x + w < 0.0 {
                  println!("Should delete {}", eid);
                }
                if position.y > WINDOW_H
                || position.y + h < 0.0 {
                  println!("Should delete {}", eid);
                }
              }
            }
        }
    }
}

fn control_system(event: &Event,
              controllers: &mut Components<PlayerController>,
              velocities: &mut Components<Velocity>) {

    const PADDLE_V: f64 = 800.0;

    for (eid, controller) in controllers.iter_mut() {
        event.press(|button| {
            if button == Keyboard(controller.up) {
                velocities.get_mut(eid).y = -PADDLE_V;
            } else if button == Keyboard(controller.down) {
                velocities.get_mut(eid).y = PADDLE_V;
            }
        });
        event.release(|button| {
            if button == Keyboard(controller.up)
            || button == Keyboard(controller.down) {
                velocities.get_mut(eid).y = 0.0;
            }
        });
    }
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub enum ClampVarient {
    Bounce,
    Stop,
    Remove // Acts when item leaves window.
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct WindowClamp {
    varient: ClampVarient
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct PlayerController {
    up: keyboard::Key,
    down: keyboard::Key
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Position {
    x: f64,
    y: f64
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shimmer;

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Color {
    r: f32,
    g: f32,
    b: f32
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub enum ShapeVarient {
    Point,
    Circle(f64), // radius
    Square(f64, f64), // width, height
}

impl Default for ShapeVarient {
    fn default() -> ShapeVarient { Point }
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Shape {
    varient: ShapeVarient,
    border: Option<f64>
}

#[deriving(Clone, Decodable, Encodable, PartialEq, Show)]
pub struct Velocity {
    x: f64,
    y: f64
}

world! {
    World,
    components Position, Shape, Velocity, Color, Shimmer, PlayerController,
        WindowClamp;
}

fn make_ball(world: &mut World) {
    const BALL_R: f64 = 20.0;
    let shape = Circle(BALL_R);
    let x = (WINDOW_W - BALL_R) / 2.0;
    let y = (WINDOW_H - BALL_R) / 2.0;

    fn random_vel() -> f64 {
        let ref mut rng = rand::task_rng();
        rng.gen_range(80.0, 100.0) * if rng.gen() { 1.0 } else { -1.0 }
    }

    let e = Entity::new()
        .with_shimmer(Shimmer)
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_velocity(
            Velocity {
                x: random_vel(),
                y: random_vel()
            })
        .with_shape(
            Shape {
                varient: shape,
                border: None
            })
        .with_color(
            Color{
                r: 1.0,
                g: 0.5,
                b: 0.2
            })
        .with_window_clamp(
            WindowClamp {
               varient: Bounce
            });
    world.add(e);
}

fn make_player(world: &mut World, p1: bool) -> u32 {
    const FROM_WALL: f64 = 20.0;
    const PADDLE_W: f64 = 20.0;
    const PADDLE_H: f64 = 150.0;
    let x = if p1 {
        FROM_WALL
    } else {
        WINDOW_W - FROM_WALL - PADDLE_W
    };
    let y = (WINDOW_H - PADDLE_H) / 2.0;
    let e = Entity::new()
        .with_player_controller(
            PlayerController {
                up: if p1 { keyboard::W } else { keyboard::I },
                down: if p1 { keyboard::S } else { keyboard::K }
            })
        .with_velocity(Velocity { x: 0.0, y: 0.0 } )
        .with_position(
            Position{
                x: x,
                y: y
            })
        .with_shape(
            Shape {
                varient: Square(PADDLE_W, PADDLE_H),
                border: None
            })
        .with_color(
            Color{
                r: 0.5,
                g: 0.2,
                b: 0.8
            })
        .with_window_clamp(
            WindowClamp {
               varient: Stop
            });
    world.add(e)
}

fn main() {
    let opengl = piston::shader_version::opengl::OpenGL_3_2;
    let mut window = WindowSDL2::new(
        opengl,
        WindowSettings {
            title: "Pong".to_string(),
            size: [WINDOW_W as u32, WINDOW_H as u32],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut gl = Gl::new(opengl);
    let mut world = World::new();

    make_player(&mut world, true);
    make_player(&mut world, false);
    make_ball(&mut world);

    let event_settings = EventSettings {
        updates_per_second: 120,
        max_frames_per_second: 60,
    };

    for e in EventIterator::new(&mut window, &event_settings) {
        control_system(&e, &mut world.player_controllers, &mut world.velocities);
        move_system(&e, &mut world.positions, &mut world.shapes,
                    &mut world.window_clamps, &mut world.velocities);
        shimmer_system(&mut world.shimmers, &mut world.colors);
        draw_system(&e, &mut gl, &mut world.positions, &mut world.shapes, &mut world.colors);
    }
}
<|endoftext|>


<commit_before>extern crate argparse;

use argparse::{ArgumentParser, StoreTrue, Store, StoreOption, Print};

fn main() {
    let mut debug = false;
    let mut path = "".to_string();
    let mut file: Option<String> = None;
    { // block limits of borrows by refer() method calls
        let mut ap = ArgumentParser::new();
        ap.add_option(&["-v", "--version"], Print(env!("CARGO_PKG_VERSION").to_string()), "show version");
        ap.refer(&mut debug).add_option(&["-d", "--debug"], StoreTrue, "enable debug output");
        ap.refer(&mut file).add_option(&["-f", "--file"], StoreOption, "possible alternates file, - for stdin");
        ap.refer(&mut path).add_argument("PATH", Store, "path to find alternate for").required();
        ap.parse_args_or_exit();
    }

    println!("required path: {}", path);

    if let Some(unwrapped_file) = file {
        println!("file: {}", unwrapped_file);
    }

    println!("debug: {}", debug);
}
<commit_msg>Add options struct & optionals<commit_after>extern crate argparse;

use argparse::{ArgumentParser, StoreTrue, Store, StoreOption, Print};
use std::io::BufRead;

struct Options {
    debug: bool,
    path: String,
    file: Option<String>
}

fn main() {
    let mut options = Options {
        debug: false,
        path: "".to_string(),
        file: None
    };

    { // block limits of borrows by refer() method calls
        let mut ap = ArgumentParser::new();
        ap.add_option(&["-v", "--version"], Print(env!("CARGO_PKG_VERSION").to_string()), "show version");
        ap.refer(&mut options.debug).add_option(&["-d", "--debug"], StoreTrue, "enable debug output");
        ap.refer(&mut options.file).add_option(&["-f", "--file"], StoreOption, "possible alternates file, - for stdin");
        ap.refer(&mut options.path).add_argument("PATH", Store, "path to find alternate for").required();
        ap.parse_args_or_exit();
    }

    println!("required path: {}", options.path);

    if let Some(unwrapped_file) = options.file {
        println!("file: {}", unwrapped_file);
        if unwrapped_file == "-" {
            let stdin = std::io::stdin();
            for line in stdin.lock().lines() {
              println!("{}", line.unwrap());
            }
        } else {
          println!("Figure out how open and read actual file");
        }
    }

    println!("debug: {}", options.debug);
}
<|endoftext|>


<commit_before>/*
Interrupt Enable Register
--------------------------- FFFF
Internal RAM
--------------------------- FF80
Empty but unusable for I/O
--------------------------- FF4C
I/O ports
--------------------------- FF00
Empty but unusable for I/O
--------------------------- FEA0
Sprite Attrib Memory (OAM)
--------------------------- FE00
Echo of 8kB Internal RAM
--------------------------- E000
8kB Internal RAM
--------------------------- C000
8kB switchable RAM bank
--------------------------- A000
8kB Video RAM
--------------------------- 8000
32kB Cartridge
--------------------------- 0000
*/
struct Memory {
    vram: [u8; 8192],
    bank: [u8; 8192],
    internal: [u8; 8192],
    oam: [u8; 160],
    ier: u8,
    dummy: u8
}

impl Memory {
    fn new() -> Memory {
        Memory {
            vram: [0u8; 8192],
            bank: [0u8; 8192],
            internal: [0u8; 8192],
            oam: [0u8; 160],
            ier: 0,
            dummy: 0
        }
    }

    fn map(&mut self, addr: u16) -> &mut u8 {
        match addr {
            0x8000 ... 0x9FFF => &mut self.vram[addr as usize - 0x8000],
            0xA000 ... 0xBFFF => &mut self.bank[addr as usize - 0xA000],
            0xC000 ... 0xDFFF => &mut self.internal[addr as usize - 0xC000],
            0xE000 ... 0xFDFF => &mut self.internal[addr as usize - 0xE000],
            0xFE00 ... 0xFE9F => &mut self.oam[addr as usize - 0xFE00],
            0xFFFF => &mut self.ier,
            _ => {
                println!("ERROR: Out of bounds memory write");
                &mut self.dummy
            } // TODO: error
        }
    }

    fn write(&mut self, addr: u16, value: u8) {
        *self.map(addr) = value;
    }

    fn write_word(&mut self, addr: u16, word: u16) {
        self.write(addr + 0, ((word >> 0) & 0xFF) as u8);
        self.write(addr + 1, ((word >> 8) & 0xFF) as u8);
    }

    // TODO: Make these not mutable
    fn read(&mut self, addr: u16) -> u8 {
        return *self.map(addr);
    }

    fn read_word(&mut self, addr: u16) -> u16 {
        return
            ((self.read(addr + 0) as u16) << 0) +
            ((self.read(addr + 1) as u16) << 8)
    }
}

fn main() {
    let mut memory = Memory::new();
    memory.write_word(0x8010, 32);
    println!("{}", memory.read_word(0x8010));
}<commit_msg>Implemented dword memory functions and split map into read/write<commit_after>/*
Interrupt Enable Register
--------------------------- FFFF
Internal RAM
--------------------------- FF80
Empty but unusable for I/O
--------------------------- FF4C
I/O ports
--------------------------- FF00
Empty but unusable for I/O
--------------------------- FEA0
Sprite Attrib Memory (OAM)
--------------------------- FE00
Echo of 8kB Internal RAM
--------------------------- E000
8kB Internal RAM
--------------------------- C000
8kB switchable RAM bank
--------------------------- A000
8kB Video RAM
--------------------------- 8000
32kB Cartridge
--------------------------- 0000
*/
struct Memory {
    vram: [u8; 8192],
    bank: [u8; 8192],
    internal: [u8; 8192],
    oam: [u8; 160],
    ier: u8
}

impl Memory {
    fn new() -> Memory {
        Memory {
            vram: [0u8; 8192],
            bank: [0u8; 8192],
            internal: [0u8; 8192],
            oam: [0u8; 160],
            ier: 0
        }
    }

    // Memory Writing
    fn write(&mut self, addr: u16, value: u8) {
        match addr {
            0x8000 ... 0x9FFF => self.vram[addr as usize - 0x8000] = value,
            0xA000 ... 0xBFFF => self.bank[addr as usize - 0xA000] = value,
            0xC000 ... 0xDFFF => self.internal[addr as usize - 0xC000] = value,
            0xE000 ... 0xFDFF => self.internal[addr as usize - 0xE000] = value,
            0xFE00 ... 0xFE9F => self.oam[addr as usize - 0xFE00] = value,
            0xFFFF            => self.ier = value,
            _ => panic!("ERROR: Out of bounds memory write")
        }
    }

    fn write_word(&mut self, addr: u16, word: u16) {
        self.write(addr + 0, ((word >> 0) & 0xFF) as u8);
        self.write(addr + 1, ((word >> 8) & 0xFF) as u8);
    }

    fn write_dword(&mut self, addr: u16, dword: u32) {
        self.write(addr + 0, ((dword >>  0) & 0xFF) as u8);
        self.write(addr + 1, ((dword >>  8) & 0xFF) as u8);
        self.write(addr + 2, ((dword >> 16) & 0xFF) as u8);
        self.write(addr + 3, ((dword >> 24) & 0xFF) as u8);
    }

    // Memory Reading
    fn read(&self, addr: u16) -> u8 {
        match addr {
            0x8000 ... 0x9FFF => self.vram[addr as usize - 0x8000],
            0xA000 ... 0xBFFF => self.bank[addr as usize - 0xA000],
            0xC000 ... 0xDFFF => self.internal[addr as usize - 0xC000],
            0xE000 ... 0xFDFF => self.internal[addr as usize - 0xE000],
            0xFE00 ... 0xFE9F => self.oam[addr as usize - 0xFE00],
            0xFFFF            => self.ier,
            _ => panic!("ERROR: Out of bounds memory read")
        }
    }

    fn read_word(&self, addr: u16) -> u16 {
        return
            ((self.read(addr + 0) as u16) << 0) +
            ((self.read(addr + 1) as u16) << 8)
    }

    fn read_dword(&self, addr: u16) -> u32 {
        return
            ((self.read(addr + 0) as u32) <<  0) +
            ((self.read(addr + 1) as u32) <<  8) +
            ((self.read(addr + 2) as u32) << 16) +
            ((self.read(addr + 3) as u32) << 24)
    }
}

fn main() {
    let mut memory = Memory::new();
    memory.write_dword(0x8000, 32);
    println!("{}", memory.read_dword(0x8000));
}<|endoftext|>


<commit_before>extern crate mqtt;
extern crate serde_json;
extern crate rs_jsonpath;
#[macro_use]
extern crate clap;
extern crate itertools;

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::sync::Arc;

use mqtt::{Encodable, Decodable};
use mqtt::packet::{ConnackPacket, Packet, PingrespPacket, PubackPacket, PublishPacket, PubrecPacket, PubcompPacket, SubackPacket, VariablePacket};
use mqtt::{TopicFilter, QualityOfService};
use mqtt::control::ConnectReturnCode;
use mqtt::packet::suback::SubscribeReturnCode;
use mqtt::packet::publish::QoSWithPacketIdentifier;

mod csv_data_processor;
use csv_data_processor::CsvDataProcessor;

macro_rules! log(
    ($($arg:tt)*) => { {
        let _ = writeln!(&mut ::std::io::stderr(), $($arg)*);
    } }
);

#[derive(Debug)]
enum ZinkError<'a> {
    Mqtt(mqtt::packet::VariablePacketError<'a>),
    NotMqttConnect,
}

impl<'a, P> From<mqtt::packet::PacketError<'a, P>> for ZinkError<'a>
    where P: mqtt::packet::Packet<'a>,
          mqtt::packet::VariablePacketError<'a>: std::convert::From<mqtt::packet::PacketError<'a, P>> {

    fn from(err: mqtt::packet::PacketError<'a, P>) -> ZinkError<'a> {
        ZinkError::Mqtt(mqtt::packet::VariablePacketError::from(err))
    }
}

impl<'a> From<mqtt::packet::VariablePacketError<'a>> for ZinkError<'a> {
    fn from(err: mqtt::packet::VariablePacketError<'a>) -> ZinkError<'a> {
        ZinkError::Mqtt(err)
    }
}

fn main() {
    let matches = clap_app!(
        zink =>
            (version: "0.1.0")
            (@arg file: -f --file +takes_value "File to append result to. If not specified, send results to stdout")
            (@arg bind: -b --bind +takes_value default_value("0.0.0.0:1883") "Bind address and port")
            (@arg JSONPATH: +required +use_delimiter +multiple "JSON paths to use")
    ).get_matches();

    let jsonpaths: Vec<String> = matches.value_of("JSONPATH").unwrap().split(",").map(String::from).collect();

    let handle: Box<Write + Send> = if let Some(filepath) = matches.value_of("file") {
        Box::new(OpenOptions::new()
                 .append(true)
                 .create(true)
                 .open(filepath)
                 .unwrap()) as Box<Write + Send>
    } else {
        Box::new(io::stdout()) as Box<Write + Send>
    };

    let csv_data_processor = Arc::new(CsvDataProcessor::new(handle, jsonpaths));

    let bind_address = matches.value_of("bind").unwrap();
    let listener = TcpListener::bind(bind_address).unwrap();

    log!("Bound to {}", bind_address);

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let csv_data_processor = csv_data_processor.clone();
                thread::spawn(move || {
                    let res = handle_client(stream, &|ref x: &PublishPacket| {
                        csv_data_processor.process_publish(x);
                    });
                    log!("handle_client exited with {:?}", res);
                });
            }
            Err(e) => {
                log!("{}", e);
            }
        }
    }
}

fn sub_to_ack(&(_, qos): &(TopicFilter, QualityOfService)) -> SubscribeReturnCode {
    match qos {
        QualityOfService::Level0 => SubscribeReturnCode::MaximumQoSLevel0,
        QualityOfService::Level1 => SubscribeReturnCode::MaximumQoSLevel1,
        QualityOfService::Level2 => SubscribeReturnCode::MaximumQoSLevel1,
    }
}

fn handle_client<'a, 'b>(mut stream: TcpStream, process_publish: &'b Fn(&PublishPacket)) -> Result<(), ZinkError<'a>> {
    // This makes .read() call blocking.
    // Otherwise, mqtt decode consumes 100% CPU time.
    let _ = stream.set_read_timeout(None);

    log!("{:?}", stream);
    if let Ok(VariablePacket::ConnectPacket(x)) = VariablePacket::decode(&mut stream) {
        log!("{:?}", x);
        try!(ConnackPacket::new(false, ConnectReturnCode::ConnectionAccepted).encode(&mut stream));
    } else {
        return Err(ZinkError::NotMqttConnect);
    }

    loop {
        let packet = try!(VariablePacket::decode(&mut stream));
        log!("{:?}", packet);
        match packet {
            VariablePacket::SubscribePacket(x) => {
                try!(SubackPacket::new(
                    x.packet_identifier(),
                    x.payload().subscribes().into_iter().map(sub_to_ack).collect()
                ).encode(&mut stream));
            }
            VariablePacket::PingreqPacket(_) => {
                try!(PingrespPacket::new()
                     .encode(&mut stream));
            }
            VariablePacket::PubrelPacket(x) => {
                try!(PubcompPacket::new(x.packet_identifier())
                     .encode(&mut stream));
            }
            VariablePacket::PublishPacket(x) => {
                match x.qos() {
                    QoSWithPacketIdentifier::Level0 => {
                        // No additional handling is required
                    }
                    QoSWithPacketIdentifier::Level1(pkid) => {
                        try!(PubackPacket::new(pkid)
                             .encode(&mut stream));
                    }
                    QoSWithPacketIdentifier::Level2(pkid) => {
                        try!(PubrecPacket::new(pkid)
                             .encode(&mut stream));
                    }
                }

                if let Ok(payload) = std::str::from_utf8(x.payload()) {
                    log!("{}: {}", x.topic_name(), payload);
                } else {
                    log!("{}: {:?}", x.topic_name(), x.payload());
                }

                process_publish(&x);
            }
            _ => {
            }
        }
    }
}
<commit_msg>Fix passing multiple jsonpaths<commit_after>extern crate mqtt;
extern crate serde_json;
extern crate rs_jsonpath;
#[macro_use]
extern crate clap;
extern crate itertools;

use std::net::{TcpListener, TcpStream};
use std::thread;
use std::fs::OpenOptions;
use std::io::{self, Write};
use std::sync::Arc;

use mqtt::{Encodable, Decodable};
use mqtt::packet::{ConnackPacket, Packet, PingrespPacket, PubackPacket, PublishPacket, PubrecPacket, PubcompPacket, SubackPacket, VariablePacket};
use mqtt::{TopicFilter, QualityOfService};
use mqtt::control::ConnectReturnCode;
use mqtt::packet::suback::SubscribeReturnCode;
use mqtt::packet::publish::QoSWithPacketIdentifier;

mod csv_data_processor;
use csv_data_processor::CsvDataProcessor;

macro_rules! log(
    ($($arg:tt)*) => { {
        let _ = writeln!(&mut ::std::io::stderr(), $($arg)*);
    } }
);

#[derive(Debug)]
enum ZinkError<'a> {
    Mqtt(mqtt::packet::VariablePacketError<'a>),
    NotMqttConnect,
}

impl<'a, P> From<mqtt::packet::PacketError<'a, P>> for ZinkError<'a>
    where P: mqtt::packet::Packet<'a>,
          mqtt::packet::VariablePacketError<'a>: std::convert::From<mqtt::packet::PacketError<'a, P>> {

    fn from(err: mqtt::packet::PacketError<'a, P>) -> ZinkError<'a> {
        ZinkError::Mqtt(mqtt::packet::VariablePacketError::from(err))
    }
}

impl<'a> From<mqtt::packet::VariablePacketError<'a>> for ZinkError<'a> {
    fn from(err: mqtt::packet::VariablePacketError<'a>) -> ZinkError<'a> {
        ZinkError::Mqtt(err)
    }
}

fn main() {
    let matches = clap_app!(
        zink =>
            (version: "0.1.0")
            (@arg file: -f --file +takes_value "File to append result to. If not specified, send results to stdout")
            (@arg bind: -b --bind +takes_value default_value("0.0.0.0:1883") "Bind address and port")
            (@arg JSONPATH: +required "JSON paths to use")
    ).get_matches();

    let jsonpaths: Vec<String> = matches.value_of("JSONPATH").unwrap().split(",").map(String::from).collect();

    let handle: Box<Write + Send> = if let Some(filepath) = matches.value_of("file") {
        Box::new(OpenOptions::new()
                 .append(true)
                 .create(true)
                 .open(filepath)
                 .unwrap()) as Box<Write + Send>
    } else {
        Box::new(io::stdout()) as Box<Write + Send>
    };

    let csv_data_processor = Arc::new(CsvDataProcessor::new(handle, jsonpaths));

    let bind_address = matches.value_of("bind").unwrap();
    let listener = TcpListener::bind(bind_address).unwrap();

    log!("Bound to {}", bind_address);

    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let csv_data_processor = csv_data_processor.clone();
                thread::spawn(move || {
                    let res = handle_client(stream, &|ref x: &PublishPacket| {
                        csv_data_processor.process_publish(x);
                    });
                    log!("handle_client exited with {:?}", res);
                });
            }
            Err(e) => {
                log!("{}", e);
            }
        }
    }
}

fn sub_to_ack(&(_, qos): &(TopicFilter, QualityOfService)) -> SubscribeReturnCode {
    match qos {
        QualityOfService::Level0 => SubscribeReturnCode::MaximumQoSLevel0,
        QualityOfService::Level1 => SubscribeReturnCode::MaximumQoSLevel1,
        QualityOfService::Level2 => SubscribeReturnCode::MaximumQoSLevel1,
    }
}

fn handle_client<'a, 'b>(mut stream: TcpStream, process_publish: &'b Fn(&PublishPacket)) -> Result<(), ZinkError<'a>> {
    // This makes .read() call blocking.
    // Otherwise, mqtt decode consumes 100% CPU time.
    let _ = stream.set_read_timeout(None);

    log!("{:?}", stream);
    if let Ok(VariablePacket::ConnectPacket(x)) = VariablePacket::decode(&mut stream) {
        log!("{:?}", x);
        try!(ConnackPacket::new(false, ConnectReturnCode::ConnectionAccepted).encode(&mut stream));
    } else {
        return Err(ZinkError::NotMqttConnect);
    }

    loop {
        let packet = try!(VariablePacket::decode(&mut stream));
        log!("{:?}", packet);
        match packet {
            VariablePacket::SubscribePacket(x) => {
                try!(SubackPacket::new(
                    x.packet_identifier(),
                    x.payload().subscribes().into_iter().map(sub_to_ack).collect()
                ).encode(&mut stream));
            }
            VariablePacket::PingreqPacket(_) => {
                try!(PingrespPacket::new()
                     .encode(&mut stream));
            }
            VariablePacket::PubrelPacket(x) => {
                try!(PubcompPacket::new(x.packet_identifier())
                     .encode(&mut stream));
            }
            VariablePacket::PublishPacket(x) => {
                match x.qos() {
                    QoSWithPacketIdentifier::Level0 => {
                        // No additional handling is required
                    }
                    QoSWithPacketIdentifier::Level1(pkid) => {
                        try!(PubackPacket::new(pkid)
                             .encode(&mut stream));
                    }
                    QoSWithPacketIdentifier::Level2(pkid) => {
                        try!(PubrecPacket::new(pkid)
                             .encode(&mut stream));
                    }
                }

                if let Ok(payload) = std::str::from_utf8(x.payload()) {
                    log!("{}: {}", x.topic_name(), payload);
                } else {
                    log!("{}: {:?}", x.topic_name(), x.payload());
                }

                process_publish(&x);
            }
            _ => {
            }
        }
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature="clippy", plugin(clippy))]
#![feature(plugin)]

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate flate2;
extern crate rusoto;
extern crate crossbeam;
extern crate chrono;
extern crate syslog;
extern crate walkdir;
extern crate ifaces;

use std::io;
use std::io::prelude::*;
use std::fs::{File, remove_file, create_dir_all};
use std::path::Path;
use std::env;
use std::str::FromStr;
use rusoto::{AutoRefreshingProvider, default_tls_client, DefaultCredentialsProvider, Region};
use rusoto::sts::{StsClient, StsAssumeRoleSessionCredentialsProvider};
use rusoto::s3::{S3Client, PutObjectRequest, Metadata};
use flate2::Compression;
use flate2::write::GzEncoder;
use chrono::prelude::Local;
use chrono::{Datelike, Timelike, Duration};
use crossbeam::scope;
use syslog::{Facility, Severity};
use walkdir::WalkDir;

// struct for our config file
#[derive(Serialize, Deserialize)]
struct ConfigFile {
    cachedir: String,
    role_arn: String,
    region: String,
    bucket: String,
    prefix: String,
}

// struct for our system information
struct SystemInfo {
    hostname: String,
    ipaddr: String,
}

// few consts. might make these configurable later
const MAX_LINES: usize = 50000;
const MAX_BYTES: usize = 10485760;
const MAX_TIMEOUT: i64 = 10;
const INTERFACE: &str = "eth0";

fn main() {

    let args: Vec<_> = env::args().collect();
    if args.len() < 1 {
        println!("s3post <config.json>");
    }

    // open our config file
    let file_open = File::open(&args[1]).expect("could not open file");
    // attempt to deserialize the config to our struct
    let config: ConfigFile = serde_json::from_reader(file_open).expect("config has invalid json");

    // need the ipaddr of our interface. will be part of the metadata
    let mut address = String::new();
    for iface in ifaces::Interface::get_all().unwrap() {
        if iface.name == INTERFACE && iface.kind == ifaces::Kind::Ipv4 {
            address = format!("{}", iface.addr.unwrap());
            address = address.replace(":0", "");
        }
    }

    // use our environment variable of hostname. will be essentially the CBID
    let hostname = match env::var("HOSTNAME") {
        Ok(hostname) => hostname,
        Err(err) => panic!("Unable to get hostname. {}", err),
    };

    // store hostname and ip address in our struct
    let system: SystemInfo = SystemInfo {
        hostname: hostname,
        ipaddr: address,
    };

    println!("MAX_LINES:\t{}\tMAX_BYTES:\t{}\tMAX_TIMEOUT:\t{}",
             MAX_LINES,
             MAX_BYTES,
             MAX_TIMEOUT);

    // attempt to resend any logs that we might have not successfully sent
    resend_logs(&config, &system);

    // create a reader from stdin
    let reader = io::stdin();
    // create a buffer from the stdin lock
    let mut buffer = reader.lock();
    // initialize an empty vector
    let mut data = vec![0];
    // grab the current time
    let mut time = Local::now();
    // create initial timeout
    let mut timeout = time + Duration::seconds(MAX_TIMEOUT);

    loop {
        match buffer.fill_buf() {
            Ok(bytes) => {
                // add the current bytes to our data vector
                data.extend(bytes);
                // evaulate if data and time meet of processing conditions
                if data.lines().count() >= MAX_LINES || data.len() >= MAX_BYTES ||
                   timeout <= time && !data.is_empty() {
                    // send the data to the compress function
                    compress(data.as_slice(), time, &config, &system);
                    // clear our data vector
                    data.clear();
                    // reset our timer
                    time = Local::now();
                    timeout = time + Duration::seconds(MAX_TIMEOUT);
                } else {
                    // update the time
                    time = Local::now();
                    // attempt to resend any logs that we might have not successfully sent
                    resend_logs(&config, &system);
                }
            }
            Err(err) => panic!(err),
        }
        // consume the data from the buffer so we don't reprocess it
        buffer.consume(data.len());
    }
}

fn compress(bytes: &[u8],
            timestamp: chrono::DateTime<chrono::Local>,
            config: &ConfigFile,
            system: &SystemInfo) {

    // our compression routine will be sent to another thread
    scope(|scope| {
        scope.spawn(move || {

            // generate our local path
            let path = format!("{}/{}/{}/{}/{}/",
                               timestamp.year(),
                               timestamp.month(),
                               timestamp.day(),
                               timestamp.hour(),
                               timestamp.minute());

            // setting the format for how we write out the file.
            let file = format!("{}.{}-{}.raw.gz",
                               timestamp.second(),
                               timestamp.timestamp_subsec_millis(),
                               &system.hostname);

            // append the cachedir so we write to the correct place
            let fullpath = format!("{}/{}", &config.cachedir, &path);

            // create the directory structure
            let _ = create_dir_all(&fullpath);

            // create the file in the full path
            let mut output = File::create(format!("{}{}", &fullpath, &file)).unwrap();

            // create a gzip encoder
            let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);

            // encode the retrieved bytes
            encoder.write_all(bytes).unwrap();
            let log = encoder.finish().unwrap();

            // write out the encoded data to our file
            output.write_all(&log).unwrap();

            // call write_s3 to send the gzip'd file to s3
            write_s3(&*config, &*system, &file, &path, &log)
        });
    });
}

fn write_s3(config: &ConfigFile, system: &SystemInfo, file: &str, path: &str, log: &[u8]) {

    // move to new thread
    scope(|scope| {
        scope.spawn(move || {

            let path = format!("{}/{}/{}", &config.prefix, &path, &file);

            // set up our credentials provider for aws
            let provider = match DefaultCredentialsProvider::new() {
                Ok(provider) => provider,
                Err(err) => panic!("Unable to load credentials. {}", err),
            };

            // initiate our sts client
            let sts_client = StsClient::new(default_tls_client().unwrap(),
                                            provider,
                                            Region::from_str(&config.region).unwrap());
            // generate a sts provider
            let sts_provider = StsAssumeRoleSessionCredentialsProvider::new(sts_client,
                                                                            config.role_arn
                                                                                .to_owned(),
                                                                            "s3post".to_owned(),
                                                                            None,
                                                                            None,
                                                                            None,
                                                                            None);
            // allow our STS to auto-refresh
            let auto_sts_provider = AutoRefreshingProvider::with_refcell(sts_provider);

            // create our s3 client initialization
            let s3 = S3Client::new(default_tls_client().unwrap(),
                                   auto_sts_provider.unwrap(),
                                   Region::from_str(&config.region).unwrap());

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            contents.extend(log);

            // generate our metadata which we add to the s3 upload
            let mut metadata = Metadata::new();
            metadata.insert("cbid".to_string(), system.hostname.to_string());
            metadata.insert("ip".to_string(), system.ipaddr.to_string());
            metadata.insert("uncompressed_bytes".to_string(), contents.len().to_string());
            metadata.insert("lines".to_string(), contents.lines().count().to_string());

            // if we can read the contents to the buffer we will attempt to send the log to s3
            // need to build our request
            let req = PutObjectRequest {
                bucket: config.bucket.to_owned(),
                key: path.to_owned(),
                body: Some(contents),
                metadata: Some(metadata),
                ..Default::default()
            };

            match s3.put_object(&req) {
                // we were successful!
                Ok(_) => {
                    // Send some notifications to SYSLOG
                    match syslog::unix(Facility::LOG_SYSLOG) {
                        Err(e) => println!("impossible to connect to syslog: {:?}", e),
                        Ok(writer) => {
                            let success = format!("Successfully wrote {}/{}", &req.bucket, &path);
                            let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                        }
                    }
                    // only remove the file if we are successful
                    let localpath = format!("{}/{}", &config.cachedir, &path);
                    if remove_file(&localpath).is_ok() {
                        // Send some notifications to SYSLOG
                        match syslog::unix(Facility::LOG_SYSLOG) {
                            Err(e) => println!("impossible to connect to syslog: {:?}", e),
                            Ok(writer) => {
                                let success = format!("Removed file {}", &localpath);
                                let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                            }
                        }
                    }
                }
                Err(e) => println!("Could not write file {} {}", &file, e),
            }
        });
    });
}

fn resend_logs(config: &ConfigFile, system: &SystemInfo) {

    // create iterator over the directory
    for entry in WalkDir::new(&config.cachedir).into_iter().filter_map(|e| e.ok()) {

        // filter out gzip'd file suffixes
        if entry.file_name()
               .to_str()
               .map(|s| s.ends_with(".gz"))
               .unwrap() {

            // get just the file name
            let filename = entry.file_name().to_str().unwrap();

            // need to return only the parent and strip off the cachedir prefix
            // this then is the path write_s3 is expecting as it is the path within s3
            let path =
                Path::new(entry.path().parent().unwrap()).strip_prefix(&config.cachedir).unwrap();
            let path = Path::new(path).to_str().unwrap();

            // build out the complete path for reading
            let filepath = format!("{}/{}/{}", &config.cachedir, &path, &filename);
            // open the file for reading
            let mut file = File::open(&filepath).expect("Unable to read file");

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            let _ = file.read_to_end(&mut contents);

            // Send some notifications to SYSLOG
            match syslog::unix(Facility::LOG_SYSLOG) {
                Err(e) => println!("impossible to connect to syslog: {:?}", e),
                Ok(writer) => {
                    let success = format!("Found unsent log {}/{}", &path, &filename);
                    let _ = writer.send_3164(Severity::LOG_ALERT, &success).is_ok();
                }
            }
            // pass the unset logs to s3
            write_s3(&*config, &*system, filename, path, &contents)

        }
    }
}
<commit_msg>refinements<commit_after>#![cfg_attr(feature="clippy", plugin(clippy))]
#![feature(plugin)]

#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
extern crate flate2;
extern crate rusoto;
extern crate crossbeam;
extern crate chrono;
extern crate syslog;
extern crate walkdir;
extern crate ifaces;

use std::io;
use std::io::prelude::*;
use std::fs::{File, remove_file, create_dir_all};
use std::path::Path;
use std::env;
use std::str::FromStr;
use rusoto::{AutoRefreshingProvider, default_tls_client, DefaultCredentialsProvider, Region};
use rusoto::sts::{StsClient, StsAssumeRoleSessionCredentialsProvider};
use rusoto::s3::{S3Client, PutObjectRequest, Metadata};
use flate2::Compression;
use flate2::write::GzEncoder;
use chrono::prelude::Local;
use chrono::{Datelike, Timelike, Duration};
use crossbeam::scope;
use syslog::{Facility, Severity};
use walkdir::WalkDir;

// struct for our config file
#[derive(Serialize, Deserialize)]
struct ConfigFile {
    cachedir: String,
    role_arn: String,
    region: String,
    bucket: String,
    prefix: String,
}

// struct for our system information
struct SystemInfo {
    hostname: String,
    ipaddr: String,
}

// few consts. might make these configurable later
const MAX_LINES: usize = 50000;
const MAX_BYTES: usize = 10485760;
const MAX_TIMEOUT: i64 = 30;

fn main() {

    let args: Vec<_> = env::args().collect();
    if args.len() < 1 {
        println!("s3post <config.json>");
    }

    // open our config file
    let file_open = File::open(&args[1]).expect("could not open file");
    // attempt to deserialize the config to our struct
    let config: ConfigFile = serde_json::from_reader(file_open).expect("config has invalid json");

    // determine our hostname.
    let mut hostname = String::new();
    if let Ok(mut file) = File::open("/etc/hostname") {
        let mut buffer = String::new();
        let _ = file.read_to_string(&mut buffer).unwrap().to_string();
        hostname = buffer.trim().to_owned();
    } else {
        hostname = "localhost".to_string();
    }

    // need the ipaddr of our interface. will be part of the metadata
    let mut address = String::new();
    for iface in ifaces::Interface::get_all().unwrap() {
        if iface.kind == ifaces::Kind::Ipv4 {
            address = format!("{}", iface.addr.unwrap());
            address = address.replace(":0", "");
        }
        continue;
    }

    // store hostname and ip address in our struct
    let system: SystemInfo = SystemInfo {
        hostname: hostname,
        ipaddr: address,
    };

    println!("MAX_LINES:\t{}\tMAX_BYTES:\t{}\tMAX_TIMEOUT:\t{}",
             MAX_LINES,
             MAX_BYTES,
             MAX_TIMEOUT);

    // attempt to resend any logs that we might have not successfully sent
    resend_logs(&config, &system);

    // create a reader from stdin
    let reader = io::stdin();
    // create a buffer from the stdin lock
    let mut buffer = reader.lock();
    // initialize an empty vector
    let mut data = vec![0];
    // grab the current time
    let mut time = Local::now();
    // create initial timeout
    let mut timeout = time + Duration::seconds(MAX_TIMEOUT);

    loop {
        match buffer.fill_buf() {
            Ok(bytes) => {
                // add the current bytes to our data vector
                data.extend(bytes);
                // evaulate if data and time meet of processing conditions
                if data.lines().count() >= MAX_LINES || data.len() >= MAX_BYTES ||
                   timeout <= time && !data.is_empty() {
                    // send the data to the compress function
                    compress(data.as_slice(), time, &config, &system);
                    // clear our data vector
                    data.clear();
                    // reset our timer
                    time = Local::now();
                    timeout = time + Duration::seconds(MAX_TIMEOUT);
                } else {
                    // update the time
                    time = Local::now();
                    // attempt to resend any logs that we might have not successfully sent
                    resend_logs(&config, &system);
                }
            }
            Err(err) => panic!(err),
        }
        // consume the data from the buffer so we don't reprocess it
        buffer.consume(data.len());
    }
}

fn compress(bytes: &[u8],
            timestamp: chrono::DateTime<chrono::Local>,
            config: &ConfigFile,
            system: &SystemInfo) {

    // our compression routine will be sent to another thread
    scope(|scope| {
        scope.spawn(move || {

            // generate our local path
            let path = format!("{}/{}/{}/{}/{}",
                               timestamp.year(),
                               timestamp.month(),
                               timestamp.day(),
                               timestamp.hour(),
                               timestamp.minute());

            // setting the format for how we write out the file.
            let file = format!("{}.{}-{}.raw.gz",
                               timestamp.second(),
                               timestamp.timestamp_subsec_millis(),
                               &system.hostname);

            // append the cachedir so we write to the correct place
            let fullpath = format!("{}/{}", &config.cachedir, &path);

            // create the directory structure
            let _ = create_dir_all(&fullpath);

            // create the file in the full path
            let mut output = File::create(format!("{}/{}", &fullpath, &file)).unwrap();

            // create a gzip encoder
            let mut encoder = GzEncoder::new(Vec::new(), Compression::Default);

            // encode the retrieved bytes
            encoder.write_all(bytes).unwrap();
            let log = encoder.finish().unwrap();

            // write out the encoded data to our file
            output.write_all(&log).unwrap();

            // call write_s3 to send the gzip'd file to s3
            write_s3(&*config, &*system, &file, &path, &log)
        });
    });
}

fn write_s3(config: &ConfigFile, system: &SystemInfo, file: &str, path: &str, log: &[u8]) {

    // move to new thread
    scope(|scope| {
        scope.spawn(move || {

            let s3path = format!("{}/{}/{}", &config.prefix, &path, &file);

            // set up our credentials provider for aws
            let provider = match DefaultCredentialsProvider::new() {
                Ok(provider) => provider,
                Err(err) => panic!("Unable to load credentials. {}", err),
            };

            // initiate our sts client
            let sts_client = StsClient::new(default_tls_client().unwrap(),
                                            provider,
                                            Region::from_str(&config.region).unwrap());
            // generate a sts provider
            let sts_provider = StsAssumeRoleSessionCredentialsProvider::new(sts_client,
                                                                            config.role_arn
                                                                                .to_owned(),
                                                                            "s3post".to_owned(),
                                                                            None,
                                                                            None,
                                                                            None,
                                                                            None);
            // allow our STS to auto-refresh
            let auto_sts_provider = AutoRefreshingProvider::with_refcell(sts_provider);

            // create our s3 client initialization
            let s3 = S3Client::new(default_tls_client().unwrap(),
                                   auto_sts_provider.unwrap(),
                                   Region::from_str(&config.region).unwrap());

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            contents.extend(log);

            // generate our metadata which we add to the s3 upload
            let mut metadata = Metadata::new();
            metadata.insert("cbid".to_string(), system.hostname.to_string());
            metadata.insert("ip".to_string(), system.ipaddr.to_string());
            metadata.insert("uncompressed_bytes".to_string(), contents.len().to_string());
            metadata.insert("lines".to_string(), contents.lines().count().to_string());

            // if we can read the contents to the buffer we will attempt to send the log to s3
            // need to build our request
            let req = PutObjectRequest {
                bucket: config.bucket.to_owned(),
                key: s3path.to_owned(),
                body: Some(contents),
                metadata: Some(metadata),
                ..Default::default()
            };

            match s3.put_object(&req) {
                // we were successful!
                Ok(_) => {
                    // Send some notifications to SYSLOG
                    match syslog::unix(Facility::LOG_SYSLOG) {
                        Err(e) => println!("impossible to connect to syslog: {:?}", e),
                        Ok(writer) => {
                            let success = format!("Successfully wrote {}/{}", &req.bucket, &s3path);
                            let _ = writer.send_3164(Severity::LOG_DEBUG, &success).is_ok();
                        }
                    }
                    // only remove the file if we are successful
                    let localpath = format!("{}/{}/{}", &config.cachedir, &path, &file);
                    if remove_file(&localpath).is_ok() {
                        // Send some notifications to SYSLOG
                        match syslog::unix(Facility::LOG_SYSLOG) {
                            Err(e) => println!("impossible to connect to syslog: {:?}", e),
                            Ok(writer) => {
                                let success = format!("Removed file {}", &localpath);
                                let _ = writer.send_3164(Severity::LOG_DEBUG, &success).is_ok();
                            }
                        }
                    } else {
                        println!("Unable to remove file: {}", &localpath);
                    }
                }
                Err(e) => println!("Could not write file {} {}", &file, e),
            }
        });
    });
}

fn resend_logs(config: &ConfigFile, system: &SystemInfo) {

    // create iterator over the directory
    for entry in WalkDir::new(&config.cachedir).into_iter().filter_map(|e| e.ok()) {

        // filter out gzip'd file suffixes
        if entry.file_name()
               .to_str()
               .map(|s| s.ends_with(".gz"))
               .unwrap() {

            // get just the file name
            let filename = entry.file_name().to_str().unwrap();

            // need to return only the parent and strip off the cachedir prefix
            // this then is the path write_s3 is expecting as it is the path within s3
            let path =
                Path::new(entry.path().parent().unwrap()).strip_prefix(&config.cachedir).unwrap();
            let path = Path::new(path).to_str().unwrap();

            // build out the complete path for reading
            let filepath = format!("{}/{}/{}", &config.cachedir, &path, &filename);
            // open the file for reading
            let mut file = File::open(&filepath).expect("Unable to read file");

            // create a u8 vector
            let mut contents: Vec<u8> = Vec::new();
            // add our encoded log file to the vector
            let _ = file.read_to_end(&mut contents);

            // Send some notifications to SYSLOG
            match syslog::unix(Facility::LOG_SYSLOG) {
                Err(e) => println!("impossible to connect to syslog: {:?}", e),
                Ok(writer) => {
                    let success = format!("Found unsent log {}/{}", &path, &filename);
                    let _ = writer.send_3164(Severity::LOG_DEBUG, &success).is_ok();
                }
            }
            // pass the unset logs to s3
            write_s3(&*config, &*system, filename, path, &contents)

        }
    }
}
<|endoftext|>