<commit_before>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::{fs, io};
use std::borrow::Cow;
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurement {
    command: String,
    mean: f64,
    stddev: f64,
    median: f64,
    user: f64,
    system: f64,
    min: f64,
    max: f64,
    times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurements {
    results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Regression {
    threshold: f64,
    difference: f64,
    baseline: Measurement,
    latest: Measurement,
}

#[derive(Debug, Clone, PartialEq)]
struct MeasurementGroup {
    version: String,
    run: String,
    measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
    #[error("NoResultsErr: The results directory has no json files in it.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    NoResultsErr(PathBuf),
}

fn regression(baseline: &Measurement, latest: &Measurement) -> Option<Regression> {
    let threshold = 1.05; // 5% regression threshold
    let difference = latest.median / baseline.median;
    if difference > threshold {
        Some(Regression {
            threshold,
            difference,
            baseline: baseline.clone(),
            latest: latest.clone(),
        })
    } else {
        None
    }
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            println!("{:?}", filename);
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Regression>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join(""),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Regression> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| (x.run.clone(), x.version.clone()))
        .into_iter()
        .map(|(_, group)| {
            let g: Vec<MeasurementGroup> = group.collect();
            regression(&g[0].measurement, &g[1].measurement)
        })
        .flatten()
        .collect();

    Ok(x)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Regression>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        match v[..] {
            [] => Err(TestError::NoResultsErr(results_directory.clone())),
            _ => Ok(())
        }?;

        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        println!("checking regressions with the following measurements:");
        for (path, _) in &v_next {
            // TODO this printed nothing. What's the size of this vector?
            println!("{}", path.file_name().map(|x| x.to_string_lossy()).unwrap_or(Cow::from("unknown file")))
        }

        calculate_regressions(&v_next)
    })
}

pub fn exit_properly(regressions: Vec<Regression>) {
    match regressions[..] {
        [] => println!("congrats! no regressions"),
        _ => {
            for r in regressions {
                println!("{:?}", r);
            }
            println!("the above regressions were found.");
            exit(1)
        }
    }
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
            (
                TestError::NoResultsErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"NoResultsErr: The results directory has no json files in it.
Filepath: dummy/path/no_file/"#
            ),
        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<commit_msg>add error for groups that are not two elements large<commit_after>use itertools::Itertools;
use serde::{Deserialize, Serialize};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::{fs, io};
use std::borrow::Cow;
use thiserror::Error;

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurement {
    command: String,
    mean: f64,
    stddev: f64,
    median: f64,
    user: f64,
    system: f64,
    min: f64,
    max: f64,
    times: Vec<f64>,
}

#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
struct Measurements {
    results: Vec<Measurement>,
}

#[derive(Debug, Clone, PartialEq)]
pub struct Regression {
    threshold: f64,
    difference: f64,
    baseline: Measurement,
    latest: Measurement,
}

#[derive(Debug, Clone, PartialEq)]
pub struct MeasurementGroup {
    version: String,
    run: String,
    measurement: Measurement,
}

#[derive(Error, Debug)]
pub enum TestError {
    #[error("BadJSONErr: JSON in file cannot be deserialized as expected.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadJSONErr(PathBuf, Option<serde_json::Error>),
    #[error("ReadErr: The file cannot be read.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    ReadErr(PathBuf, Option<io::Error>),
    #[error("MissingFilenameErr: The path provided does not specify a file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    MissingFilenameErr(PathBuf),
    #[error("FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    FilenameNotUnicodeErr(PathBuf),
    #[error("BadFileContentsErr: Check that the file exists and is readable.\nFilepath: {}\nOriginating Exception:{}", .0.to_string_lossy().into_owned(), .1.as_ref().map_or("None".to_owned(), |e| format!("{}", e)))]
    BadFileContentsErr(PathBuf, Option<io::Error>),
    #[error("NoResultsErr: The results directory has no json files in it.\nFilepath: {}", .0.to_string_lossy().into_owned())]
    NoResultsErr(PathBuf),
    #[error("OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.\nFile Count: {}\nFilepath: {}", .0, .1.to_string_lossy().into_owned())]
    OddResultsCountErr(usize, PathBuf),
    #[error("BadGroupSizeErr: Expected two results per group, one for each branch-project pair.\nCount: {}\nGroup: {:?}", .0, .1.into_iter().map(|group| (&group.version[..], &group.run[..])).collect::<Vec<(&str, &str)>>())]
    BadGroupSizeErr(usize, Vec<MeasurementGroup>),
}

fn regression(baseline: &Measurement, latest: &Measurement) -> Option<Regression> {
    let threshold = 1.05; // 5% regression threshold
    let difference = latest.median / baseline.median;
    if difference > threshold {
        Some(Regression {
            threshold,
            difference,
            baseline: baseline.clone(),
            latest: latest.clone(),
        })
    } else {
        None
    }
}

// given a directory, read all files in the directory and return each
// filename with the deserialized json contents of that file
fn measurements_from_files(
    results_directory: &Path,
) -> Result<Vec<(PathBuf, Measurements)>, TestError> {
    let result_files = fs::read_dir(results_directory)
        .or_else(|e| Err(TestError::ReadErr(results_directory.to_path_buf(), Some(e))))?;

    result_files
        .into_iter()
        .map(|f| f.unwrap().path())
        .filter(|filename| {
            filename
                .extension()
                .and_then(|ext| ext.to_str())
                .map_or(false, |ext| ext.ends_with("json"))
        })
        .map(|filename| {
            fs::read_to_string(&filename)
                .or_else(|e| Err(TestError::BadFileContentsErr(filename.clone(), Some(e))))
                .and_then(|contents| {
                    serde_json::from_str::<Measurements>(&contents)
                        .or_else(|e| Err(TestError::BadJSONErr(filename.clone(), Some(e))))
                })
                .map(|m| (filename, m))
        })
        .collect()
}

// given a list of filename-measurement pairs, detect any regressions by grouping
// measurements together by filename
fn calculate_regressions(
    measurements: &[(PathBuf, Measurement)],
) -> Result<Vec<Regression>, TestError> {
    let mut measurement_groups: Vec<MeasurementGroup> = measurements
        .into_iter()
        .map(|(p, m)| {
            p.file_name()
                .ok_or_else(|| TestError::MissingFilenameErr(p.to_path_buf()))
                .and_then(|name| {
                    name.to_str()
                        .ok_or_else(|| TestError::FilenameNotUnicodeErr(p.to_path_buf()))
                })
                .map(|name| {
                    let parts: Vec<&str> = name.split("_").collect();
                    MeasurementGroup {
                        version: parts[0].to_owned(),
                        run: parts[1..].join(""),
                        measurement: m.clone(),
                    }
                })
        })
        .collect::<Result<Vec<MeasurementGroup>, TestError>>()?;

    measurement_groups.sort_by(|x, y| (&x.run, &x.version).cmp(&(&y.run, &y.version)));

    // locking up mutation
    let sorted_measurement_groups = measurement_groups;

    let x: Vec<Regression> = sorted_measurement_groups
        .into_iter()
        .group_by(|x| (x.run.clone(), x.version.clone()))
        .into_iter()
        .map(|(_, group)| {
            let g: Vec<MeasurementGroup> = group.collect();
            match g.len() {
                2 => regression(&g[0].measurement, &g[1].measurement).map(Ok),
                i => Some(Err(TestError::BadGroupSizeErr(i, g))),
            }
        })
        .flatten()
        .collect::<Result<Vec<Regression>, TestError>>()?;

    Ok(x)
}

pub fn regressions(results_directory: &PathBuf) -> Result<Vec<Regression>, TestError> {
    measurements_from_files(Path::new(&results_directory)).and_then(|v| {
        // exit early with an Err if there are no results to process
        match v.len() {
            0 => Err(TestError::NoResultsErr(results_directory.clone())),
            i if i % 2 == 1 => Err(TestError::OddResultsCountErr(i, results_directory.clone())),
            _ => Ok(()),
        }?;

        let v_next: Vec<(PathBuf, Measurement)> = v
            .into_iter()
            // the way we're running these, the files will each contain exactly one measurement, hence `results[0]`
            .map(|(p, ms)| (p, ms.results[0].clone()))
            .collect();

        println!("checking regressions with the following measurements:");
        for (path, _) in &v_next {
            // TODO this printed nothing. What's the size of this vector?
            println!("{}", path.file_name().map(|x| x.to_string_lossy()).unwrap_or(Cow::from("unknown file")))
        }

        calculate_regressions(&v_next)
    })
}

pub fn exit_properly(regressions: Vec<Regression>) {
    match regressions[..] {
        [] => println!("congrats! no regressions"),
        _ => {
            for r in regressions {
                println!("{:?}", r);
            }
            println!("the above regressions were found.");
            exit(1)
        }
    }
}

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

    #[test]
    fn test_error_messages() {
        let pairs = vec![
            (
                TestError::BadJSONErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"BadJSONErr: JSON in file cannot be deserialized as expected.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::ReadErr(Path::new("dummy/path/file.json").to_path_buf(), None),
                r#"ReadErr: The file cannot be read.
Filepath: dummy/path/file.json
Originating Exception:None"#
            ),
            (
                TestError::MissingFilenameErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"MissingFilenameErr: The path provided does not specify a file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::FilenameNotUnicodeErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"FilenameNotUnicodeErr: The filename is not expressible in unicode. Consider renaming the file.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadFileContentsErr(Path::new("dummy/path/filenotexist.json").to_path_buf(), None),
                r#"BadFileContentsErr: Check that the file exists and is readable.
Filepath: dummy/path/filenotexist.json
Originating Exception:None"#
            ),
            (
                TestError::NoResultsErr(Path::new("dummy/path/no_file/").to_path_buf()),
                r#"NoResultsErr: The results directory has no json files in it.
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::OddResultsCountErr(3, Path::new("dummy/path/no_file/").to_path_buf()),
                r#"OddResultsCountErr: The results directory has an odd number of results in it. Expected an even number.
File Count: 3
Filepath: dummy/path/no_file/"#
            ),
            (
                TestError::BadGroupSizeErr(1, vec![MeasurementGroup {
                    version: "dev".to_owned(),
                    run: "some command".to_owned(),
                    measurement: Measurement {
                        command: "some command".to_owned(),
                        mean: 1.0,
                        stddev: 1.0,
                        median: 1.0,
                        user: 1.0,
                        system: 1.0,
                        min: 1.0,
                        max: 1.0,
                        times: vec![1.0, 1.1, 0.9, 1.0, 1.1, 0.9, 1.1],
                    }
                }]),
                r#"BadGroupSizeErr: Expected two results per group, one for each branch-project pair.
Count: 1
Group: [("dev", "some command")]"#
            ),
        ];

        for (err, msg) in pairs {
            assert_eq!(format!("{}", err), msg)
        }
    }

}
<|endoftext|>


<commit_before>// Copyright 2015-2017 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use czmq::{ZCert, ZSock, ZSys};
use project::{Language, ProjectConfig};
use std::env::set_current_dir;
use std::thread::{JoinHandle, spawn};
use tempdir::TempDir;
use write_conf;

pub struct MockEnv {
    _auth_handler: JoinHandle<()>,
    pub _proj_dir: TempDir,
}

impl MockEnv {
    pub fn new() -> MockEnv {
        let proj_dir = TempDir::new("remote_host_connect").unwrap();
        set_current_dir(proj_dir.path()).unwrap();

        let cert = ZCert::new().unwrap();
        cert.save_secret("user.crt").unwrap();

        let cert = ZCert::new().unwrap();
        cert.save_public("auth.crt").unwrap();
        cert.save_secret(".auth_secret.crt").unwrap();

        ZSys::init();
        let mut sock = ZSock::new(::czmq::SocketType::REP);
        let cert = ZCert::load(".auth_secret.crt").unwrap();
        cert.apply(&mut sock);
        sock.set_curve_server(true);
        sock.set_zap_domain("mock_auth_server");
        let port = sock.bind("tcp://127.0.0.1:*[60000-]").unwrap();

        let config = ProjectConfig {
            language: Language::Rust,
            auth_server: "127.0.0.1".into(),
            auth_api_port: 0,
            auth_update_port: port as u32,
        };
        write_conf(&config, "project.json").unwrap();

        let handle = spawn(move|| MockEnv::auth_handler(sock));

        MockEnv {
            _auth_handler: handle,
            _proj_dir: proj_dir,
        }
    }

    // This fn exists to coerce lazy_static into initialising a new
    // MockEnv instance.
    pub fn init(&self) {}

    fn auth_handler(mut sock: ZSock) {
        loop {
            sock.recv_str().unwrap().unwrap();

            let reply = ::czmq::ZMsg::new();
            reply.addstr("Ok").unwrap();
            reply.addstr("0000000000000000000000000000000000000000").unwrap();
            reply.send(&mut sock).unwrap();
        }
    }
}
<commit_msg>Add build_server to mock_env.rs<commit_after>// Copyright 2015-2017 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

use czmq::{ZCert, ZSock, ZSys};
use project::{Language, ProjectConfig};
use std::env::set_current_dir;
use std::thread::{JoinHandle, spawn};
use tempdir::TempDir;
use write_conf;

pub struct MockEnv {
    _auth_handler: JoinHandle<()>,
    pub _proj_dir: TempDir,
}

impl MockEnv {
    pub fn new() -> MockEnv {
        let proj_dir = TempDir::new("remote_host_connect").unwrap();
        set_current_dir(proj_dir.path()).unwrap();

        let cert = ZCert::new().unwrap();
        cert.save_secret("user.crt").unwrap();

        let cert = ZCert::new().unwrap();
        cert.save_public("auth.crt").unwrap();
        cert.save_secret(".auth_secret.crt").unwrap();

        ZSys::init();
        let mut sock = ZSock::new(::czmq::SocketType::REP);
        let cert = ZCert::load(".auth_secret.crt").unwrap();
        cert.apply(&mut sock);
        sock.set_curve_server(true);
        sock.set_zap_domain("mock_auth_server");
        let port = sock.bind("tcp://127.0.0.1:*[60000-]").unwrap();

        let config = ProjectConfig {
            language: Language::Rust,
            auth_server: "127.0.0.1".into(),
            auth_api_port: 0,
            auth_update_port: port as u32,
            build_server: None,
        };
        write_conf(&config, "project.json").unwrap();

        let handle = spawn(move|| MockEnv::auth_handler(sock));

        MockEnv {
            _auth_handler: handle,
            _proj_dir: proj_dir,
        }
    }

    // This fn exists to coerce lazy_static into initialising a new
    // MockEnv instance.
    pub fn init(&self) {}

    fn auth_handler(mut sock: ZSock) {
        loop {
            sock.recv_str().unwrap().unwrap();

            let reply = ::czmq::ZMsg::new();
            reply.addstr("Ok").unwrap();
            reply.addstr("0000000000000000000000000000000000000000").unwrap();
            reply.send(&mut sock).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>extern crate gtk;

use gtk::prelude::*;
use gtk::{IconSize, Orientation, ReliefStyle, Widget};

struct Notebook {
    notebook: gtk::Notebook,
    tabs: Vec<gtk::Box>
}

impl Notebook {
    fn new() -> Notebook {
        Notebook {
            notebook: gtk::Notebook::new(),
            tabs: Vec::new()
        }
    }

    fn create_tab(&mut self, title: &str, widget: Widget) -> u32 {
        let close_image = gtk::Image::new_from_icon_name("window-close",
                                                         IconSize::Button.into());
        let button = gtk::Button::new();
        let label = gtk::Label::new(Some(title));
        let tab = gtk::Box::new(Orientation::Horizontal, 0);

        button.set_relief(ReliefStyle::None);
        ButtonExt::set_focus_on_click(&button, false);
        button.add(&close_image);

        tab.pack_start(&label, false, false, 0);
        tab.pack_start(&button, false, false, 0);
        tab.show_all();

        let index = self.notebook.append_page(&widget, Some(&tab));

        let notebook_clone = self.notebook.clone();
        button.connect_clicked(move |_| {
            let index = notebook_clone.page_num(&widget).unwrap();
            notebook_clone.remove_page(Some(index));
        });

        self.tabs.push(tab);

        index
    }
}

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = gtk::Window::new(gtk::WindowType::Toplevel);

    window.set_title("Notebook");
    window.set_position(gtk::WindowPosition::Center);
    window.set_default_size(640, 480);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    let mut notebook = Notebook::new();

    for i in 1..4 {
        let title = format!("sheet {}", i);
        let label = gtk::Label::new(Some(&title));
        notebook.create_tab(&title, label.upcast());
    }

    window.add(&notebook.notebook);
    window.show_all();
    gtk::main();
}

<commit_msg>Use set_focus_on_click without concrete trait<commit_after>extern crate gtk;

use gtk::prelude::*;
use gtk::{IconSize, Orientation, ReliefStyle, Widget};

struct Notebook {
    notebook: gtk::Notebook,
    tabs: Vec<gtk::Box>
}

impl Notebook {
    fn new() -> Notebook {
        Notebook {
            notebook: gtk::Notebook::new(),
            tabs: Vec::new()
        }
    }

    fn create_tab(&mut self, title: &str, widget: Widget) -> u32 {
        let close_image = gtk::Image::new_from_icon_name("window-close",
                                                         IconSize::Button.into());
        let button = gtk::Button::new();
        let label = gtk::Label::new(Some(title));
        let tab = gtk::Box::new(Orientation::Horizontal, 0);

        button.set_relief(ReliefStyle::None);
        button.set_focus_on_click(false);
        button.add(&close_image);

        tab.pack_start(&label, false, false, 0);
        tab.pack_start(&button, false, false, 0);
        tab.show_all();

        let index = self.notebook.append_page(&widget, Some(&tab));

        let notebook_clone = self.notebook.clone();
        button.connect_clicked(move |_| {
            let index = notebook_clone.page_num(&widget).unwrap();
            notebook_clone.remove_page(Some(index));
        });

        self.tabs.push(tab);

        index
    }
}

fn main() {
    if gtk::init().is_err() {
        println!("Failed to initialize GTK.");
        return;
    }

    let window = gtk::Window::new(gtk::WindowType::Toplevel);

    window.set_title("Notebook");
    window.set_position(gtk::WindowPosition::Center);
    window.set_default_size(640, 480);

    window.connect_delete_event(|_, _| {
        gtk::main_quit();
        Inhibit(false)
    });

    let mut notebook = Notebook::new();

    for i in 1..4 {
        let title = format!("sheet {}", i);
        let label = gtk::Label::new(Some(&title));
        notebook.create_tab(&title, label.upcast());
    }

    window.add(&notebook.notebook);
    window.show_all();
    gtk::main();
}

<|endoftext|>


<commit_before>use std::io::{Read, Write};
use std::mem::size_of;
use std::net::{
    TcpStream,
    ToSocketAddrs
};

use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt};
use num::FromPrimitive;

use errors::BMemcachedError;

enum Type {
    Request = 0x80,
    Response = 0x81
}

#[derive(Debug)]
enum Command {
    Get = 0x00,
    Set = 0x01,
    Add = 0x02,
    Replace = 0x03,
    Delete = 0x04,
    Increment = 0x05,
    Decrement = 0x06,
    // Quit = 0x07,
    // Flush = 0x08,
    // GetQ = 0x09,
    // NoOp = 0x0A,
    // Version = 0x0B,
    // GetK = 0x0C,
    // GetKQ = 0x0D,
    // Append = 0x0E,
    // Prepend = 0x0F,
    // Stat = 0x10,
    // SetQ = 0x11,
    // AddQ = 0x12,
    // ReplaceQ = 0x13,
    // DeleteQ = 0x14,
    // IncrementQ = 0x15,
    // DecrementQ = 0x16,
    // QuitQ = 0x17,
    // FlushQ = 0x18,
    // AppendQ = 0x19,
    // PrependQ = 0x1A
}

enum_from_primitive! {
    #[derive(Debug, PartialEq)]
    pub enum Status {
        Success = 0x00,
        KeyNotFound = 0x01,
        KeyExists = 0x02,
        AuthError = 0x08,
        UnknownCommand = 0x81
    }
}

#[derive(Debug)]
pub struct Request {
    magic: u8,
    opcode: u8,
    key_length: u16,
    extras_length: u8,
    data_type: u8,
    reserved: u16,
    body_length: u32,
    opaque: u32,
    cas: u64
}

#[derive(Debug)]
pub struct Response {
    magic: u8,
    opcode: u8,
    key_length: u16,
    extras_length: u8,
    data_type: u8,
    status: u16,
    body_length: u32,
    opaque: u32,
    cas: u64
}

#[derive(Debug)]
pub struct Protocol {
    connection: TcpStream
}

impl Protocol {
    pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<Protocol, BMemcachedError> {
        Ok(Protocol{connection: try!(TcpStream::connect(addr))})
    }

    pub fn connection_info(&self) -> String {
        format!("{:?}", self.connection.peer_addr().unwrap())
    }

    fn build_request(command: Command, key_length: usize, value_length: usize, data_type: u8,
        extras_length: usize, cas: u64) -> Request {
        Request {
            magic: Type::Request as u8, opcode: command as u8,
            key_length: key_length as u16,
            extras_length: extras_length as u8,
            data_type: data_type, reserved: 0,
            body_length: (key_length + value_length + extras_length) as u32,
            opaque: 0, cas: cas
        }
    }

    fn write_request(&self, request: Request, final_payload: &[u8]) -> Result<(), BMemcachedError> {
        let mut buf = &self.connection;
        try!(buf.write_u8(request.magic));
        try!(buf.write_u8(request.opcode));
        try!(buf.write_u16::<BigEndian>(request.key_length));
        try!(buf.write_u8(request.extras_length));
        try!(buf.write_u8(request.data_type));
        try!(buf.write_u16::<BigEndian>(request.reserved));
        try!(buf.write_u32::<BigEndian>(request.body_length));
        try!(buf.write_u32::<BigEndian>(request.opaque));
        try!(buf.write_u64::<BigEndian>(request.cas));
        try!(buf.write(final_payload));
        Ok(())
    }

    fn read_response(&mut self) -> Result<Response, BMemcachedError> {
        let mut buf = &self.connection;
        let magic: u8 = try!(buf.read_u8());
        if magic != Type::Response as u8 {
            // TODO Consume the stream, disconnect or something?
            debug!("Server sent an unknown magic code {:?}", magic);
            return Err(BMemcachedError::UnkownError("Server sent an unknown magic code"))
        }
        Ok(Response {
            magic: magic,
            opcode: try!(buf.read_u8()),
            key_length: try!(buf.read_u16::<BigEndian>()),
            extras_length: try!(buf.read_u8()),
            data_type: try!(buf.read_u8()),
            status: try!(buf.read_u16::<BigEndian>()),
            body_length: try!(buf.read_u32::<BigEndian>()),
            opaque: try!(buf.read_u32::<BigEndian>()),
            cas: try!(buf.read_u64::<BigEndian>())
        })
    }

    fn consume_body(&mut self, size: u32) -> Result<(), BMemcachedError> {
        debug!("Consuming body");
        let mut buf: Vec<u8> = vec![0; size as usize];
        try!(self.connection.read(&mut *buf));
        let str_buf = try!(String::from_utf8(buf));
        debug!("Consumed body {:?}", str_buf);
        Ok(())
    }

    fn set_add_replace<K, V>(&mut self, command: Command, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        let key = key.as_ref();
        let value = value.as_ref();
        let extras_length = 8; // Flags: u32 and Expiration time: u32
        let request = Protocol::build_request(command, key.len(), value.len(), 0x00, extras_length, 0x00);
        let mut final_payload = vec![];
        // Flags
        try!(final_payload.write_u32::<BigEndian>(0));
        try!(final_payload.write_u32::<BigEndian>(time));
        // After flags key and value
        try!(final_payload.write(key));
        try!(final_payload.write(value));
        try!(self.write_request(request, final_payload.as_slice()));
        let response = try!(self.read_response());
        match Status::from_u16(response.status) {
            Some(Status::Success) => Ok(()),
            Some(rest) => {
                try!(self.consume_body(response.body_length));
                Err(BMemcachedError::Status(rest))
            },
            None => Err(BMemcachedError::UnkownError("Server returned an unknown status code"))
        }
    }

    pub fn set<K, V>(&mut self, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        self.set_add_replace(Command::Set, key, value, time)
    }

    pub fn add<K, V>(&mut self, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        self.set_add_replace(Command::Add, key, value, time)
    }

    pub fn replace<K, V>(&mut self, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        self.set_add_replace(Command::Replace, key, value, time)
    }

    pub fn get<K>(&mut self, key: K) -> Result<String, BMemcachedError> where K: AsRef<[u8]> {
        let key = key.as_ref();
        let request = Protocol::build_request(Command::Get, key.len(), 0 as usize, 0, 0, 0x00);
        try!(self.write_request(request, key));
        let response = try!(self.read_response());
        match Status::from_u16(response.status) {
            Some(Status::Success) => {},
            Some(status) => {
                try!(self.consume_body(response.body_length));
                return Err(BMemcachedError::Status(status))
            },
            None => return Err(BMemcachedError::UnkownError("Server sent an unknown status code"))
        };
        // Discard extras for now
        try!(self.connection.read_u32::<BigEndian>());
        let mut outbuf = vec![0; (response.body_length - response.extras_length as u32) as usize];
        try!(self.connection.read_exact(&mut outbuf));
        Ok(try!(String::from_utf8(outbuf)))
    }

    pub fn delete<K>(&mut self, key: K) -> Result<(), BMemcachedError> where K: AsRef<[u8]> {
        let key = key.as_ref();
        let request = Protocol::build_request(Command::Delete, key.len(), 0 as usize, 0, 0, 0x00);
        try!(self.write_request(request, key));
        let response = try!(self.read_response());

        match Status::from_u16(response.status) {
            Some(Status::Success) => Ok(()),
            Some(Status::KeyNotFound) => {
                try!(self.consume_body(response.body_length));
                Ok(())
            },
            Some(status) => {
                try!(self.consume_body(response.body_length));
                Err(BMemcachedError::Status(status))
            },
            None => Err(BMemcachedError::UnkownError("Server sent an unknown status code"))
        }
    }

    fn increment_decrement<K>(&mut self, key: K, amount: u64, initial: u64, time: u32, command: Command) -> Result<u64, BMemcachedError> where K: AsRef<[u8]> {
        let key = key.as_ref();
        let extras_length = 20; // Amount: u64, Initial: u64, Time: u32
        let request = Protocol::build_request(command, key.len(), 0, 0, extras_length, 0x00);
        let mut final_payload: Vec<u8> = vec!();
        try!(final_payload.write_u64::<BigEndian>(amount));
        try!(final_payload.write_u64::<BigEndian>(initial));
        try!(final_payload.write_u32::<BigEndian>(time));
        try!(final_payload.write(key));
        try!(self.write_request(request, &mut final_payload));
        let response = try!(self.read_response());
        match Status::from_u16(response.status) {
            Some(Status::Success) => Ok(try!(self.connection.read_u64::<BigEndian>())),
            Some(status) => {
                try!(self.consume_body(response.body_length));
                Err(BMemcachedError::Status(status))
            }
            None => Err(BMemcachedError::UnkownError("Server sent an unknown status code"))
        }
    }

    pub fn increment<K>(&mut self, key: K, amount: u64, initial: u64, time: u32) -> Result<u64, BMemcachedError> where K: AsRef<[u8]> {
        self.increment_decrement(key, amount, initial, time, Command::Increment)
    }

    pub fn decrement<K>(&mut self, key: K, amount: u64, initial: u64, time: u32) -> Result<u64, BMemcachedError> where K: AsRef<[u8]> {
        self.increment_decrement(key, amount, initial, time, Command::Decrement)
    }
}
#[cfg(test)]
mod tests {
    extern crate env_logger;
    use errors::BMemcachedError;
    use super::*;

    #[test]
    fn set_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Set";
        let value = "World";
        p.set(key, value, 1000).unwrap();
        p.delete(key).unwrap();
    }

    #[test]
    fn add_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Add";
        let value = "World";
        p.add(key, value, 10).unwrap();
        let result = p.add(key, value, 10);
        match result {
            Ok(()) => panic!("Add key should return error"),
            Err(BMemcachedError::Status(Status::KeyExists)) => {},
            Err(_) => panic!("Some strange error that should not happen")
        };
        p.delete(key).unwrap();
    }

    #[test]
    fn get_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Get";
        let value = "World";
        p.set(key, value, 10000).unwrap();
        assert_eq!(p.get(key).unwrap(),  value);
        match p.get("not found".to_string()) {
            Ok(_) => panic!("This key should not exist"),
            Err(BMemcachedError::Status(Status::KeyNotFound)) => {},
            Err(_) => panic!("This should return KeyNotFound")
        };
        p.delete(key).unwrap();
    }

    #[test]
    fn delete_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Delete";
        let value = "World";
        p.set(key, value, 1000).unwrap();
        p.delete(key).unwrap();
        p.delete(key).unwrap();
    }

    #[test]
    fn increment() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Increment";
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 0);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 1);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 2);
        p.delete(key).unwrap();
    }

    #[test]
    fn decrement() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Decrement";
        assert_eq!(p.decrement(key, 1, 0, 1000).unwrap(), 0);
        assert_eq!(p.decrement(key, 1, 0, 1000).unwrap(), 0);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 1);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 2);
        assert_eq!(p.decrement(key, 1, 0, 1000).unwrap(), 1);
        p.delete(key).unwrap();
    }
}
<commit_msg>Remove warning<commit_after>use std::io::{Read, Write};
use std::net::{
    TcpStream,
    ToSocketAddrs
};

use byteorder::{ReadBytesExt, BigEndian, WriteBytesExt};
use num::FromPrimitive;

use errors::BMemcachedError;

enum Type {
    Request = 0x80,
    Response = 0x81
}

#[derive(Debug)]
enum Command {
    Get = 0x00,
    Set = 0x01,
    Add = 0x02,
    Replace = 0x03,
    Delete = 0x04,
    Increment = 0x05,
    Decrement = 0x06,
    // Quit = 0x07,
    // Flush = 0x08,
    // GetQ = 0x09,
    // NoOp = 0x0A,
    // Version = 0x0B,
    // GetK = 0x0C,
    // GetKQ = 0x0D,
    // Append = 0x0E,
    // Prepend = 0x0F,
    // Stat = 0x10,
    // SetQ = 0x11,
    // AddQ = 0x12,
    // ReplaceQ = 0x13,
    // DeleteQ = 0x14,
    // IncrementQ = 0x15,
    // DecrementQ = 0x16,
    // QuitQ = 0x17,
    // FlushQ = 0x18,
    // AppendQ = 0x19,
    // PrependQ = 0x1A
}

enum_from_primitive! {
    #[derive(Debug, PartialEq)]
    pub enum Status {
        Success = 0x00,
        KeyNotFound = 0x01,
        KeyExists = 0x02,
        AuthError = 0x08,
        UnknownCommand = 0x81
    }
}

#[derive(Debug)]
pub struct Request {
    magic: u8,
    opcode: u8,
    key_length: u16,
    extras_length: u8,
    data_type: u8,
    reserved: u16,
    body_length: u32,
    opaque: u32,
    cas: u64
}

#[derive(Debug)]
pub struct Response {
    magic: u8,
    opcode: u8,
    key_length: u16,
    extras_length: u8,
    data_type: u8,
    status: u16,
    body_length: u32,
    opaque: u32,
    cas: u64
}

#[derive(Debug)]
pub struct Protocol {
    connection: TcpStream
}

impl Protocol {
    pub fn connect<A: ToSocketAddrs>(addr: A) -> Result<Protocol, BMemcachedError> {
        Ok(Protocol{connection: try!(TcpStream::connect(addr))})
    }

    pub fn connection_info(&self) -> String {
        format!("{:?}", self.connection.peer_addr().unwrap())
    }

    fn build_request(command: Command, key_length: usize, value_length: usize, data_type: u8,
        extras_length: usize, cas: u64) -> Request {
        Request {
            magic: Type::Request as u8, opcode: command as u8,
            key_length: key_length as u16,
            extras_length: extras_length as u8,
            data_type: data_type, reserved: 0,
            body_length: (key_length + value_length + extras_length) as u32,
            opaque: 0, cas: cas
        }
    }

    fn write_request(&self, request: Request, final_payload: &[u8]) -> Result<(), BMemcachedError> {
        let mut buf = &self.connection;
        try!(buf.write_u8(request.magic));
        try!(buf.write_u8(request.opcode));
        try!(buf.write_u16::<BigEndian>(request.key_length));
        try!(buf.write_u8(request.extras_length));
        try!(buf.write_u8(request.data_type));
        try!(buf.write_u16::<BigEndian>(request.reserved));
        try!(buf.write_u32::<BigEndian>(request.body_length));
        try!(buf.write_u32::<BigEndian>(request.opaque));
        try!(buf.write_u64::<BigEndian>(request.cas));
        try!(buf.write(final_payload));
        Ok(())
    }

    fn read_response(&mut self) -> Result<Response, BMemcachedError> {
        let mut buf = &self.connection;
        let magic: u8 = try!(buf.read_u8());
        if magic != Type::Response as u8 {
            // TODO Consume the stream, disconnect or something?
            debug!("Server sent an unknown magic code {:?}", magic);
            return Err(BMemcachedError::UnkownError("Server sent an unknown magic code"))
        }
        Ok(Response {
            magic: magic,
            opcode: try!(buf.read_u8()),
            key_length: try!(buf.read_u16::<BigEndian>()),
            extras_length: try!(buf.read_u8()),
            data_type: try!(buf.read_u8()),
            status: try!(buf.read_u16::<BigEndian>()),
            body_length: try!(buf.read_u32::<BigEndian>()),
            opaque: try!(buf.read_u32::<BigEndian>()),
            cas: try!(buf.read_u64::<BigEndian>())
        })
    }

    fn consume_body(&mut self, size: u32) -> Result<(), BMemcachedError> {
        debug!("Consuming body");
        let mut buf: Vec<u8> = vec![0; size as usize];
        try!(self.connection.read(&mut *buf));
        let str_buf = try!(String::from_utf8(buf));
        debug!("Consumed body {:?}", str_buf);
        Ok(())
    }

    fn set_add_replace<K, V>(&mut self, command: Command, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        let key = key.as_ref();
        let value = value.as_ref();
        let extras_length = 8; // Flags: u32 and Expiration time: u32
        let request = Protocol::build_request(command, key.len(), value.len(), 0x00, extras_length, 0x00);
        let mut final_payload = vec![];
        // Flags
        try!(final_payload.write_u32::<BigEndian>(0));
        try!(final_payload.write_u32::<BigEndian>(time));
        // After flags key and value
        try!(final_payload.write(key));
        try!(final_payload.write(value));
        try!(self.write_request(request, final_payload.as_slice()));
        let response = try!(self.read_response());
        match Status::from_u16(response.status) {
            Some(Status::Success) => Ok(()),
            Some(rest) => {
                try!(self.consume_body(response.body_length));
                Err(BMemcachedError::Status(rest))
            },
            None => Err(BMemcachedError::UnkownError("Server returned an unknown status code"))
        }
    }

    pub fn set<K, V>(&mut self, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        self.set_add_replace(Command::Set, key, value, time)
    }

    pub fn add<K, V>(&mut self, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        self.set_add_replace(Command::Add, key, value, time)
    }

    pub fn replace<K, V>(&mut self, key: K, value: V, time: u32) -> Result<(), BMemcachedError>
        where K: AsRef<[u8]>, V: AsRef<[u8]> {
        self.set_add_replace(Command::Replace, key, value, time)
    }

    pub fn get<K>(&mut self, key: K) -> Result<String, BMemcachedError> where K: AsRef<[u8]> {
        let key = key.as_ref();
        let request = Protocol::build_request(Command::Get, key.len(), 0 as usize, 0, 0, 0x00);
        try!(self.write_request(request, key));
        let response = try!(self.read_response());
        match Status::from_u16(response.status) {
            Some(Status::Success) => {},
            Some(status) => {
                try!(self.consume_body(response.body_length));
                return Err(BMemcachedError::Status(status))
            },
            None => return Err(BMemcachedError::UnkownError("Server sent an unknown status code"))
        };
        // Discard extras for now
        try!(self.connection.read_u32::<BigEndian>());
        let mut outbuf = vec![0; (response.body_length - response.extras_length as u32) as usize];
        try!(self.connection.read_exact(&mut outbuf));
        Ok(try!(String::from_utf8(outbuf)))
    }

    pub fn delete<K>(&mut self, key: K) -> Result<(), BMemcachedError> where K: AsRef<[u8]> {
        let key = key.as_ref();
        let request = Protocol::build_request(Command::Delete, key.len(), 0 as usize, 0, 0, 0x00);
        try!(self.write_request(request, key));
        let response = try!(self.read_response());

        match Status::from_u16(response.status) {
            Some(Status::Success) => Ok(()),
            Some(Status::KeyNotFound) => {
                try!(self.consume_body(response.body_length));
                Ok(())
            },
            Some(status) => {
                try!(self.consume_body(response.body_length));
                Err(BMemcachedError::Status(status))
            },
            None => Err(BMemcachedError::UnkownError("Server sent an unknown status code"))
        }
    }

    fn increment_decrement<K>(&mut self, key: K, amount: u64, initial: u64, time: u32, command: Command) -> Result<u64, BMemcachedError> where K: AsRef<[u8]> {
        let key = key.as_ref();
        let extras_length = 20; // Amount: u64, Initial: u64, Time: u32
        let request = Protocol::build_request(command, key.len(), 0, 0, extras_length, 0x00);
        let mut final_payload: Vec<u8> = vec!();
        try!(final_payload.write_u64::<BigEndian>(amount));
        try!(final_payload.write_u64::<BigEndian>(initial));
        try!(final_payload.write_u32::<BigEndian>(time));
        try!(final_payload.write(key));
        try!(self.write_request(request, &mut final_payload));
        let response = try!(self.read_response());
        match Status::from_u16(response.status) {
            Some(Status::Success) => Ok(try!(self.connection.read_u64::<BigEndian>())),
            Some(status) => {
                try!(self.consume_body(response.body_length));
                Err(BMemcachedError::Status(status))
            }
            None => Err(BMemcachedError::UnkownError("Server sent an unknown status code"))
        }
    }

    pub fn increment<K>(&mut self, key: K, amount: u64, initial: u64, time: u32) -> Result<u64, BMemcachedError> where K: AsRef<[u8]> {
        self.increment_decrement(key, amount, initial, time, Command::Increment)
    }

    pub fn decrement<K>(&mut self, key: K, amount: u64, initial: u64, time: u32) -> Result<u64, BMemcachedError> where K: AsRef<[u8]> {
        self.increment_decrement(key, amount, initial, time, Command::Decrement)
    }
}
#[cfg(test)]
mod tests {
    extern crate env_logger;
    use errors::BMemcachedError;
    use super::*;

    #[test]
    fn set_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Set";
        let value = "World";
        p.set(key, value, 1000).unwrap();
        p.delete(key).unwrap();
    }

    #[test]
    fn add_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Add";
        let value = "World";
        p.add(key, value, 10).unwrap();
        let result = p.add(key, value, 10);
        match result {
            Ok(()) => panic!("Add key should return error"),
            Err(BMemcachedError::Status(Status::KeyExists)) => {},
            Err(_) => panic!("Some strange error that should not happen")
        };
        p.delete(key).unwrap();
    }

    #[test]
    fn get_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Get";
        let value = "World";
        p.set(key, value, 10000).unwrap();
        assert_eq!(p.get(key).unwrap(),  value);
        match p.get("not found".to_string()) {
            Ok(_) => panic!("This key should not exist"),
            Err(BMemcachedError::Status(Status::KeyNotFound)) => {},
            Err(_) => panic!("This should return KeyNotFound")
        };
        p.delete(key).unwrap();
    }

    #[test]
    fn delete_key() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Delete";
        let value = "World";
        p.set(key, value, 1000).unwrap();
        p.delete(key).unwrap();
        p.delete(key).unwrap();
    }

    #[test]
    fn increment() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Increment";
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 0);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 1);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 2);
        p.delete(key).unwrap();
    }

    #[test]
    fn decrement() {
        let _ = env_logger::init();
        let mut p = Protocol::connect("127.0.0.1:11211").unwrap();
        let key = "Hello Decrement";
        assert_eq!(p.decrement(key, 1, 0, 1000).unwrap(), 0);
        assert_eq!(p.decrement(key, 1, 0, 1000).unwrap(), 0);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 1);
        assert_eq!(p.increment(key, 1, 0, 1000).unwrap(), 2);
        assert_eq!(p.decrement(key, 1, 0, 1000).unwrap(), 1);
        p.delete(key).unwrap();
    }
}
<|endoftext|>


<commit_before>use models::*;

pub fn print_headline_with_author(item: &HnItem, index: &i32) {
    let s = create_headline_with_author(item, index).unwrap(); // Not handling errs
    println!("{}", s);
}

fn create_headline_with_author(item: &HnItem, index: &i32) -> Result<String, String> {
    match item.title {
        Some(ref title) => {
            let s = format!("[{:3}] {:80} ~ {}",
                            &index,
                            &item.title.as_ref().unwrap(),
                            &item.by);
            Ok(s)

        }
        None => Err(String::from("Not headline")),
    }
}<commit_msg>added test<commit_after>use models::*;

pub fn print_headline_with_author(item: &HnItem, index: &i32) {
    let s = create_headline_with_author(item, index).unwrap(); // Not handling errs
    println!("{}", s);
}

fn create_headline_with_author(item: &HnItem, index: &i32) -> Result<String, String> {
    match item.title {
        Some(_) => {
            let s = format!("[{:3}] {:80} ~ {}",
                            index,
                            item.title.as_ref().unwrap(),
                            item.by);
            Ok(s)

        }
        None => Err(String::from("Not headline")),
    }
}

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

    #[test]
    fn create_headline_with_author_test() {
        use std::fs::File;
        use std::io::prelude::*;
        use serde_json;
        let mut contents = String::new();
        File::open("res/test/item.json")
            .and_then(|mut file| file.read_to_string(&mut contents))
            .unwrap();
        let deserialized: HnItem = serde_json::from_str(&contents).unwrap();
        let index = 1;
        let s:String = create_headline_with_author(&deserialized, &index).unwrap();
        assert!(s.len() != 0);
        assert!(s.contains("1"));
        assert!(s.contains("dhouston"));
        assert!(s.contains("My YC app: Dropbox - Throw away your USB drive"));
    }
}<|endoftext|>


<commit_before>use codegen::buffer::*;
use codegen::x64::reg::*;
use codegen::x64::reg::Reg::*;
use codegen::x64::emit::*;

use parser::ast::ctxt::Context;
use parser::ast::*;
use parser::ast::Expr::*;
use parser::ast::Stmt::*;
use parser::ast::visit::*;

pub struct CodeGen<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    fct: &'ast Function,
    buf: Buffer,

    lbl_break: Option<Label>,
    lbl_continue: Option<Label>,
}

impl<'a, 'ast> CodeGen<'a, 'ast> {
    pub fn new(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> CodeGen<'a, 'ast> {
        CodeGen {
            ctxt: ctxt,
            fct: fct,
            buf: Buffer::new(),

            lbl_break: None,
            lbl_continue: None
        }
    }

    pub fn generate(mut self) -> Buffer {
        self.emit_prolog();
        self.visit_fct(self.fct);
        self.emit_epilog();

        self.buf
    }

    fn emit_prolog(&mut self) {
        emit_pushq_reg(&mut self.buf, Reg::RBP);
        emit_movq_reg_reg(&mut self.buf, Reg::RSP, Reg::RBP);
    }

    fn emit_epilog(&mut self) {
        emit_popq_reg(&mut self.buf, Reg::RBP);
        emit_retq(&mut self.buf);
    }

    fn emit_stmt_return(&mut self, s: &'ast StmtReturnType) {
        if let Some(ref expr) = s.expr {
            self.visit_expr(expr);
        }
    }

    fn emit_stmt_while(&mut self, s: &'ast StmtWhileType) {
        let lbl_start = self.buf.create_label();
        let lbl_end = self.buf.create_label();

        self.buf.define_label(lbl_start);

        // execute condition, when condition is false jump to
        // end of while
        self.visit_expr(&s.cond);
        emit_testl_reg_reg(&mut self.buf, RAX, RAX);
        emit_jz(&mut self.buf, lbl_end);

        let old_lbl_break = self.lbl_break;
        let old_lbl_continue = self.lbl_continue;
        self.lbl_break = Some(lbl_end);
        self.lbl_continue = Some(lbl_start);

        // execute while body, then jump back to condition
        self.visit_stmt(&s.block);
        emit_jmp(&mut self.buf, lbl_start);

        self.lbl_break = old_lbl_break;
        self.lbl_continue = old_lbl_continue;

        self.buf.define_label(lbl_end);
    }

    fn emit_stmt_loop(&mut self, s: &'ast StmtLoopType) {
        let lbl_start = self.buf.create_label();
        let lbl_end = self.buf.create_label();
        self.buf.define_label(lbl_start);

        let old_lbl_break = self.lbl_break;
        let old_lbl_continue = self.lbl_continue;
        self.lbl_break = Some(lbl_end);
        self.lbl_continue = Some(lbl_start);

        self.visit_stmt(&s.block);

        self.lbl_break = old_lbl_break;
        self.lbl_continue = old_lbl_continue;

        self.buf.define_label(lbl_end);

        emit_jmp(&mut self.buf, lbl_start);
    }

    fn emit_stmt_if(&mut self, s: &'ast StmtIfType) {
        let lbl_end = self.buf.create_label();
        let lbl_else = if s.else_block.is_some() {
            self.buf.create_label()
        } else {
            lbl_end
        };

        self.visit_expr(&s.cond);
        emit_testl_reg_reg(&mut self.buf, RAX, RAX);
        emit_jz(&mut self.buf, lbl_else);

        self.visit_stmt(&s.then_block);

        if let Some(ref else_block) = s.else_block {
            emit_jmp(&mut self.buf, lbl_end);
            self.buf.define_label(lbl_else);

            self.visit_stmt(else_block);
        }

        self.buf.define_label(lbl_end);
    }

    fn emit_stmt_break(&mut self, s: &'ast StmtBreakType)  {
        emit_jmp(&mut self.buf, self.lbl_break.unwrap());
    }

    fn emit_stmt_continue(&mut self, s: &'ast StmtContinueType) {
        emit_jmp(&mut self.buf, self.lbl_continue.unwrap());
    }

    fn emit_stmt_expr(&mut self, s: &'ast StmtExprType) {
        self.visit_expr(&s.expr);
    }

    fn emit_stmt_block(&mut self, s: &'ast StmtBlockType) {
        for stmt in &s.stmts {
            self.visit_stmt(stmt);
        }
    }

    fn emit_stmt_var(&mut self, s: &'ast StmtVarType) {
        if let Some(ref expr) = s.expr {
            self.visit_expr(expr);

            // TODO: save result into var
            // self.ctxt.vars.borrow().get(&s.id).unwrap().offset;
        }
    }

    fn emit_expr_lit_int(&mut self, lit: &'ast ExprLitIntType) {
        emit_movl_imm_reg(&mut self.buf, lit.value as u32, Reg::RAX);
    }

    fn emit_expr_lit_bool(&mut self, lit: &'ast ExprLitBoolType) {
        let value : u32 = if lit.value { 1 } else { 0 };
        emit_movl_imm_reg(&mut self.buf, value, Reg::RAX);
    }
}

impl<'a, 'ast> visit::Visitor<'ast> for CodeGen<'a, 'ast> {
    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtExpr(ref stmt) => self.emit_stmt_expr(stmt),
            StmtIf(ref stmt) => self.emit_stmt_if(stmt),
            StmtLoop(ref stmt) => self.emit_stmt_loop(stmt),
            StmtWhile(ref stmt) => self.emit_stmt_while(stmt),
            StmtReturn(ref stmt) => self.emit_stmt_return(stmt),
            StmtBreak(ref stmt) => self.emit_stmt_break(stmt),
            StmtContinue(ref stmt) => self.emit_stmt_continue(stmt),
            StmtBlock(ref stmt) => self.emit_stmt_block(stmt),
            StmtVar(ref stmt) => self.emit_stmt_var(stmt),
        }
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        match *e {
            ExprLitInt(ref expr) => self.emit_expr_lit_int(expr),
            ExprLitBool(ref expr) => self.emit_expr_lit_bool(expr),
            _ => unreachable!()
        }
    }
}

#[cfg(test)]
mod tests {
    use parser::ast;
    use parser::Parser;
    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use mem::CodeMemory;
    use semck;
    use std::mem;

    use super::*;

    fn run<T>(code: &'static str) -> T {
        let (ast, interner) = Parser::from_str(code).parse().unwrap();
        let args : Args = Default::default();
        let ast_map = ast::map::build(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &ast_map, &ast);
        semck::check(&ctxt);

        assert!(!ctxt.diag.borrow().has_errors());

        // generate code for first function
        let fct = ast.elements[0].to_function().unwrap();

        let buffer = CodeGen::new(&ctxt, fct).generate().finish();
        let mem = CodeMemory::new(&buffer);

        let compiled_fct : extern "C" fn() -> T = unsafe { mem::transmute(mem.ptr()) };

        compiled_fct()
    }

    #[test]
    fn test_lit_int() {
        assert_eq!(1i32, run("fn f() -> int { return 1; }"));
        assert_eq!(2i32, run("fn f() -> int { return 2; }"));
        assert_eq!(3i32, run("fn f() -> int { return 3; }"));
    }

    #[test]
    fn test_lit_bool() {
        assert_eq!(true, run("fn f() -> bool { return true; }"));
        assert_eq!(false, run("fn f() -> bool { return false; }"));
    }
}
<commit_msg>codegen: added save_label_state function<commit_after>use codegen::buffer::*;
use codegen::info;
use codegen::x64::reg::*;
use codegen::x64::reg::Reg::*;
use codegen::x64::emit::*;

use parser::ast::ctxt::Context;
use parser::ast::*;
use parser::ast::Expr::*;
use parser::ast::Stmt::*;
use parser::ast::visit::*;

pub struct CodeGen<'a, 'ast: 'a> {
    ctxt: &'a Context<'a, 'ast>,
    fct: &'ast Function,
    buf: Buffer,

    lbl_break: Option<Label>,
    lbl_continue: Option<Label>,
}

impl<'a, 'ast> CodeGen<'a, 'ast> where 'ast: 'a {
    pub fn new(ctxt: &'a Context<'a, 'ast>, fct: &'ast Function) -> CodeGen<'a, 'ast> {
        CodeGen {
            ctxt: ctxt,
            fct: fct,
            buf: Buffer::new(),

            lbl_break: None,
            lbl_continue: None
        }
    }

    pub fn generate(mut self) -> Buffer {
        self.emit_prolog();
        self.visit_fct(self.fct);
        self.emit_epilog();

        self.buf
    }

    fn emit_prolog(&mut self) {
        emit_pushq_reg(&mut self.buf, Reg::RBP);
        emit_movq_reg_reg(&mut self.buf, Reg::RSP, Reg::RBP);
    }

    fn emit_epilog(&mut self) {
        emit_popq_reg(&mut self.buf, Reg::RBP);
        emit_retq(&mut self.buf);
    }

    fn emit_stmt_return(&mut self, s: &'ast StmtReturnType) {
        if let Some(ref expr) = s.expr {
            self.visit_expr(expr);
        }
    }

    fn emit_stmt_while(&mut self, s: &'ast StmtWhileType) {
        let lbl_start = self.buf.create_label();
        let lbl_end = self.buf.create_label();

        self.buf.define_label(lbl_start);

        // execute condition, when condition is false jump to
        // end of while
        self.visit_expr(&s.cond);
        emit_testl_reg_reg(&mut self.buf, RAX, RAX);
        emit_jz(&mut self.buf, lbl_end);

        self.save_label_state(lbl_end, lbl_start, |this| {
            // execute while body, then jump back to condition
            this.visit_stmt(&s.block);
            emit_jmp(&mut this.buf, lbl_start);
        });

        self.buf.define_label(lbl_end);
    }

    fn emit_stmt_loop(&mut self, s: &'ast StmtLoopType) {
        let lbl_start = self.buf.create_label();
        let lbl_end = self.buf.create_label();
        self.buf.define_label(lbl_start);

        self.save_label_state(lbl_end, lbl_start, |this| {
            this.visit_stmt(&s.block);
        });

        self.buf.define_label(lbl_end);

        emit_jmp(&mut self.buf, lbl_start);
    }

    fn save_label_state<F>(&mut self, lbl_break: Label, lbl_continue: Label, f: F)
            where F: FnOnce(&mut CodeGen<'a, 'ast>) {
        let old_lbl_break = self.lbl_break;
        let old_lbl_continue = self.lbl_continue;

        self.lbl_break = Some(lbl_break);
        self.lbl_continue = Some(lbl_continue);

        f(self);

        self.lbl_break = old_lbl_break;
        self.lbl_continue = old_lbl_continue;
    }

    fn emit_stmt_if(&mut self, s: &'ast StmtIfType) {
        let lbl_end = self.buf.create_label();
        let lbl_else = if s.else_block.is_some() {
            self.buf.create_label()
        } else {
            lbl_end
        };

        self.visit_expr(&s.cond);
        emit_testl_reg_reg(&mut self.buf, RAX, RAX);
        emit_jz(&mut self.buf, lbl_else);

        self.visit_stmt(&s.then_block);

        if let Some(ref else_block) = s.else_block {
            emit_jmp(&mut self.buf, lbl_end);
            self.buf.define_label(lbl_else);

            self.visit_stmt(else_block);
        }

        self.buf.define_label(lbl_end);
    }

    fn emit_stmt_break(&mut self, s: &'ast StmtBreakType)  {
        emit_jmp(&mut self.buf, self.lbl_break.unwrap());
    }

    fn emit_stmt_continue(&mut self, s: &'ast StmtContinueType) {
        emit_jmp(&mut self.buf, self.lbl_continue.unwrap());
    }

    fn emit_stmt_expr(&mut self, s: &'ast StmtExprType) {
        self.visit_expr(&s.expr);
    }

    fn emit_stmt_block(&mut self, s: &'ast StmtBlockType) {
        for stmt in &s.stmts {
            self.visit_stmt(stmt);
        }
    }

    fn emit_stmt_var(&mut self, s: &'ast StmtVarType) {
        if let Some(ref expr) = s.expr {
            self.visit_expr(expr);

            // TODO: save result into var
            // self.ctxt.vars.borrow().get(&s.id).unwrap().offset;
        }
    }

    fn emit_expr_lit_int(&mut self, lit: &'ast ExprLitIntType) {
        emit_movl_imm_reg(&mut self.buf, lit.value as u32, Reg::RAX);
    }

    fn emit_expr_lit_bool(&mut self, lit: &'ast ExprLitBoolType) {
        let value : u32 = if lit.value { 1 } else { 0 };
        emit_movl_imm_reg(&mut self.buf, value, Reg::RAX);
    }
}

impl<'a, 'ast> visit::Visitor<'ast> for CodeGen<'a, 'ast> {
    fn visit_stmt(&mut self, s: &'ast Stmt) {
        match *s {
            StmtExpr(ref stmt) => self.emit_stmt_expr(stmt),
            StmtIf(ref stmt) => self.emit_stmt_if(stmt),
            StmtLoop(ref stmt) => self.emit_stmt_loop(stmt),
            StmtWhile(ref stmt) => self.emit_stmt_while(stmt),
            StmtReturn(ref stmt) => self.emit_stmt_return(stmt),
            StmtBreak(ref stmt) => self.emit_stmt_break(stmt),
            StmtContinue(ref stmt) => self.emit_stmt_continue(stmt),
            StmtBlock(ref stmt) => self.emit_stmt_block(stmt),
            StmtVar(ref stmt) => self.emit_stmt_var(stmt),
        }
    }

    fn visit_expr(&mut self, e: &'ast Expr) {
        match *e {
            ExprLitInt(ref expr) => self.emit_expr_lit_int(expr),
            ExprLitBool(ref expr) => self.emit_expr_lit_bool(expr),
            _ => unreachable!()
        }
    }
}

#[cfg(test)]
mod tests {
    use parser::ast;
    use parser::Parser;
    use parser::ast::ctxt::Context;
    use driver::cmd::Args;
    use mem::CodeMemory;
    use semck;
    use std::mem;

    use super::*;

    fn run<T>(code: &'static str) -> T {
        let (ast, interner) = Parser::from_str(code).parse().unwrap();
        let args : Args = Default::default();
        let ast_map = ast::map::build(&ast, &interner);

        let ctxt = Context::new(&args, &interner, &ast_map, &ast);
        semck::check(&ctxt);

        assert!(!ctxt.diag.borrow().has_errors());

        // generate code for first function
        let fct = ast.elements[0].to_function().unwrap();

        let buffer = CodeGen::new(&ctxt, fct).generate().finish();
        let mem = CodeMemory::new(&buffer);

        let compiled_fct : extern "C" fn() -> T = unsafe { mem::transmute(mem.ptr()) };

        compiled_fct()
    }

    #[test]
    fn test_lit_int() {
        assert_eq!(1i32, run("fn f() -> int { return 1; }"));
        assert_eq!(2i32, run("fn f() -> int { return 2; }"));
        assert_eq!(3i32, run("fn f() -> int { return 3; }"));
    }

    #[test]
    fn test_lit_bool() {
        assert_eq!(true, run("fn f() -> bool { return true; }"));
        assert_eq!(false, run("fn f() -> bool { return false; }"));
    }
}
<|endoftext|>


<commit_before>use anyhow::*;
use diesel;
use diesel::prelude::*;
#[cfg(feature = "profile-index")]
use flame;
use log::{error, info};
use rayon::prelude::*;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::time;
use std::sync::mpsc::*;

use crate::config::MiscSettings;
use crate::db::{directories, misc_settings, songs, DB};
use crate::index::metadata;
use crate::vfs::VFSSource;

const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction
const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Insertions in each transaction

pub fn update(db: &DB) -> Result<()> {
	let start = time::Instant::now();
	info!("Beginning library index update");
	clean(db)?;
	populate(db)?;
	info!(
		"Library index update took {} seconds",
		start.elapsed().as_millis() as f32 / 1000.0
	);
	#[cfg(feature = "profile-index")]
	flame::dump_html(&mut fs::File::create("index-flame-graph.html").unwrap()).unwrap();
	Ok(())
}

#[derive(Debug, Insertable)]
#[table_name = "songs"]
struct NewSong {
	path: String,
	parent: String,
	track_number: Option<i32>,
	disc_number: Option<i32>,
	title: Option<String>,
	artist: Option<String>,
	album_artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	duration: Option<i32>,
}

#[derive(Debug, Insertable)]
#[table_name = "directories"]
struct NewDirectory {
	path: String,
	parent: Option<String>,
	artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	date_added: i32,
}

struct IndexUpdater {
	directory_sender: Sender<NewDirectory>,
	song_sender: Sender<NewSong>,
	album_art_pattern: Regex,
}

impl IndexUpdater {
	#[cfg_attr(feature = "profile-index", flame)]
	fn new(album_art_pattern: Regex, directory_sender: Sender<NewDirectory>, song_sender: Sender<NewSong>) -> Result<IndexUpdater> {
		Ok(IndexUpdater {
			directory_sender,
			song_sender,
			album_art_pattern,
		})
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_song(&mut self, song: NewSong) -> Result<()> {
		self.song_sender.send(song).map_err(Error::new)
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_directory(&mut self, directory: NewDirectory) -> Result<()> {
		self.directory_sender.send(directory).map_err(Error::new)
	}

	fn get_artwork(&self, dir: &Path) -> Result<Option<String>> {
		for file in fs::read_dir(dir)? {
			let file = file?;
			if let Some(name_string) = file.file_name().to_str() {
				if self.album_art_pattern.is_match(name_string) {
					return Ok(file.path().to_str().map(|p| p.to_owned()));
				}
			}
		}
		Ok(None)
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn populate_directory(&mut self, parent: Option<&Path>, path: &Path) -> Result<()> {
		// Find artwork
		let artwork = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("artwork");
			self.get_artwork(path).unwrap_or(None)
		};

		// Extract path and parent path
		let parent_string = parent.and_then(|p| p.to_str()).map(|s| s.to_owned());
		let path_string = path.to_str().ok_or(anyhow!("Invalid directory path"))?;

		// Find date added
		let metadata = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("metadata");
			fs::metadata(path_string)?
		};
		let created = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("created_date");
			metadata
			.created()
			.or_else(|_| metadata.modified())?
			.duration_since(time::UNIX_EPOCH)?
			.as_secs() as i32
		};

		let mut directory_album = None;
		let mut directory_year = None;
		let mut directory_artist = None;
		let mut inconsistent_directory_album = false;
		let mut inconsistent_directory_year = false;
		let mut inconsistent_directory_artist = false;

		// Sub directories
		let mut sub_directories = Vec::new();

		// Insert content
		for file in fs::read_dir(path)? {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("directory-entry");
			let file_path = match file {
				Ok(ref f) => f.path(),
				_ => {
					error!("File read error within {}", path_string);
					break;
				}
			};

			if file_path.is_dir() {
				sub_directories.push(file_path.to_path_buf());
				continue;
			}

			if let Some(file_path_string) = file_path.to_str() {
				if let Some(tags) = metadata::read(file_path.as_path()) {

					#[cfg(feature = "profile-index")]
					let _guard = flame::start_guard("process_song");

					if tags.year.is_some() {
						inconsistent_directory_year |=
							directory_year.is_some() && directory_year != tags.year;
						directory_year = tags.year;
					}

					if tags.album.is_some() {
						inconsistent_directory_album |=
							directory_album.is_some() && directory_album != tags.album;
						directory_album = tags.album.as_ref().cloned();
					}

					if tags.album_artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.album_artist;
						directory_artist = tags.album_artist.as_ref().cloned();
					} else if tags.artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.artist;
						directory_artist = tags.artist.as_ref().cloned();
					}

					let song = NewSong {
						path: file_path_string.to_owned(),
						parent: path_string.to_owned(),
						disc_number: tags.disc_number.map(|n| n as i32),
						track_number: tags.track_number.map(|n| n as i32),
						title: tags.title,
						duration: tags.duration.map(|n| n as i32),
						artist: tags.artist,
						album_artist: tags.album_artist,
						album: tags.album,
						year: tags.year,
						artwork: artwork.as_ref().cloned(),
					};

					self.push_song(song)?;
				}
			}
		}

		// Insert directory
		let directory = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("create_directory");

			if inconsistent_directory_year {
				directory_year = None;
			}
			if inconsistent_directory_album {
				directory_album = None;
			}
			if inconsistent_directory_artist {
				directory_artist = None;
			}

			NewDirectory {
				path: path_string.to_owned(),
				parent: parent_string,
				artwork,
				album: directory_album,
				artist: directory_artist,
				year: directory_year,
				date_added: created,
			}
		};

		self.push_directory(directory)?;

		// Populate subdirectories
		for sub_directory in sub_directories {
			self.populate_directory(Some(path), &sub_directory)?;
		}

		Ok(())
	}
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn clean(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;

	{
		let all_songs: Vec<String>;
		{
			let connection = db.connect()?;
			all_songs = songs::table.select(songs::path).load(&connection)?;
		}

		let missing_songs = all_songs
			.par_iter()
			.filter(|ref song_path| {
				let path = Path::new(&song_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(songs::table.filter(songs::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	{
		let all_directories: Vec<String>;
		{
			let connection = db.connect()?;
			all_directories = directories::table
				.select(directories::path)
				.load(&connection)?;
		}

		let missing_directories = all_directories
			.par_iter()
			.filter(|ref directory_path| {
				let path = Path::new(&directory_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(directories::table.filter(directories::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	Ok(())
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn populate(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;
	let mount_points = vfs.get_mount_points();

	let album_art_pattern  = {
		let connection = db.connect()?;
		let settings: MiscSettings = misc_settings::table.get_result(&connection)?;
		Regex::new(&settings.index_album_art_pattern)?
	};

	let (directory_sender, directory_receiver) = channel();
	let (song_sender, song_receiver) = channel();

	let songs_db = db.clone();
	let directories_db = db.clone();

	let directories_thread = std::thread::spawn(move || {
		insert_directories(directory_receiver, directories_db);
	});

	let songs_thread = std::thread::spawn(move || {
		insert_songs(song_receiver, songs_db);
	});

	{
		let mut updater = IndexUpdater::new(album_art_pattern, directory_sender, song_sender)?;
		for target in mount_points.values() {
			updater.populate_directory(None, target.as_path())?;
		}
	}

	match directories_thread.join() {
		Err(e) => error!("Error while waiting for directory insertions to complete: {:?}", e),
		_ => (),
	}

	match songs_thread.join() {
		Err(e) => error!("Error while waiting for song insertions to complete: {:?}", e),
		_ => (),
	}

	Ok(())
}

fn flush_directories(db: &DB, entries: &Vec<NewDirectory>) {
	if db.connect()
	.and_then(|connection|{
		diesel::insert_into(directories::table)
			.values(entries)
			.execute(&*connection) // TODO https://github.com/diesel-rs/diesel/issues/1822
			.map_err(Error::new)
	})
	.is_err() {
		error!("Could not insert new directories in database");
	}
}

fn flush_songs(db: &DB, entries: &Vec<NewSong>) {
	if db.connect()
	.and_then(|connection|{
		diesel::insert_into(songs::table)
			.values(entries)
			.execute(&*connection) // TODO https://github.com/diesel-rs/diesel/issues/1822
			.map_err(Error::new)
	})
	.is_err() {
		error!("Could not insert new songs in database");
	}
}

fn insert_directories(receiver: Receiver<NewDirectory>, db: DB) {
	let mut new_entries = Vec::new();
	new_entries.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);

	loop {
		match receiver.recv() {
			Ok(s) => {
				new_entries.push(s);
				if new_entries.len() >= INDEX_BUILDING_INSERT_BUFFER_SIZE {
					flush_directories(&db, &new_entries);
					new_entries.clear();
				}
			},
			Err(_) => break,
		}
	}

	if new_entries.len() > 0 {
		flush_directories(&db, &new_entries);
	}
}

fn insert_songs(receiver: Receiver<NewSong>, db: DB) {
	let mut new_entries = Vec::new();
	new_entries.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);

	loop {
		match receiver.recv() {
			Ok(s) => {
				new_entries.push(s);
				if new_entries.len() >= INDEX_BUILDING_INSERT_BUFFER_SIZE {
					flush_songs(&db, &new_entries);
					new_entries.clear();
				}
			},
			Err(_) => break,
		}
	}

	if new_entries.len() > 0 {
		flush_songs(&db, &new_entries);
	}
}
<commit_msg>More readable profile markers<commit_after>use anyhow::*;
use diesel;
use diesel::prelude::*;
#[cfg(feature = "profile-index")]
use flame;
use log::{error, info};
use rayon::prelude::*;
use regex::Regex;
use std::fs;
use std::path::Path;
use std::time;
use std::sync::mpsc::*;

use crate::config::MiscSettings;
use crate::db::{directories, misc_settings, songs, DB};
use crate::index::metadata;
use crate::vfs::VFSSource;

const INDEX_BUILDING_INSERT_BUFFER_SIZE: usize = 1000; // Insertions in each transaction
const INDEX_BUILDING_CLEAN_BUFFER_SIZE: usize = 500; // Insertions in each transaction

pub fn update(db: &DB) -> Result<()> {
	let start = time::Instant::now();
	info!("Beginning library index update");
	clean(db)?;
	populate(db)?;
	info!(
		"Library index update took {} seconds",
		start.elapsed().as_millis() as f32 / 1000.0
	);
	#[cfg(feature = "profile-index")]
	flame::dump_html(&mut fs::File::create("index-flame-graph.html").unwrap()).unwrap();
	Ok(())
}

#[derive(Debug, Insertable)]
#[table_name = "songs"]
struct NewSong {
	path: String,
	parent: String,
	track_number: Option<i32>,
	disc_number: Option<i32>,
	title: Option<String>,
	artist: Option<String>,
	album_artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	duration: Option<i32>,
}

#[derive(Debug, Insertable)]
#[table_name = "directories"]
struct NewDirectory {
	path: String,
	parent: Option<String>,
	artist: Option<String>,
	year: Option<i32>,
	album: Option<String>,
	artwork: Option<String>,
	date_added: i32,
}

struct IndexUpdater {
	directory_sender: Sender<NewDirectory>,
	song_sender: Sender<NewSong>,
	album_art_pattern: Regex,
}

impl IndexUpdater {
	#[cfg_attr(feature = "profile-index", flame)]
	fn new(album_art_pattern: Regex, directory_sender: Sender<NewDirectory>, song_sender: Sender<NewSong>) -> Result<IndexUpdater> {
		Ok(IndexUpdater {
			directory_sender,
			song_sender,
			album_art_pattern,
		})
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_song(&mut self, song: NewSong) -> Result<()> {
		self.song_sender.send(song).map_err(Error::new)
	}

	#[cfg_attr(feature = "profile-index", flame)]
	fn push_directory(&mut self, directory: NewDirectory) -> Result<()> {
		self.directory_sender.send(directory).map_err(Error::new)
	}

	fn get_artwork(&self, dir: &Path) -> Result<Option<String>> {
		for file in fs::read_dir(dir)? {
			let file = file?;
			if let Some(name_string) = file.file_name().to_str() {
				if self.album_art_pattern.is_match(name_string) {
					return Ok(file.path().to_str().map(|p| p.to_owned()));
				}
			}
		}
		Ok(None)
	}

	fn populate_directory(&mut self, parent: Option<&Path>, path: &Path) -> Result<()> {

		#[cfg(feature = "profile-index")]
		let _guard = flame::start_guard(format!("dir: {}",
			path.file_name().map(|s| {
				s.to_string_lossy().into_owned()
			}).unwrap_or("Unknown".to_owned())
		));

		// Find artwork
		let artwork = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("artwork");
			self.get_artwork(path).unwrap_or(None)
		};

		// Extract path and parent path
		let parent_string = parent.and_then(|p| p.to_str()).map(|s| s.to_owned());
		let path_string = path.to_str().ok_or(anyhow!("Invalid directory path"))?;

		// Find date added
		let metadata = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("metadata");
			fs::metadata(path_string)?
		};
		let created = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("created_date");
			metadata
			.created()
			.or_else(|_| metadata.modified())?
			.duration_since(time::UNIX_EPOCH)?
			.as_secs() as i32
		};

		let mut directory_album = None;
		let mut directory_year = None;
		let mut directory_artist = None;
		let mut inconsistent_directory_album = false;
		let mut inconsistent_directory_year = false;
		let mut inconsistent_directory_artist = false;

		// Sub directories
		let mut sub_directories = Vec::new();

		// Insert content
		for file in fs::read_dir(path)? {

			let file_path = match file {
				Ok(ref f) => f.path(),
				_ => {
					error!("File read error within {}", path_string);
					break;
				}
			};

			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard(format!("file: {}",
				file_path.as_path().file_name().map(|s| {
					s.to_string_lossy().into_owned()
				}).unwrap_or("Unknown".to_owned())
			));

			if file_path.is_dir() {
				sub_directories.push(file_path.to_path_buf());
				continue;
			}

			if let Some(file_path_string) = file_path.to_str() {
				if let Some(tags) = metadata::read(file_path.as_path()) {

					if tags.year.is_some() {
						inconsistent_directory_year |=
							directory_year.is_some() && directory_year != tags.year;
						directory_year = tags.year;
					}

					if tags.album.is_some() {
						inconsistent_directory_album |=
							directory_album.is_some() && directory_album != tags.album;
						directory_album = tags.album.as_ref().cloned();
					}

					if tags.album_artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.album_artist;
						directory_artist = tags.album_artist.as_ref().cloned();
					} else if tags.artist.is_some() {
						inconsistent_directory_artist |=
							directory_artist.is_some() && directory_artist != tags.artist;
						directory_artist = tags.artist.as_ref().cloned();
					}

					let song = NewSong {
						path: file_path_string.to_owned(),
						parent: path_string.to_owned(),
						disc_number: tags.disc_number.map(|n| n as i32),
						track_number: tags.track_number.map(|n| n as i32),
						title: tags.title,
						duration: tags.duration.map(|n| n as i32),
						artist: tags.artist,
						album_artist: tags.album_artist,
						album: tags.album,
						year: tags.year,
						artwork: artwork.as_ref().cloned(),
					};

					self.push_song(song)?;
				}
			}
		}

		// Insert directory
		let directory = {
			#[cfg(feature = "profile-index")]
			let _guard = flame::start_guard("create_directory");

			if inconsistent_directory_year {
				directory_year = None;
			}
			if inconsistent_directory_album {
				directory_album = None;
			}
			if inconsistent_directory_artist {
				directory_artist = None;
			}

			NewDirectory {
				path: path_string.to_owned(),
				parent: parent_string,
				artwork,
				album: directory_album,
				artist: directory_artist,
				year: directory_year,
				date_added: created,
			}
		};

		self.push_directory(directory)?;

		// Populate subdirectories
		for sub_directory in sub_directories {
			self.populate_directory(Some(path), &sub_directory)?;
		}

		Ok(())
	}
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn clean(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;

	{
		let all_songs: Vec<String>;
		{
			let connection = db.connect()?;
			all_songs = songs::table.select(songs::path).load(&connection)?;
		}

		let missing_songs = all_songs
			.par_iter()
			.filter(|ref song_path| {
				let path = Path::new(&song_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_songs[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(songs::table.filter(songs::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	{
		let all_directories: Vec<String>;
		{
			let connection = db.connect()?;
			all_directories = directories::table
				.select(directories::path)
				.load(&connection)?;
		}

		let missing_directories = all_directories
			.par_iter()
			.filter(|ref directory_path| {
				let path = Path::new(&directory_path);
				!path.exists() || vfs.real_to_virtual(path).is_err()
			})
			.collect::<Vec<_>>();

		{
			let connection = db.connect()?;
			for chunk in missing_directories[..].chunks(INDEX_BUILDING_CLEAN_BUFFER_SIZE) {
				diesel::delete(directories::table.filter(directories::path.eq_any(chunk)))
					.execute(&connection)?;
			}
		}
	}

	Ok(())
}

#[cfg_attr(feature = "profile-index", flame)]
pub fn populate(db: &DB) -> Result<()> {
	let vfs = db.get_vfs()?;
	let mount_points = vfs.get_mount_points();

	let album_art_pattern  = {
		let connection = db.connect()?;
		let settings: MiscSettings = misc_settings::table.get_result(&connection)?;
		Regex::new(&settings.index_album_art_pattern)?
	};

	let (directory_sender, directory_receiver) = channel();
	let (song_sender, song_receiver) = channel();

	let songs_db = db.clone();
	let directories_db = db.clone();

	let directories_thread = std::thread::spawn(move || {
		insert_directories(directory_receiver, directories_db);
	});

	let songs_thread = std::thread::spawn(move || {
		insert_songs(song_receiver, songs_db);
	});

	{
		let mut updater = IndexUpdater::new(album_art_pattern, directory_sender, song_sender)?;
		for target in mount_points.values() {
			updater.populate_directory(None, target.as_path())?;
		}
	}

	match directories_thread.join() {
		Err(e) => error!("Error while waiting for directory insertions to complete: {:?}", e),
		_ => (),
	}

	match songs_thread.join() {
		Err(e) => error!("Error while waiting for song insertions to complete: {:?}", e),
		_ => (),
	}

	Ok(())
}

fn flush_directories(db: &DB, entries: &Vec<NewDirectory>) {
	if db.connect()
	.and_then(|connection|{
		diesel::insert_into(directories::table)
			.values(entries)
			.execute(&*connection) // TODO https://github.com/diesel-rs/diesel/issues/1822
			.map_err(Error::new)
	})
	.is_err() {
		error!("Could not insert new directories in database");
	}
}

fn flush_songs(db: &DB, entries: &Vec<NewSong>) {
	if db.connect()
	.and_then(|connection|{
		diesel::insert_into(songs::table)
			.values(entries)
			.execute(&*connection) // TODO https://github.com/diesel-rs/diesel/issues/1822
			.map_err(Error::new)
	})
	.is_err() {
		error!("Could not insert new songs in database");
	}
}

fn insert_directories(receiver: Receiver<NewDirectory>, db: DB) {
	let mut new_entries = Vec::new();
	new_entries.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);

	loop {
		match receiver.recv() {
			Ok(s) => {
				new_entries.push(s);
				if new_entries.len() >= INDEX_BUILDING_INSERT_BUFFER_SIZE {
					flush_directories(&db, &new_entries);
					new_entries.clear();
				}
			},
			Err(_) => break,
		}
	}

	if new_entries.len() > 0 {
		flush_directories(&db, &new_entries);
	}
}

fn insert_songs(receiver: Receiver<NewSong>, db: DB) {
	let mut new_entries = Vec::new();
	new_entries.reserve_exact(INDEX_BUILDING_INSERT_BUFFER_SIZE);

	loop {
		match receiver.recv() {
			Ok(s) => {
				new_entries.push(s);
				if new_entries.len() >= INDEX_BUILDING_INSERT_BUFFER_SIZE {
					flush_songs(&db, &new_entries);
					new_entries.clear();
				}
			},
			Err(_) => break,
		}
	}

	if new_entries.len() > 0 {
		flush_songs(&db, &new_entries);
	}
}
<|endoftext|>