<commit_before>extern crate bev;

#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;

use bev::{Bez3o2d};

use gfx::traits::FactoryExt;
use gfx::{Device, Primitive};
use gfx::state::Rasterizer;

use glutin::Event;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!{ Vertex {
    pos: [f32; 2] = "v_pos",
    col: [f32; 3] = "v_col",
}}

gfx_pipeline!{ pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    offset: gfx::Global<[f32; 2]> = "offset",
    out: gfx::RenderTarget<ColorFormat> = "r_target",
}}

fn main() {
    let builder = glutin::WindowBuilder::new()
        .with_dimensions(720, 720)
        .with_multisampling(16)
        .with_title("Hello Bézier".into());
    let (window, mut device, mut factory, main_color, _) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);
    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    let sset = factory.create_shader_set(VERT, FRAG).unwrap();
    let pso = factory.create_pipeline_state(
        &sset,
        Primitive::TriangleStrip,
        Rasterizer::new_fill(),
        pipe::new()
    ).unwrap();

    let curve = Bez3o2d::new(
        0.0, 0.0, 1.0, 1.0,
        0.0, 1.0, 0.0, 1.0
    );

    let verts = [Vertex{ pos: [0.0, 0.0], col: [1.0, 1.0, 1.0] }; 31];
    let mut perps = [Vertex{ pos: [0.0, 0.0], col: [1.0, 1.0, 1.0] }; 62];
    for i in 0..verts.len() {
        let t = i as f32/(verts.len()-1) as f32;

        let interp = curve.interp(t);

        let perp = curve.slope(t).normalize().perp() * 0.01;
        perps[i*2].pos = (-perp + interp).into();
        perps[i*2 + 1].pos = (perp + interp).into();
    }

    let circle = gen_circle(16, 0.02, [1.0, 0.0, 0.0]);
    let mut indices = vec![0u16; 42];
    for (ind, i) in indices.iter_mut().enumerate() {
        let ind = ind + 3;
        if ind % 3 == 1 {
            *i = ind as u16/3;
        } else if ind % 3 == 2 {
            *i = ind as u16/3 + 1
        }
    }
    // println!("{:#?}", circle);

    let (perp_buffer, perp_slice) = factory.create_vertex_buffer_with_slice(&perps, ());
    let (cir_buffer, cir_slice) = factory.create_vertex_buffer_with_slice(&circle, &indices[..]);
    let mut data = pipe::Data {
        vbuf: perp_buffer.clone(),
        offset: [0.0, 0.0],
        out: main_color
    };

    'main: loop {
        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main,
                _ => ()
            }
        }

        encoder.clear(&data.out, [0.0, 0.0, 0.0, 1.0]);

        data.offset = [0.0, 0.0];
        data.vbuf = perp_buffer.clone();
        encoder.draw(&perp_slice, &pso, &data);

        data.vbuf = cir_buffer.clone();
        data.offset = curve.start().into();
        encoder.draw(&cir_slice, &pso, &data);
        data.offset = curve.ctrl0().into();
        encoder.draw(&cir_slice, &pso, &data);
        data.offset = curve.ctrl1().into();
        encoder.draw(&cir_slice, &pso, &data);
        data.offset = curve.end().into();
        encoder.draw(&cir_slice, &pso, &data);

        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}

fn gen_circle(divs: u16, scale: f32, col: [f32; 3]) -> Vec<Vertex> {
    use std::f32::consts::PI;
    let mut verts = vec![Vertex{ pos: [0.0, 0.0], col: col}; divs as usize];

    for d in 0..divs {
        let theta = 2.0 * PI * (d as f32/divs as f32);
        let d = d as usize;
        verts[d].pos[0] = theta.sin() * scale;
        verts[d].pos[1] = theta.cos() * scale;
    }

    verts
}

const VERT: &'static [u8] = br#"
    #version 150 core

    uniform vec2 offset;

    in vec2 v_pos;
    in vec3 v_col;
    out vec4 f_col;

    void main() {
        f_col = vec4(v_col, 1.0);
        gl_Position = vec4(v_pos + offset, 0.0, 1.0);
    }
"#;

const FRAG: &'static [u8] = br#"
    #version 150 core

    in vec4 f_col;
    out vec4 r_target;

    void main() {
        r_target = f_col;
    }
"#;<commit_msg>Removed `verts` array, renamed `perp...` variables to `cvert...`<commit_after>extern crate bev;

#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;

use bev::{Bez3o2d};

use gfx::traits::FactoryExt;
use gfx::{Device, Primitive};
use gfx::state::Rasterizer;

use glutin::Event;

pub type ColorFormat = gfx::format::Rgba8;
pub type DepthFormat = gfx::format::DepthStencil;

gfx_vertex_struct!{ Vertex {
    pos: [f32; 2] = "v_pos",
    col: [f32; 3] = "v_col",
}}

gfx_pipeline!{ pipe {
    vbuf: gfx::VertexBuffer<Vertex> = (),
    offset: gfx::Global<[f32; 2]> = "offset",
    out: gfx::RenderTarget<ColorFormat> = "r_target",
}}

fn main() {
    let builder = glutin::WindowBuilder::new()
        .with_dimensions(720, 720)
        .with_multisampling(16)
        .with_title("Hello Bézier".into());
    let (window, mut device, mut factory, main_color, _) =
        gfx_window_glutin::init::<ColorFormat, DepthFormat>(builder);
    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    let sset = factory.create_shader_set(VERT, FRAG).unwrap();
    let pso = factory.create_pipeline_state(
        &sset,
        Primitive::TriangleStrip,
        Rasterizer::new_fill(),
        pipe::new()
    ).unwrap();

    let curve = Bez3o2d::new(
        0.0, 0.0, 1.0, 1.0,
        0.0, 1.0, 0.0, 1.0
    );

    const SAMPLES: usize = 31;
    let mut cverts = [Vertex{ pos: [0.0, 0.0], col: [1.0, 1.0, 1.0] }; SAMPLES * 2];
    for i in 0..SAMPLES {
        let t = i as f32/(SAMPLES-1) as f32;

        let interp = curve.interp(t);

        let perp = curve.slope(t).normalize().perp() * 0.01;
        cverts[i*2].pos = (-perp + interp).into();
        cverts[i*2 + 1].pos = (perp + interp).into();
    }

    let circle = gen_circle(16, 0.02, [1.0, 0.0, 0.0]);
    let mut indices = vec![0u16; 42];
    for (ind, i) in indices.iter_mut().enumerate() {
        let ind = ind + 3;
        if ind % 3 == 1 {
            *i = ind as u16/3;
        } else if ind % 3 == 2 {
            *i = ind as u16/3 + 1
        }
    }

    let (cvert_buffer, cvert_slice) = factory.create_vertex_buffer_with_slice(&cverts, ());
    let (cir_buffer, cir_slice) = factory.create_vertex_buffer_with_slice(&circle, &indices[..]);
    let mut data = pipe::Data {
        vbuf: cvert_buffer.clone(),
        offset: [0.0, 0.0],
        out: main_color
    };

    'main: loop {
        for event in window.poll_events() {
            match event {
                Event::Closed => break 'main,
                _ => ()
            }
        }

        encoder.clear(&data.out, [0.0, 0.0, 0.0, 1.0]);

        data.offset = [0.0, 0.0];
        data.vbuf = cvert_buffer.clone();
        encoder.draw(&cvert_slice, &pso, &data);

        data.vbuf = cir_buffer.clone();
        data.offset = curve.start().into();
        encoder.draw(&cir_slice, &pso, &data);
        data.offset = curve.ctrl0().into();
        encoder.draw(&cir_slice, &pso, &data);
        data.offset = curve.ctrl1().into();
        encoder.draw(&cir_slice, &pso, &data);
        data.offset = curve.end().into();
        encoder.draw(&cir_slice, &pso, &data);

        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}

fn gen_circle(divs: u16, scale: f32, col: [f32; 3]) -> Vec<Vertex> {
    use std::f32::consts::PI;
    let mut verts = vec![Vertex{ pos: [0.0, 0.0], col: col}; divs as usize];

    for d in 0..divs {
        let theta = 2.0 * PI * (d as f32/divs as f32);
        let d = d as usize;
        verts[d].pos[0] = theta.sin() * scale;
        verts[d].pos[1] = theta.cos() * scale;
    }

    verts
}

const VERT: &'static [u8] = br#"
    #version 150 core

    uniform vec2 offset;

    in vec2 v_pos;
    in vec3 v_col;
    out vec4 f_col;

    void main() {
        f_col = vec4(v_col, 1.0);
        gl_Position = vec4(v_pos + offset, 0.0, 1.0);
    }
"#;

const FRAG: &'static [u8] = br#"
    #version 150 core

    in vec4 f_col;
    out vec4 r_target;

    void main() {
        r_target = f_col;
    }
"#;<|endoftext|>


<commit_before>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

//! Utility to transform a synchronous callback into an asynchronous one.

use futures::{Async, AsyncSink, Sink};
use futures::unsync::mpsc::{Receiver, Sender};
use futures_glib::MainLoop;

macro_rules! unexpected_error {
    () => {
        panic!("Unexpected error while sending the default return value of the callback")
    };
}

/// Struct use to set the return value of a synchronous callback.
pub struct Resolver<T: Default> {
    lp: MainLoop,
    send_done: bool,
    tx: Sender<T>,
}

impl<T: Default> Drop for Resolver<T> {
    fn drop(&mut self) {
        if !self.send_done {
            self.send(Default::default());
        }
    }
}

impl<T: Default> Resolver<T> {
    #[doc(hidden)]
    pub fn channel(lp: MainLoop) -> (Self, Receiver<T>) {
        let (tx, rx) = ::futures::unsync::mpsc::channel(1);
        (Resolver::new(lp, tx), rx)
    }

    fn new(lp: MainLoop, tx: Sender<T>) -> Self {
        Resolver {
            lp,
            send_done: false,
            tx,
        }
    }

    /// Set the return value of a synchronous callback in an asynchronous fashion.
    pub fn resolve(&mut self, value: T) {
        self.send(value);
    }

    fn send(&mut self, value: T) {
        match self.tx.start_send(value) {
            Ok(AsyncSink::Ready) => {
                match self.tx.poll_complete() {
                    Ok(result) => assert_eq!(result, Async::Ready(())),
                    Err(_error) => unexpected_error!(),
                }
            },
            _ => unexpected_error!(),
        }
        self.send_done = true;
        self.lp.quit();
    }
}
<commit_msg>Add Clone implementation for Resolver<commit_after>/*
 * Copyright (c) 2017 Boucher, Antoni <bouanto@zoho.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of
 * this software and associated documentation files (the "Software"), to deal in
 * the Software without restriction, including without limitation the rights to
 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
 * the Software, and to permit persons to whom the Software is furnished to do so,
 * subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
 * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
 * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
 * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */

//! Utility to transform a synchronous callback into an asynchronous one.

use futures::{Async, AsyncSink, Sink};
use futures::unsync::mpsc::{Receiver, Sender};
use futures_glib::MainLoop;

macro_rules! unexpected_error {
    () => {
        panic!("Unexpected error while sending the default return value of the callback")
    };
}

/// Struct use to set the return value of a synchronous callback.
#[derive(Clone)]
pub struct Resolver<T: Default> {
    lp: MainLoop,
    send_done: bool,
    tx: Sender<T>,
}

impl<T: Default> Drop for Resolver<T> {
    fn drop(&mut self) {
        if !self.send_done {
            self.send(Default::default());
        }
    }
}

impl<T: Default> Resolver<T> {
    #[doc(hidden)]
    pub fn channel(lp: MainLoop) -> (Self, Receiver<T>) {
        let (tx, rx) = ::futures::unsync::mpsc::channel(1);
        (Resolver::new(lp, tx), rx)
    }

    fn new(lp: MainLoop, tx: Sender<T>) -> Self {
        Resolver {
            lp,
            send_done: false,
            tx,
        }
    }

    /// Set the return value of a synchronous callback in an asynchronous fashion.
    pub fn resolve(&mut self, value: T) {
        self.send(value);
    }

    fn send(&mut self, value: T) {
        match self.tx.start_send(value) {
            Ok(AsyncSink::Ready) => {
                match self.tx.poll_complete() {
                    Ok(result) => assert_eq!(result, Async::Ready(())),
                    Err(_error) => unexpected_error!(),
                }
            },
            _ => unexpected_error!(),
        }
        self.send_done = true;
        self.lp.quit();
    }
}
<|endoftext|>


<commit_before>// Implements the `zakopane checksum` subcommand.

use std::io::Read;
use std::io::Write;

use crate::structs::ChecksumCliOptions;
use crate::structs::ZakopaneError;

const MAX_TASKS: usize = 8;
const READ_SIZE: usize = 2 << 20;

struct ChecksumWithPath {
    checksum: String,
    path: std::path::PathBuf,
}

impl ChecksumWithPath {
    pub fn new(checksum: String, path: std::path::PathBuf) -> Self {
        Self { checksum, path }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    path: std::path::PathBuf,

    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        path: std::path::PathBuf,
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            path,
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(MAX_TASKS)),
            spawn_counter,
            sender,
        }
    }
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

fn checksum_task_impl(path: std::path::PathBuf) -> ChecksumResult {
    let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA256);
    let mut buffer: Vec<u8> = vec![0; READ_SIZE];
    let mut file = std::fs::File::open(&path).map_err(ZakopaneError::Io)?;
    loop {
        let read_bytes = file.read(&mut buffer).map_err(ZakopaneError::Io)?;
        if read_bytes == 0 {
            let checksum = hasher
                .finish()
                .into_iter()
                .map(|byte| format!("{:02x}", byte))
                .collect::<Vec<String>>()
                .join("");
            return Ok(ChecksumWithPath::new(checksum, path));
        }
        hasher
            .write_all(&buffer[..read_bytes])
            .map_err(ZakopaneError::Io)?;
    }
}

// Represents a spawned checksum task.
//
// `_permit` is moved into this checksum task simply to hold onto the
// semaphore-dispensed resource for the duration of this task.
fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    _permit: tokio::sync::OwnedSemaphorePermit,
) {
    let result = checksum_task_impl(path);
    checksum_task_send_result(result, sender);
}

// Represents the spawned collection task.
async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            break;
        }
    }
    if errors != 0 {
        eprintln!("WARNING: {} errors collected", errors);
    }
    sums
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector(
    path: std::path::PathBuf,
) -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(MAX_TASKS);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let join_handle = tokio::task::spawn(collector_task(receiver, spawn_counter.clone()));
    (
        ChecksumTaskDispatcherData::new(path, spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    let walk_iter = walkdir::WalkDir::new(&context.path).into_iter();
    // The `filter_entry()` call is crafted s.t.
    // *    we skip and don't descend into hidden directories
    // *    unless the hidden directory is the target directory, because
    //      the target directory is always the first yielded value from
    //      the `WalkDir`.
    for entry in walk_iter.filter_entry(|e| {
        e.path() == &context.path
            || !e
                .file_name()
                .to_str()
                .map(|path| path.starts_with("."))
                .unwrap_or(false)
    }) {
        if let Ok(direntry) = entry {
            let path = direntry.into_path();
            if let Ok(metadata) = std::fs::symlink_metadata(&path) {
                if !metadata.file_type().is_file() {
                    continue;
                }
            } else {
                continue;
            }

            context
                .spawn_counter
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let permit = context.semaphore.clone().acquire_owned().await.unwrap();
            let sender = context.sender.clone();
            tokio::task::spawn_blocking(move || checksum_task(path, sender, permit));
        }
    }
}

// Pretty-prints the sorted `checksums` in a format much like what the
// `sha256sum` binary outputs.
//
// Note that the standard zakopane snapshot header is not added here.
fn pretty_format_checksums(path: std::path::PathBuf, checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = checksums
        .into_iter()
        .map(|e| {
            format!(
                "{}  ./{}",
                e.checksum,
                e.path.strip_prefix(&path).unwrap().to_str().unwrap()
            )
        })
        .collect();
    buffer.push(String::new());
    buffer.join("\n")
}

async fn checksum_impl(options: ChecksumCliOptions) -> String {
    let (dispatcher_data, join_handle) = spawn_collector(options.path.clone()).await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(options.path, checksums)
}

pub fn checksum(options: ChecksumCliOptions) -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl(options))
}
<commit_msg>checksum: Use ChecksumCliOptions throughout<commit_after>// Implements the `zakopane checksum` subcommand.

use std::io::Read;
use std::io::Write;

use crate::structs::ChecksumCliOptions;
use crate::structs::ZakopaneError;

const MAX_TASKS: usize = 8;
const READ_SIZE: usize = 2 << 20;

struct ChecksumWithPath {
    checksum: String,
    path: std::path::PathBuf,
}

impl ChecksumWithPath {
    pub fn new(checksum: String, path: std::path::PathBuf) -> Self {
        Self { checksum, path }
    }
}

type ChecksumResult = Result<ChecksumWithPath, ZakopaneError>;
type ChecksumTaskJoinHandle = tokio::task::JoinHandle<Vec<ChecksumWithPath>>;

struct ChecksumTaskDispatcherData {
    cli_options: ChecksumCliOptions,

    // Rate-limiter for spawned checksum tasks.
    semaphore: std::sync::Arc<tokio::sync::Semaphore>,

    // Counts total number of spawned checksum tasks.
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,

    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
}

impl ChecksumTaskDispatcherData {
    pub fn new(
        cli_options: ChecksumCliOptions,
        spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
        sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    ) -> Self {
        Self {
            cli_options,
            semaphore: std::sync::Arc::new(tokio::sync::Semaphore::new(MAX_TASKS)),
            spawn_counter,
            sender,
        }
    }
}

// Sends a checksum task result downstream to the collector task.
fn checksum_task_send_result(
    result: ChecksumResult,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
) {
    if let Err(e) = sender.blocking_send(result) {
        eprintln!("BUG: Sender::send() failed: {}", e);
    }
}

fn checksum_task_impl(path: std::path::PathBuf) -> ChecksumResult {
    let mut hasher = crypto_hash::Hasher::new(crypto_hash::Algorithm::SHA256);
    let mut buffer: Vec<u8> = vec![0; READ_SIZE];
    let mut file = std::fs::File::open(&path).map_err(ZakopaneError::Io)?;
    loop {
        let read_bytes = file.read(&mut buffer).map_err(ZakopaneError::Io)?;
        if read_bytes == 0 {
            let checksum = hasher
                .finish()
                .into_iter()
                .map(|byte| format!("{:02x}", byte))
                .collect::<Vec<String>>()
                .join("");
            return Ok(ChecksumWithPath::new(checksum, path));
        }
        hasher
            .write_all(&buffer[..read_bytes])
            .map_err(ZakopaneError::Io)?;
    }
}

// Represents a spawned checksum task.
//
// `_permit` is moved into this checksum task simply to hold onto the
// semaphore-dispensed resource for the duration of this task.
fn checksum_task(
    path: std::path::PathBuf,
    sender: tokio::sync::mpsc::Sender<ChecksumResult>,
    _permit: tokio::sync::OwnedSemaphorePermit,
) {
    let result = checksum_task_impl(path);
    checksum_task_send_result(result, sender);
}

// Represents the spawned collection task.
async fn collector_task(
    mut receiver: tokio::sync::mpsc::Receiver<ChecksumResult>,
    spawn_counter: std::sync::Arc<std::sync::atomic::AtomicUsize>,
) -> Vec<ChecksumWithPath> {
    let mut sums: Vec<ChecksumWithPath> = Vec::new();
    let mut errors: usize = 0;
    loop {
        while let Some(result) = receiver.recv().await {
            match result {
                Ok(digest_with_path) => sums.push(digest_with_path),
                Err(_) => errors += 1,
            }
        }
        if sums.len() + errors == spawn_counter.load(std::sync::atomic::Ordering::SeqCst) {
            break;
        }
    }
    if errors != 0 {
        eprintln!("WARNING: {} errors collected", errors);
    }
    sums
}

// Spawns the collector task that listens for checksum results
// provided by the checksum tasks.
async fn spawn_collector(
    options: ChecksumCliOptions,
) -> (ChecksumTaskDispatcherData, ChecksumTaskJoinHandle) {
    let (sender, receiver) = tokio::sync::mpsc::channel(MAX_TASKS);
    let spawn_counter = std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0));
    let join_handle = tokio::task::spawn(collector_task(receiver, spawn_counter.clone()));
    (
        ChecksumTaskDispatcherData::new(options, spawn_counter, sender),
        join_handle,
    )
}

async fn spawn_checksum_tasks(context: ChecksumTaskDispatcherData) {
    let walk_iter = walkdir::WalkDir::new(&context.cli_options.path).into_iter();
    // The `filter_entry()` call is crafted s.t.
    // *    we skip and don't descend into hidden directories
    // *    unless the hidden directory is the target directory, because
    //      the target directory is always the first yielded value from
    //      the `WalkDir`.
    for entry in walk_iter.filter_entry(|e| {
        e.path() == &context.cli_options.path
            || !e
                .file_name()
                .to_str()
                .map(|path| path.starts_with("."))
                .unwrap_or(false)
    }) {
        if let Ok(direntry) = entry {
            let path = direntry.into_path();
            if let Ok(metadata) = std::fs::symlink_metadata(&path) {
                if !metadata.file_type().is_file() {
                    continue;
                }
            } else {
                continue;
            }

            context
                .spawn_counter
                .fetch_add(1, std::sync::atomic::Ordering::SeqCst);
            let permit = context.semaphore.clone().acquire_owned().await.unwrap();
            let sender = context.sender.clone();
            tokio::task::spawn_blocking(move || checksum_task(path, sender, permit));
        }
    }
}

// Pretty-prints the sorted `checksums` in a format much like what the
// `sha256sum` binary outputs.
//
// Note that the standard zakopane snapshot header is not added here.
fn pretty_format_checksums(path: std::path::PathBuf, checksums: Vec<ChecksumWithPath>) -> String {
    let mut buffer: Vec<String> = checksums
        .into_iter()
        .map(|e| {
            format!(
                "{}  ./{}",
                e.checksum,
                e.path.strip_prefix(&path).unwrap().to_str().unwrap()
            )
        })
        .collect();
    buffer.push(String::new());
    buffer.join("\n")
}

async fn checksum_impl(options: ChecksumCliOptions) -> String {
    let path = options.path.clone();
    let (dispatcher_data, join_handle) = spawn_collector(options).await;
    spawn_checksum_tasks(dispatcher_data).await;
    let mut checksums: Vec<ChecksumWithPath> = join_handle.await.unwrap();
    checksums.sort_by(|a, b| a.path.cmp(&b.path));
    pretty_format_checksums(path, checksums)
}

pub fn checksum(options: ChecksumCliOptions) -> String {
    let runtime = tokio::runtime::Runtime::new().unwrap();
    runtime.block_on(checksum_impl(options))
}
<|endoftext|>


<commit_before>//*****************************
// Java Codecity data model

extern crate classreader;

use self::classreader::*;
use std::collections::HashMap;
use std::cmp;

#[derive(Debug)]
pub struct Building {
    name:   String,
    width:  u16,
    depth:  u16,
    height: u16,
    // Placement
    pos_w:  u16,
    pos_d:  u16
}

impl Building {
    fn new(class: &Class) -> Building {
        Building {
            name: get_class_name(class).to_string(),
            width: class.methods.len() as u16 + 1,
            depth: class.fields.len() as u16 + 1,
            height: get_total_code_size(&class) as u16 / 10 + 1,
            pos_w: 0,
            pos_d: 0
        }
    }
}

#[derive(Debug)]
pub struct Package {
    name:  String,
    width: u16,
    depth: u16,
    packages:  HashMap<String, Package>,
    buildings: Vec<Building>
}

impl Package {
    /// Create a new Package.
    pub fn new(name: &str) -> Package {
        Package {
            name: name.to_string(),
            width: 1,
            depth: 1,
            packages: HashMap::new(),
            buildings: Vec::new()
        }
    }
    /// Add a class or package to this package
    fn add(&mut self, names: &[&str], class: &Class) {
        if names.len() == 1 {
            self.buildings.push(Building::new(class));
        } else {
            match names.split_first() {
                Some((head, tail)) => {
                    self.packages.entry(head.to_string()).or_insert_with(|| Package::new(head)).add(&tail, class);
                },
                _ => {}
            }
        }
    }

    /// Pack own members for the nice rendering
    fn pack(&mut self) {
        for (_, ch) in &mut self.packages {
            ch.pack();
        }
        let mut free_w = 0;
        let mut free_d = 0;
        let mut cur_w = 1;
        let mut cur_d = 1;
        // TODO Packages
        // Buildings
        self.buildings.sort_by_key(|b| b.width * b.depth);
        self.buildings.reverse();
        for b in &mut self.buildings {
            // println!("Used: {}x{} free: {}x{} placing {}x{}", cur_w, cur_d, free_w, free_d, b.width, b.depth);
            if b.width >= free_w || b.depth >= free_d {
                b.pos_w = self.width;
                b.pos_d = 1;
                cur_w = self.width;
                cur_d = 1 + b.depth;
                // grow by width always for now
                self.width += b.width + 1;
                self.depth = cmp::max(self.depth, b.depth + 2);
                free_w = b.width;
                free_d = self.depth - b.depth - 2;
            } else {
                // Enough room
                b.pos_w = cur_w;
                b.pos_d = cur_d;
                free_d -= b.depth;
                cur_d += b.depth;
            }
        }
    }
}

pub fn build_packages(classes: &Vec<Class>) -> Package {
    let mut root_pkg = Package::new("_root_");
    for c in classes {
        let name = get_class_name(&c);
        let names: Vec<&str> = name.split('/').collect();
        root_pkg.add(&names, c);
    }
    return root_pkg;
}

pub fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => {
                    let end = get_opt_line_number(line_table.last());
                    let start = get_opt_line_number(line_table.first());
                    if start > end { // TODO: Investigate when it happens
                        0
                    } else {
                        end - start
                    }
                },
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

//*****************************
// Unit tests

#[test]
fn pack_buildings() {
    fn mock(name: &str, w: u16, d: u16) -> Building {
        Building {
            name: name.to_string(),
            width: w,
            depth: d,
            height: 0,
            pos_w: 0,
            pos_d: 0
        }
    };
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("b", 8, 3));
    pkg.buildings.push(mock("c", 4, 2));
    pkg.buildings.push(mock("a", 10, 7));
    pkg.buildings.push(mock("d", 3, 2));
    pkg.buildings.push(mock("e1", 1, 1));
    pkg.buildings.push(mock("e2", 1, 1));
    pkg.buildings.push(mock("e3", 1, 1));
    println!("Starting the test");
    println!("{:?}", pkg);
    pkg.pack();
    println!("{:?}", pkg);
    assert!(false);
}
<commit_msg>Made the initial attempt to pack in two directions<commit_after>//*****************************
// Java Codecity data model

extern crate classreader;

use self::classreader::*;
use std::collections::HashMap;
use std::cmp;

#[derive(Debug)]
pub struct Building {
    name:   String,
    width:  u16,
    depth:  u16,
    height: u16,
    // Placement
    pos_w:  u16,
    pos_d:  u16
}

impl Building {
    fn new(class: &Class) -> Building {
        Building {
            name: get_class_name(class).to_string(),
            width: class.methods.len() as u16 + 1,
            depth: class.fields.len() as u16 + 1,
            height: get_total_code_size(&class) as u16 / 10 + 1,
            pos_w: 0,
            pos_d: 0
        }
    }
}

enum Direction {
    Width,
    Depth
}

#[derive(Debug)]
pub struct Package {
    name:  String,
    width: u16,
    depth: u16,
    packages:  HashMap<String, Package>,
    buildings: Vec<Building>
}

impl Package {
    /// Create a new Package.
    pub fn new(name: &str) -> Package {
        Package {
            name: name.to_string(),
            width: 1,
            depth: 1,
            packages: HashMap::new(),
            buildings: Vec::new()
        }
    }
    /// Add a class or package to this package
    fn add(&mut self, names: &[&str], class: &Class) {
        if names.len() == 1 {
            self.buildings.push(Building::new(class));
        } else {
            match names.split_first() {
                Some((head, tail)) => {
                    self.packages.entry(head.to_string()).or_insert_with(|| Package::new(head)).add(&tail, class);
                },
                _ => {}
            }
        }
    }

    /// Pack own members for the nice rendering
    fn pack(&mut self) {
        for (_, ch) in &mut self.packages {
            ch.pack();
        }
        let mut free_w = 0;
        let mut free_d = 0;
        let mut cur_w = 1;
        let mut cur_d = 1;
        let mut dir = Direction::Width;
        // TODO Packages
        // Buildings
        self.buildings.sort_by_key(|b| b.width * b.depth);
        self.buildings.reverse();
        for b in &mut self.buildings {
            println!("Used: {}x{} free: {}x{} placing {}x{}", cur_w, cur_d, free_w, free_d, b.width, b.depth);
            if b.width > free_w || b.depth > free_d {
                // decide the direction for growth
                if self.width <= self.depth {
                    // Grow by width
                    dir = Direction::Width;
                    b.pos_w = self.width;
                    b.pos_d = 1;
                    cur_w = self.width;
                    cur_d = 2 + b.depth;
                    self.width += b.width + 1;
                    self.depth = cmp::max(self.depth, b.depth + 2);
                    free_w = b.width;
                    free_d = self.depth - b.depth - 2;
                } else {
                    // Grow by depth
                    dir = Direction::Depth;
                    b.pos_d = self.depth;
                    b.pos_w = 1;
                    cur_d = self.depth;
                    cur_w = 2 + b.width;
                    self.depth += b.depth + 1;
                    self.width = cmp::max(self.width, b.width + 2);
                    free_d = b.depth;
                    free_w = self.width - b.width - 2;
                }
            } else {
                // Enough room
                match dir {
                    Direction::Width => {
                        b.pos_w = cur_w;
                        b.pos_d = cur_d;
                        free_d -= b.depth;
                        cur_d += b.depth;
                    }
                    Direction::Depth => {
                        b.pos_d = cur_d;
                        b.pos_w = cur_w;
                        free_w -= b.width;
                        cur_w += b.width;
                    }
                }
            }
        }
    }
}

pub fn build_packages(classes: &Vec<Class>) -> Package {
    let mut root_pkg = Package::new("_root_");
    for c in classes {
        let name = get_class_name(&c);
        let names: Vec<&str> = name.split('/').collect();
        root_pkg.add(&names, c);
    }
    return root_pkg;
}

pub fn process_class(class: &Class) {
    assert_eq!(0xCAFEBABE, class.magic);
    println!("class: {}", get_class_name(&class));
    println!("method count: {}", class.methods.len());
    println!("field count:  {}", class.fields.len());
    println!("code size:    {}", get_total_code_size(&class));
    for method in &class.methods {
        println!("\tmethod: {}{}", get_string(&class, method.name_index as usize), get_string(&class, method.descriptor_index as usize));
        println!("\t\tsize:       {}", get_method_size(&method));
        println!("\t\tlines:      {}", get_method_lines(&method));
        println!("\t\tcomplexity: {}", get_method_complexity(&method));
    }

    println!("======================================================");
}

/// Get constant from a pool, correcting for java's 1-based indexes.
fn get_const(class: &Class, i: usize) -> &ConstantPoolInfo {
    &class.constant_pool[i - 1]
}

/// Get string from constant pool
fn get_string(class: &Class, index: usize) -> String {
    match get_const(class, index) {
        &ConstantPoolInfo::Utf8(ref s) => s.clone(),
        _ => "?".to_string()
    }
}

fn get_class_name(class: &Class) -> String {
    match get_const(class, class.this_class as usize) {
        &ConstantPoolInfo::Class(index) => get_string(class, index as usize),
        _ => "?".to_string()
    }
}

fn get_total_code_size(class: &Class) -> usize {
    class.methods.iter().fold(0usize, |sum, method| sum + get_method_size(method))
}

/// Compute method size in byte code instructions
/// If multiple code segments are present in a method (not a javac output) sum it up
fn get_method_size(method: &Method) -> usize {
    method.attributes.iter().fold(0usize, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => code.len(),
            _ => 0
        })
}

/// Compute method size in lines of code.
/// If multiple code segments or multiple line number attrbitues are present in a method (not a javac output) sum it up
fn get_method_lines(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref attributes, ..} => attributes.iter().fold(0, |sum, a| sum + match a {
                &Attribute::LineNumberTable(ref line_table) => {
                    let end = get_opt_line_number(line_table.last());
                    let start = get_opt_line_number(line_table.first());
                    if start > end { // TODO: Investigate when it happens
                        0
                    } else {
                        end - start
                    }
                },
                _ => 0
            }),
            _ => 0
        })

}

fn get_opt_line_number(line: Option<&LineNumber>) -> u16 {
    line.map(|x| x.line_number).unwrap_or(0)
}

fn get_method_complexity(method: &Method) -> u16 {
    method.attributes.iter().fold(0, |sum, a| sum + match a {
            &Attribute::Code{ref code, ..} => get_code_complexity(code),
            _ => 0
        })
}

/// Using complexity definition from http://www.leepoint.net/principles_and_practices/complexity/complexity-java-method.html
/// Start with 1 for the method. Add one for each of the following flow-related elements that are found in the method.
/// Returns     Each return that isn't the last statement of a method.
/// Selection   if, else, case, default.
/// Loops   for, while, do-while, break, and continue.
/// Operators   &&, ||, ?, and :
/// Exceptions  catch, finally, throw, or throws clause.
/// Bytecode reference: http://www.angelibrary.com/computer/java_21days/ch32.htm
fn get_code_complexity(code: &Vec<(u32, Instruction)>) -> u16 {
    // println!("{:?}", code);
    // We start from zero to account for always-present final return - it will add the "starting" 1 for us.
    code.iter().fold(0, |sum, instruction| sum + match instruction.1 {
        Instruction::areturn => 1,
        Instruction::athrow => 1,
        Instruction::dreturn => 1,
        Instruction::freturn => 1,
        Instruction::if_acmpeq(..) => 1,
        Instruction::if_acmpne(..) => 1,
        Instruction::if_icmpeq(..) => 1,
        Instruction::if_icmpne(..) => 1,
        Instruction::if_icmplt(..) => 1,
        Instruction::if_icmpge(..) => 1,
        Instruction::if_icmpgt(..) => 1,
        Instruction::if_icmple(..) => 1,
        Instruction::ifeq(..) => 1,
        Instruction::ifne(..) => 1,
        Instruction::iflt(..) => 1,
        Instruction::ifge(..) => 1,
        Instruction::ifgt(..) => 1,
        Instruction::ifle(..) => 1,
        Instruction::ifnonnull(..) => 1,
        Instruction::ifnull(..) => 1,
        Instruction::ireturn => 1,
        Instruction::lookupswitch(_, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        Instruction::lreturn => 1,
        Instruction::return_ => 1,
        Instruction::tableswitch(_, _, ref jumps) => jumps.len() as u16, // tricky - lets use the size of the jump table
        // Subroutines in the method. Currently - do not count them towards the complexity. They are used to compile "finally", but I do not see them as branching really. Finally block is executed every time
        // Instruction::jsr(..) => 1,
        // Instruction::jsr_w(..) => 1,
        // Instruction::ret => 1,
        // Instruction::ret_w => 1,
        _ => 0
    })
}

//*****************************
// Unit tests

fn mock(name: &str, w: u16, d: u16) -> Building {
  Building {
    name: name.to_string(),
    width: w,
    depth: d,
    height: 0,
    pos_w: 0,
    pos_d: 0
  }
}

#[test]
fn pack_4_singles() {
    let mut pkg = Package::new("_root_");
    for _ in 0..4 {
        pkg.buildings.push(mock("a", 1, 1));
    }
    pkg.pack();
    println!("{:?}", pkg);
    for b in &pkg.buildings {
        println!("{:?}", b);
    }
    assert!(pkg.width == 5);
    assert!(pkg.depth == 5);
    // Make sure the last building is in the corner
    assert!(pkg.buildings[3].pos_w == 3);
    assert!(pkg.buildings[3].pos_d == 3);

}

#[test]
fn pack_buildings() {
    let mut pkg = Package::new("_root_");
    pkg.buildings.push(mock("b", 8, 3));
    pkg.buildings.push(mock("c", 4, 2));
    pkg.buildings.push(mock("a", 10, 7));
    pkg.buildings.push(mock("d", 3, 2));
    pkg.buildings.push(mock("e1", 1, 1));
    pkg.buildings.push(mock("e2", 1, 1));
    pkg.buildings.push(mock("e3", 1, 1));
    println!("Starting the test");
    println!("{:?}", pkg);
    pkg.pack();
    println!("{:?}", pkg);
    assert!(true);
}
<|endoftext|>


<commit_before>use std::io::process::Command;
use config::Config;
use xlib_window_system::XlibWindowSystem;
use workspaces::{Workspaces, MoveOp};
use xlib::Window;

pub enum Cmd {
  Exec(String),
  SwitchWorkspace(uint),
  SwitchScreen(uint),
  MoveToWorkspace(uint),
  MoveToScreen(uint),
  KillClient,
  FocusUp,
  FocusDown,
  FocusMaster,
  SwapUp,
  SwapDown,
  SwapMaster,
}

impl Cmd {
  pub fn call(&self, ws: &XlibWindowSystem, workspaces: &mut Workspaces, config: &Config) {
    match *self {
      Cmd::Exec(ref cmd) => {
        debug!("Cmd::Exec: {}", cmd);
        exec(cmd.clone());
      },
      Cmd::SwitchWorkspace(index) => {
        debug!("Cmd::SwitchWorkspace: {}", index);
        workspaces.switch_to(ws, config, index - 1);
      },
      Cmd::SwitchScreen(screen) => {
        debug!("Cmd::SwitchScreen: {}", screen);
        workspaces.switch_to_screen(ws, config, screen - 1);
      },
      Cmd::MoveToWorkspace(index) => {
        debug!("Cmd::MoveToWorkspace: {}", index);
        workspaces.move_window_to(ws, config, index - 1);
      },
      Cmd::MoveToScreen(screen) => {
        debug!("Cmd::MoveToScreen: {}", screen);
        workspaces.move_window_to_screen(ws, config, screen - 1);
      }
      Cmd::KillClient => {
        let focused_window = workspaces.current_mut().get_focused_window();
        debug!("Cmd::KillClient: {}", focused_window);
        ws.kill_window(focused_window);
      },
      Cmd::FocusUp => {
        debug!("Cmd::FocusUp: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_focus(ws, config, MoveOp::Up);
      },
      Cmd::FocusDown => {
        debug!("Cmd::FocusDown: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_focus(ws, config, MoveOp::Down);
      },
      Cmd::FocusMaster => {
        debug!("Cmd::FocusMaster: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_focus(ws, config, MoveOp::Swap);
      },
      Cmd::SwapUp => {
        debug!("Cmd::SwapUp: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_window(ws, config, MoveOp::Up);
      },
      Cmd::SwapDown => {
        debug!("Cmd::SwapDown: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_window(ws, config, MoveOp::Down);
      },
      Cmd::SwapMaster => {
        debug!("Cmd::SwapMaster: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_window(ws, config, MoveOp::Swap);
      }
    }
  }
}

pub struct ManageHook {
  pub class_name: String,
  pub cmd: CmdManage
}

pub enum CmdManage {
  Move(uint),
  Float,
  Fullscreen,
  Ignore
}

impl CmdManage {
  pub fn call(&self, ws: &XlibWindowSystem, workspaces: &mut Workspaces, config: &Config, window: Window) {
    match *self {
      CmdManage::Move(index) => {
        debug!("CmdManage::Move: {}, {}", window, index - 1);
        workspaces.get_mut(index - 1).add_window(ws, config, window);
        workspaces.get_mut(index - 1).focus_window(ws, config, window);
      },
      CmdManage::Float => {
        debug!("CmdManage::Float");
        unimplemented!()
      },
      CmdManage::Fullscreen => {
        debug!("CmdManage::Fullscreen");
        unimplemented!()
      },
      CmdManage::Ignore => {
        debug!("CmdManage::Ignore");
        unimplemented!()
      }
    }
  }
}

pub enum LogInfo {
  Workspaces(Vec<String>, uint, Vec<uint>),
  Title(String),
  Layout(String)
}

pub struct LogHook<'a> {
  pub logs: Vec<CmdLogHook>,
  pub output: |Vec<LogInfo>|:'a -> String
}

impl<'a> LogHook<'a> {
  pub fn call(&mut self, ws: &XlibWindowSystem, workspaces: &Workspaces) {
    println!("{}", (self.output)(self.logs.iter().map(|x| x.call(ws, workspaces)).collect()));
  }
}

pub enum CmdLogHook {
  Workspaces,
  Title,
  Layout
}

impl CmdLogHook {
  pub fn call(&self, ws: &XlibWindowSystem, workspaces: &Workspaces) -> LogInfo {
    match *self {
      CmdLogHook::Workspaces => {
        LogInfo::Workspaces(workspaces.all().iter().map(|x| x.get_tag()).collect(), workspaces.get_index(), Vec::new())
      },
      CmdLogHook::Title => {
        LogInfo::Title(ws.get_window_title(workspaces.current().get_focused_window()))
      },
      CmdLogHook::Layout => {
        LogInfo::Layout(workspaces.current().get_layout().name())
      }
    }
  }
}

fn exec(cmd: String) {
  spawn(move || {
    let args : Vec<&str> = cmd.as_slice().split(' ').collect();

    if args.len() > 0 {
      let mut cmd = Command::new(args[0]);

      if args.len() > 1 {
        cmd.args(args.as_slice().slice_from(1));
      }

      match cmd.detached().spawn() {
        Ok(_) => (),
        _ => panic!("failed to start \"{}\"", cmd)
      }
    }

  });
}
<commit_msg>removed depricated spawn call<commit_after>use std::io::process::Command;
use std::thread::Thread;
use config::Config;
use xlib_window_system::XlibWindowSystem;
use workspaces::{Workspaces, MoveOp};
use xlib::Window;

pub enum Cmd {
  Exec(String),
  SwitchWorkspace(uint),
  SwitchScreen(uint),
  MoveToWorkspace(uint),
  MoveToScreen(uint),
  KillClient,
  FocusUp,
  FocusDown,
  FocusMaster,
  SwapUp,
  SwapDown,
  SwapMaster,
}

impl Cmd {
  pub fn call(&self, ws: &XlibWindowSystem, workspaces: &mut Workspaces, config: &Config) {
    match *self {
      Cmd::Exec(ref cmd) => {
        debug!("Cmd::Exec: {}", cmd);
        exec(cmd.clone());
      },
      Cmd::SwitchWorkspace(index) => {
        debug!("Cmd::SwitchWorkspace: {}", index);
        workspaces.switch_to(ws, config, index - 1);
      },
      Cmd::SwitchScreen(screen) => {
        debug!("Cmd::SwitchScreen: {}", screen);
        workspaces.switch_to_screen(ws, config, screen - 1);
      },
      Cmd::MoveToWorkspace(index) => {
        debug!("Cmd::MoveToWorkspace: {}", index);
        workspaces.move_window_to(ws, config, index - 1);
      },
      Cmd::MoveToScreen(screen) => {
        debug!("Cmd::MoveToScreen: {}", screen);
        workspaces.move_window_to_screen(ws, config, screen - 1);
      }
      Cmd::KillClient => {
        let focused_window = workspaces.current_mut().get_focused_window();
        debug!("Cmd::KillClient: {}", focused_window);
        ws.kill_window(focused_window);
      },
      Cmd::FocusUp => {
        debug!("Cmd::FocusUp: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_focus(ws, config, MoveOp::Up);
      },
      Cmd::FocusDown => {
        debug!("Cmd::FocusDown: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_focus(ws, config, MoveOp::Down);
      },
      Cmd::FocusMaster => {
        debug!("Cmd::FocusMaster: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_focus(ws, config, MoveOp::Swap);
      },
      Cmd::SwapUp => {
        debug!("Cmd::SwapUp: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_window(ws, config, MoveOp::Up);
      },
      Cmd::SwapDown => {
        debug!("Cmd::SwapDown: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_window(ws, config, MoveOp::Down);
      },
      Cmd::SwapMaster => {
        debug!("Cmd::SwapMaster: {}", workspaces.current_mut().get_focused_window());
        workspaces.current_mut().move_window(ws, config, MoveOp::Swap);
      }
    }
  }
}

pub struct ManageHook {
  pub class_name: String,
  pub cmd: CmdManage
}

pub enum CmdManage {
  Move(uint),
  Float,
  Fullscreen,
  Ignore
}

impl CmdManage {
  pub fn call(&self, ws: &XlibWindowSystem, workspaces: &mut Workspaces, config: &Config, window: Window) {
    match *self {
      CmdManage::Move(index) => {
        debug!("CmdManage::Move: {}, {}", window, index - 1);
        workspaces.get_mut(index - 1).add_window(ws, config, window);
        workspaces.get_mut(index - 1).focus_window(ws, config, window);
      },
      CmdManage::Float => {
        debug!("CmdManage::Float");
        unimplemented!()
      },
      CmdManage::Fullscreen => {
        debug!("CmdManage::Fullscreen");
        unimplemented!()
      },
      CmdManage::Ignore => {
        debug!("CmdManage::Ignore");
        unimplemented!()
      }
    }
  }
}

pub enum LogInfo {
  Workspaces(Vec<String>, uint, Vec<uint>),
  Title(String),
  Layout(String)
}

pub struct LogHook<'a> {
  pub logs: Vec<CmdLogHook>,
  pub output: |Vec<LogInfo>|:'a -> String
}

impl<'a> LogHook<'a> {
  pub fn call(&mut self, ws: &XlibWindowSystem, workspaces: &Workspaces) {
    println!("{}", (self.output)(self.logs.iter().map(|x| x.call(ws, workspaces)).collect()));
  }
}

pub enum CmdLogHook {
  Workspaces,
  Title,
  Layout
}

impl CmdLogHook {
  pub fn call(&self, ws: &XlibWindowSystem, workspaces: &Workspaces) -> LogInfo {
    match *self {
      CmdLogHook::Workspaces => {
        LogInfo::Workspaces(workspaces.all().iter().map(|x| x.get_tag()).collect(), workspaces.get_index(), Vec::new())
      },
      CmdLogHook::Title => {
        LogInfo::Title(ws.get_window_title(workspaces.current().get_focused_window()))
      },
      CmdLogHook::Layout => {
        LogInfo::Layout(workspaces.current().get_layout().name())
      }
    }
  }
}

fn exec(cmd: String) {
  Thread::spawn(move || {
    let args : Vec<&str> = cmd.as_slice().split(' ').collect();

    if args.len() > 0 {
      let mut cmd = Command::new(args[0]);

      if args.len() > 1 {
        cmd.args(args.as_slice().slice_from(1));
      }

      match cmd.detached().spawn() {
        Ok(_) => (),
        _ => panic!("failed to start \"{}\"", cmd)
      }
    }

  }).detach();
}
<|endoftext|>


<commit_before>use alphred::Item;
use chrono::prelude::*;
use reqwest;

use coordinate::Coordinate;
use errors::*;
use forecast;
use sparkline;
use theme::Theme;

#[derive(Debug)]
pub struct Location {
    pub description: String,
    pub coord: Coordinate,
}

pub struct DarkSky {
    pub dark_sky_api_key: String,
    pub location: Location,
    pub theme: Theme,
    pub units: forecast::Units,
}

impl DarkSky {
    pub fn run(&self) -> Result<()> {
        let forecast = self.forecast(self.location.coord.clone())?;

        let mut items = Vec::new();

        let item = Item::new(self.location.description.clone())
            .subtitle("• Powered by Dark Sky")
            .arg(&self.arg())
            .icon("icons/dark_sky.png");
        items.push(item);

        if let Some(item) = forecast.currently.and_then(|point| self.currently(&point)) {
            items.push(item);
        }

        if let Some(item) = forecast.minutely.and_then(|block| self.minutely(&block)) {
            items.push(item);
        }

        if let Some(daily) = forecast.daily {
            daily
                .data
                .iter()
                .take(5)
                .flat_map(|point| self.daily(point))
                .for_each(|x| items.push(x));
        }

        let json = json!({ "items": items });
        println!("{}", json);

        Ok(())
    }

    fn forecast(&self, coord: Coordinate) -> Result<forecast::Forecast> {
        let Coordinate(lat, long) = coord;
        let units = match self.units {
            forecast::Units::Auto => "auto",
            forecast::Units::Ca => "ca",
            forecast::Units::Uk2 => "uk2",
            forecast::Units::Us => "us",
            forecast::Units::Si => "si",
        };
        let url = format!(
            "https://api.darksky.net/forecast/{}/{},{}?units={}",
            self.dark_sky_api_key,
            lat,
            long,
            units,
        );
        Ok(reqwest::get(&url)?.json()?)
    }

    fn arg(&self) -> String {
        let Coordinate(lat, long) = self.location.coord;
        format!("{:.4},{:.4}", lat, long)
    }

    fn currently(&self, point: &forecast::Point) -> Option<Item> {
        if let (Some(title), Some(temp), Some(apparent_temp), Some(icon)) = (
            point.summary.clone(),
            point.temp,
            point.apparent_temperature.clone(),
            point.icon.clone(),
        ) {
            let mut subtitle = vec![
                format!("{}°", temp.round()),
                format!("Feels like {}", apparent_temp),
            ];
            if let Some(human_precip) = point.human_precipitation() {
                subtitle.push(human_precip);
            }
            let subtitle = subtitle.join(" · ");

            let mut item = Item::new(title);
            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = self.theme.icon_path(&icon) {
                item = item.icon(path.as_path());
            }
            Some(item)
        } else {
            None
        }
    }

    fn minutely(&self, block: &forecast::Block) -> Option<Item> {
        if let (Some(title), Some(icon)) = (block.summary.clone(), block.icon.clone()) {
            let mut item = Item::new(title);

            let mut subtitle = Vec::new();
            let intensities = block.precip_intensities();
            if let (Some(min), Some(max)) = (intensities.iter().min(), intensities.iter().max()) {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    max.0,
                    intensities.clone().iter().map(|x| x.0).collect(),
                    4,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let probabilities = block.precip_probabilities();
            if let (Some(min), Some(max)) = (probabilities.iter().min(), probabilities.iter().max())
            {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    1.,
                    probabilities.clone().iter().map(|x| x.0).collect(),
                    5,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let subtitle = subtitle.join(" · ");

            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = self.theme.icon_path(&icon) {
                item = item.icon(path.as_path());
            }
            Some(item)
        } else {
            None
        }
    }

    fn daily(&self, point: &forecast::Point) -> Option<Item> {
        if let (Some(ref summary), Some(ref min), Some(ref max), Some(ref icon)) = (
            point.summary.clone(),
            point.apparent_temperature_min.clone(),
            point.apparent_temperature_max.clone(),
            point.icon.clone(),
        ) {
            let weekday = if point.time.date() == Local::today() {
                "Today".into()
            } else {
                point.time.format("%A").to_string()
            };
            let title = format!("{} - {}", weekday, summary);
            let subtitle = format!("Low: {} · High: {}", min, max);
            let arg = format!("{}/{}", self.arg(), point.time.timestamp());
            let mut item = Item::new(title);
            item = item.subtitle(&subtitle);
            item = item.arg(&arg);
            if let Some(path) = self.theme.icon_path(icon) {
                item = item.icon(path.as_path());
            }
            Some(item)
        } else {
            None
        }
    }
}

<commit_msg>Make the precipitation intensity shorter<commit_after>use alphred::Item;
use chrono::prelude::*;
use reqwest;

use coordinate::Coordinate;
use errors::*;
use forecast;
use sparkline;
use theme::Theme;

#[derive(Debug)]
pub struct Location {
    pub description: String,
    pub coord: Coordinate,
}

pub struct DarkSky {
    pub dark_sky_api_key: String,
    pub location: Location,
    pub theme: Theme,
    pub units: forecast::Units,
}

impl DarkSky {
    pub fn run(&self) -> Result<()> {
        let forecast = self.forecast(self.location.coord.clone())?;

        let mut items = Vec::new();

        let item = Item::new(self.location.description.clone())
            .subtitle("• Powered by Dark Sky")
            .arg(&self.arg())
            .icon("icons/dark_sky.png");
        items.push(item);

        if let Some(item) = forecast.currently.and_then(|point| self.currently(&point)) {
            items.push(item);
        }

        if let Some(item) = forecast.minutely.and_then(|block| self.minutely(&block)) {
            items.push(item);
        }

        if let Some(daily) = forecast.daily {
            daily
                .data
                .iter()
                .take(5)
                .flat_map(|point| self.daily(point))
                .for_each(|x| items.push(x));
        }

        let json = json!({ "items": items });
        println!("{}", json);

        Ok(())
    }

    fn forecast(&self, coord: Coordinate) -> Result<forecast::Forecast> {
        let Coordinate(lat, long) = coord;
        let units = match self.units {
            forecast::Units::Auto => "auto",
            forecast::Units::Ca => "ca",
            forecast::Units::Uk2 => "uk2",
            forecast::Units::Us => "us",
            forecast::Units::Si => "si",
        };
        let url = format!(
            "https://api.darksky.net/forecast/{}/{},{}?units={}",
            self.dark_sky_api_key,
            lat,
            long,
            units,
        );
        Ok(reqwest::get(&url)?.json()?)
    }

    fn arg(&self) -> String {
        let Coordinate(lat, long) = self.location.coord;
        format!("{:.4},{:.4}", lat, long)
    }

    fn currently(&self, point: &forecast::Point) -> Option<Item> {
        if let (Some(title), Some(temp), Some(apparent_temp), Some(icon)) = (
            point.summary.clone(),
            point.temp,
            point.apparent_temperature.clone(),
            point.icon.clone(),
        ) {
            let mut subtitle = vec![
                format!("{}°", temp.round()),
                format!("Feels like {}", apparent_temp),
            ];
            if let Some(human_precip) = point.human_precipitation() {
                subtitle.push(human_precip);
            }
            let subtitle = subtitle.join(" · ");

            let mut item = Item::new(title);
            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = self.theme.icon_path(&icon) {
                item = item.icon(path.as_path());
            }
            Some(item)
        } else {
            None
        }
    }

    fn minutely(&self, block: &forecast::Block) -> Option<Item> {
        if let (Some(title), Some(icon)) = (block.summary.clone(), block.icon.clone()) {
            let mut item = Item::new(title);

            let mut subtitle = Vec::new();
            let intensities = block.precip_intensities();
            if let (Some(min), Some(max)) = (intensities.iter().min(), intensities.iter().max()) {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    max.0,
                    intensities.clone().iter().map(|x| x.0).collect(),
                    5,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let probabilities = block.precip_probabilities();
            if let (Some(min), Some(max)) = (probabilities.iter().min(), probabilities.iter().max())
            {
                let sparkline = sparkline::Ascii::new(
                    min.0,
                    1.,
                    probabilities.clone().iter().map(|x| x.0).collect(),
                    5,
                );
                subtitle.push(format!("{} {} {}", min, sparkline, max));
            }
            let subtitle = subtitle.join(" · ");

            item = item.subtitle(&subtitle);
            item = item.arg(&self.arg());
            if let Some(path) = self.theme.icon_path(&icon) {
                item = item.icon(path.as_path());
            }
            Some(item)
        } else {
            None
        }
    }

    fn daily(&self, point: &forecast::Point) -> Option<Item> {
        if let (Some(ref summary), Some(ref min), Some(ref max), Some(ref icon)) = (
            point.summary.clone(),
            point.apparent_temperature_min.clone(),
            point.apparent_temperature_max.clone(),
            point.icon.clone(),
        ) {
            let weekday = if point.time.date() == Local::today() {
                "Today".into()
            } else {
                point.time.format("%A").to_string()
            };
            let title = format!("{} - {}", weekday, summary);
            let subtitle = format!("Low: {} · High: {}", min, max);
            let arg = format!("{}/{}", self.arg(), point.time.timestamp());
            let mut item = Item::new(title);
            item = item.subtitle(&subtitle);
            item = item.arg(&arg);
            if let Some(path) = self.theme.icon_path(icon) {
                item = item.icon(path.as_path());
            }
            Some(item)
        } else {
            None
        }
    }
}

<|endoftext|>


<commit_before>use update_client::*;
use errors::*;

use crypto::digest::Digest;
use crypto::sha2::Sha256;
use base64::*;
use std;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};

pub trait Database: Send + Sync + Clone {
    fn query(&self, &str, ThreatDescriptor) -> Result<Option<()>>;
    fn update(&self, &FetchResponse) -> Result<()>;
    fn validate(&self, &FetchResponse) -> Result<()>;
}

#[derive(Clone)]
pub struct HashDB {
    inner_db: Arc<Mutex<HashMap<ThreatDescriptor, Vec<String>>>>,
}

impl HashDB {
    pub fn new() -> HashDB {
        HashDB { inner_db: Arc::new(Mutex::new(HashMap::new())) }
    }

    fn remove(&self,
              removal_map: &HashMap<ThreatDescriptor, (ResponseType, HashSet<usize>)>)
              -> Result<()> {

        for (descriptor, &(_response_type, ref removals)) in removal_map {
            if removals.is_empty() {
                continue;
            }

            info!("Removing {:#?} from {:#?}", removals.len(), descriptor);

            let mut cur_map = self.inner_db.lock().expect("Failed to attain lock for inner_db");

            let mut cur_hashes = cur_map.entry(*descriptor)
                                        .or_insert(vec![]);

            let new_hashes: Vec<String> = cur_hashes.iter()
                                                    .enumerate()
                                                    .filter_map(|(ix, s)| {
                                                        if removals.contains(&ix) {
                                                            Some(s.clone())
                                                        } else {
                                                            None
                                                        }
                                                    })
                                                    .collect();

            *cur_hashes = new_hashes;
        }

        Ok(())
    }

    fn add(&self,
           addition_map: &HashMap<ThreatDescriptor, (ResponseType, Vec<String>)>)
           -> Result<()> {

        for (descriptor, &(response_type, ref additions)) in addition_map {
            if additions.is_empty() {
                continue;
            }
            info!("Adding {:#?} to {:#?}", additions.len(), descriptor);
            let mut cur_map = self.inner_db.lock().expect("Failed to attain lock for inner_db");

            if let ResponseType::PartialUpdate = response_type {

                let mut cur_hashes = cur_map.entry(*descriptor)
                                            .or_insert(vec![]);

                cur_hashes.extend_from_slice(&additions[..]);

                cur_hashes.sort();
            } else if let ResponseType::FullUpdate = response_type {
                cur_map.insert(*descriptor, additions.clone());
            }
        }

        Ok(())
    }
}

impl Database for HashDB {
    fn update(&self, res: &FetchResponse) -> Result<()> {
        let rem_indices = try!(removals(res));
        let add_prefixes = try!(additions(res));

        try!(self.remove(&rem_indices));
        try!(self.add(&add_prefixes));

        Ok(())
    }

    fn validate(&self, res: &FetchResponse) -> Result<()> {
        for response in &res.list_update_responses {
            let descriptor = ThreatDescriptor {
                threat_type: response.threat_type,
                platform_type: response.platform_type,
                threat_entry_type: response.threat_entry_type,
            };
            let checksum = &response.checksum;

            let mut hash = {
                let mut cur_map = self.inner_db.lock().expect("Failed to attain lock for inner_db");

                let cur_hashes = match cur_map.get_mut(&descriptor) {
                    Some(h) => h,
                    None => {
                        info!("Could not get hashes for descriptor: {:#?}", descriptor);
                        continue;
                    }
                };

                cur_hashes.iter()
                          .fold(Sha256::new(), |mut s, r| {
                              s.input(r.as_bytes());
                              s
                          })
            };

            let result = {
                hash.input(&vec![]);
                let mut result = vec![0; 32];
                hash.result(&mut result);
                result
            };
            let hash = result;

            let dec_checksum = decode(&checksum.sha256).unwrap();

            if hash != dec_checksum {
                error!("Checksum failed {:?} != {:?}",
                       hash,
                       checksum.sha256.as_bytes());
            } else {
                info!("Database validated");
            }

        }

        Ok(())
    }

    fn query(&self, _url: &str, _descriptor: ThreatDescriptor) -> Result<Option<()>> {
        unimplemented!()
    }
}

fn additions(fetch_response: &FetchResponse)
             -> Result<HashMap<ThreatDescriptor, (ResponseType, Vec<String>)>> {

    let mut threat_map = HashMap::new();

    for response in &fetch_response.list_update_responses {
        let mut hash_prefixes = Vec::new();

        // Each set of additions will contain a string. This string is the concatenated list of
        // hash prefixes. In a single 'addition' threat entry the prefix size is the same for all
        // hashes, but the prefix size may vary across threat entries.
        for threat_entry in &response.additions {
            if let CompressionType::Raw = threat_entry.compression_type {

                let raw_hashes = &threat_entry.raw_hashes;

                if raw_hashes.raw_hashes.len() % raw_hashes.prefix_size as usize != 0 {
                    error!("Raw hashes not divisible by prefix_size. Skipping update.");
                    continue;
                }

                let hash_count = raw_hashes.raw_hashes.len() / raw_hashes.prefix_size as usize;
                hash_prefixes.reserve(hash_count);

                let init = raw_hashes.raw_hashes.as_bytes();
                let mut ix = 0;
                loop {
                    if ix == init.len() {
                        break;
                    }

                    let char_slice =
                        try!(String::from_utf8(init[ix..ix + raw_hashes.prefix_size as usize]
                                                   .to_vec())
                                 .chain_err(|| "could not convert hash to utf8 string"));
                    ix = ix + raw_hashes.prefix_size as usize;
                    hash_prefixes.push(char_slice);
                }
            }
        }
        hash_prefixes.sort();
        threat_map.insert(ThreatDescriptor {
                              threat_type: response.threat_type,
                              platform_type: response.platform_type,
                              threat_entry_type: response.threat_entry_type,
                          },
                          (response.response_type, hash_prefixes));
    }
    Ok(threat_map)
}

fn removals(fetch_response: &FetchResponse)
            -> Result<HashMap<ThreatDescriptor, (ResponseType, HashSet<usize>)>> {

    let mut threat_map = HashMap::new();

    for response in &fetch_response.list_update_responses {
        let mut raw_indices = HashSet::new();

        for threat_entry in &response.removals {
            if let CompressionType::Raw = threat_entry.compression_type {

                for raw in threat_entry.raw_indices.indices.clone() {
                    raw_indices.insert(raw);
                }
            }
        }

        threat_map.insert(ThreatDescriptor {
                              threat_type: response.threat_type,
                              platform_type: response.platform_type,
                              threat_entry_type: response.threat_entry_type,
                          },
                          (response.response_type, raw_indices));
    }
    Ok(threat_map)
}
<commit_msg>Remove base64<commit_after>use update_client::*;
use errors::*;

use crypto::digest::Digest;
use crypto::sha2::Sha256;
use base64::*;
use std;
use std::collections::{HashMap, HashSet};
use std::sync::{Arc, Mutex};

pub trait Database: Send + Sync + Clone {
    fn query(&self, &str, ThreatDescriptor) -> Result<Option<()>>;
    fn update(&self, &FetchResponse) -> Result<()>;
    fn validate(&self, &FetchResponse) -> Result<()>;
}

#[derive(Clone)]
pub struct HashDB {
    inner_db: Arc<Mutex<HashMap<ThreatDescriptor, Vec<String>>>>,
}

impl HashDB {
    pub fn new() -> HashDB {
        HashDB { inner_db: Arc::new(Mutex::new(HashMap::new())) }
    }

    fn remove(&self,
              removal_map: &HashMap<ThreatDescriptor, (ResponseType, HashSet<usize>)>)
              -> Result<()> {

        for (descriptor, &(_response_type, ref removals)) in removal_map {
            if removals.is_empty() {
                continue;
            }

            info!("Removing {:#?} from {:#?}", removals.len(), descriptor);

            let mut cur_map = self.inner_db.lock().expect("Failed to attain lock for inner_db");

            let mut cur_hashes = cur_map.entry(*descriptor)
                                        .or_insert(vec![]);

            let new_hashes: Vec<String> = cur_hashes.iter()
                                                    .enumerate()
                                                    .filter_map(|(ix, s)| {
                                                        if removals.contains(&ix) {
                                                            Some(s.clone())
                                                        } else {
                                                            None
                                                        }
                                                    })
                                                    .collect();

            *cur_hashes = new_hashes;
        }

        Ok(())
    }

    fn add(&self,
           addition_map: &HashMap<ThreatDescriptor, (ResponseType, Vec<String>)>)
           -> Result<()> {

        for (descriptor, &(response_type, ref additions)) in addition_map {
            if additions.is_empty() {
                continue;
            }
            info!("Adding {:#?} to {:#?}", additions.len(), descriptor);
            let mut cur_map = self.inner_db.lock().expect("Failed to attain lock for inner_db");

            if let ResponseType::PartialUpdate = response_type {

                let mut cur_hashes = cur_map.entry(*descriptor)
                                            .or_insert(vec![]);

                cur_hashes.extend_from_slice(&additions[..]);

                cur_hashes.sort();
            } else if let ResponseType::FullUpdate = response_type {
                cur_map.insert(*descriptor, additions.clone());
            }
        }

        Ok(())
    }
}

impl Database for HashDB {
    fn update(&self, res: &FetchResponse) -> Result<()> {
        let rem_indices = try!(removals(res));
        let add_prefixes = try!(additions(res));

        try!(self.remove(&rem_indices));
        try!(self.add(&add_prefixes));

        Ok(())
    }

    fn validate(&self, res: &FetchResponse) -> Result<()> {
        for response in &res.list_update_responses {
            let descriptor = ThreatDescriptor {
                threat_type: response.threat_type,
                platform_type: response.platform_type,
                threat_entry_type: response.threat_entry_type,
            };
            let checksum = &response.checksum;

            let mut hash = {
                let mut cur_map = self.inner_db.lock().expect("Failed to attain lock for inner_db");

                let cur_hashes = match cur_map.get_mut(&descriptor) {
                    Some(h) => h,
                    None => {
                        info!("Could not get hashes for descriptor: {:#?}", descriptor);
                        continue;
                    }
                };

                cur_hashes.iter()
                          .fold(Sha256::new(), |mut s, r| {
                              s.input(r.as_bytes());
                              s
                          })
            };

            let result = {
                hash.input(&vec![]);
                let mut result = vec![0; 32];
                hash.result(&mut result);
                result
            };
            let hash = result;

            if hash != checksum.sha256.as_bytes() {
                error!("Checksum failed {:?} != {:?}",
                       hash,
                       checksum.sha256.as_bytes());
            } else {
                info!("Database validated");
            }

        }

        Ok(())
    }

    fn query(&self, _url: &str, _descriptor: ThreatDescriptor) -> Result<Option<()>> {
        unimplemented!()
    }
}

fn additions(fetch_response: &FetchResponse)
             -> Result<HashMap<ThreatDescriptor, (ResponseType, Vec<String>)>> {

    let mut threat_map = HashMap::new();

    for response in &fetch_response.list_update_responses {
        let mut hash_prefixes = Vec::new();

        // Each set of additions will contain a string. This string is the concatenated list of
        // hash prefixes. In a single 'addition' threat entry the prefix size is the same for all
        // hashes, but the prefix size may vary across threat entries.
        for threat_entry in &response.additions {
            if let CompressionType::Raw = threat_entry.compression_type {

                let raw_hashes = &threat_entry.raw_hashes;

                if raw_hashes.raw_hashes.len() % raw_hashes.prefix_size as usize != 0 {
                    error!("Raw hashes not divisible by prefix_size. Skipping update.");
                    continue;
                }

                let hash_count = raw_hashes.raw_hashes.len() / raw_hashes.prefix_size as usize;
                hash_prefixes.reserve(hash_count);

                let init = raw_hashes.raw_hashes.as_bytes();
                let mut ix = 0;
                loop {
                    if ix == init.len() {
                        break;
                    }

                    let char_slice =
                        try!(String::from_utf8(init[ix..ix + raw_hashes.prefix_size as usize]
                                                   .to_vec())
                                 .chain_err(|| "could not convert hash to utf8 string"));
                    ix = ix + raw_hashes.prefix_size as usize;
                    hash_prefixes.push(char_slice);
                }
            }
        }
        hash_prefixes.sort();
        threat_map.insert(ThreatDescriptor {
                              threat_type: response.threat_type,
                              platform_type: response.platform_type,
                              threat_entry_type: response.threat_entry_type,
                          },
                          (response.response_type, hash_prefixes));
    }
    Ok(threat_map)
}

fn removals(fetch_response: &FetchResponse)
            -> Result<HashMap<ThreatDescriptor, (ResponseType, HashSet<usize>)>> {

    let mut threat_map = HashMap::new();

    for response in &fetch_response.list_update_responses {
        let mut raw_indices = HashSet::new();

        for threat_entry in &response.removals {
            if let CompressionType::Raw = threat_entry.compression_type {

                for raw in threat_entry.raw_indices.indices.clone() {
                    raw_indices.insert(raw);
                }
            }
        }

        threat_map.insert(ThreatDescriptor {
                              threat_type: response.threat_type,
                              platform_type: response.platform_type,
                              threat_entry_type: response.threat_entry_type,
                          },
                          (response.response_type, raw_indices));
    }
    Ok(threat_map)
}
<|endoftext|>