<commit_before>// Copyright 2020 The Evcxr Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::code_block::CodeBlock;
use crate::errors::{bail, CompilationError, Error};
use crate::EvalContext;
use json;
use regex::Regex;
use std;
use std::fs;
use std::path::{Path, PathBuf};
use which;

fn shared_object_name_from_crate_name(crate_name: &str) -> String {
    if cfg!(target_os = "macos") {
        format!("lib{}.dylib", crate_name)
    } else if cfg!(target_os = "windows") {
        format!("{}.dll", crate_name)
    } else {
        format!("lib{}.so", crate_name)
    }
}

fn create_dir(dir: &Path) -> Result<(), Error> {
    if let Err(err) = fs::create_dir_all(dir) {
        bail!("Error creating directory '{:?}': {}", dir, err);
    }
    Ok(())
}

fn write_file(dir: &Path, basename: &str, contents: &str) -> Result<(), Error> {
    create_dir(dir)?;
    let filename = dir.join(basename);
    if let Err(err) = fs::write(&filename, contents) {
        bail!("Error writing '{:?}': {}", filename, err);
    }
    Ok(())
}

/// On Mac, if we copy the dylib, we get intermittent failures where we end up
/// with the previous version of the file when we shouldn't. On windows, if
/// rename the file, we get errors subsequently when something (perhaps the
/// Windows linker) tries to delete the file that it expects to still be there.
/// On Linux either renaming or copying works, but renaming should be more
/// efficient, so we do that.
#[cfg(windows)]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    // Copy file by reading and writing instead of using std::fs::copy. The src
    // is a hard-linked file and we want to make extra sure that we end up with
    // a completely indepent copy.
    fn alt_copy(src: &Path, dest: &Path) -> Result<(), std::io::Error> {
        use std::fs::File;
        std::io::copy(&mut File::open(src)?, &mut File::create(dest)?)?;
        Ok(())
    }
    if let Err(err) = alt_copy(src, dest) {
        bail!("Error copying '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

#[cfg(not(windows))]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    if let Err(err) = fs::rename(src, dest) {
        bail!("Error renaming '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

pub(crate) struct Module {
    pub(crate) tmpdir: PathBuf,
    build_num: i32,
    /// Whether to pass -Ztime-passes to the compiler and print the result.
    /// Causes the nightly compiler, which must be installed to be selected.
    pub(crate) time_passes: bool,
    pub(crate) linker: String,
    sccache: Option<PathBuf>,
}

const CRATE_NAME: &str = "ctx";

impl Module {
    pub(crate) fn new(tmpdir: PathBuf) -> Result<Module, Error> {
        let linker = if !cfg!(target_os = "macos") && which::which("lld").is_ok() {
            "lld".to_owned()
        } else {
            "system".to_owned()
        };
        let module = Module {
            tmpdir,
            build_num: 0,
            time_passes: false,
            linker,
            sccache: None,
        };
        Ok(module)
    }

    pub(crate) fn deps_dir(&self) -> PathBuf {
        self.target_dir().join("debug").join("deps")
    }

    fn target_dir(&self) -> PathBuf {
        self.tmpdir.join("target")
    }

    fn so_path(&self) -> PathBuf {
        self.deps_dir()
            .join(shared_object_name_from_crate_name(CRATE_NAME))
    }

    fn src_dir(&self) -> PathBuf {
        self.tmpdir.join("src")
    }

    pub(crate) fn crate_dir(&self) -> &Path {
        &self.tmpdir
    }

    pub fn set_sccache(&mut self, enabled: bool) -> Result<(), Error> {
        if enabled {
            if let Ok(path) = which::which("sccache") {
                self.sccache = Some(path);
            } else {
                bail!("Couldn't find sccache. Try running `cargo install sscache`.");
            }
        } else {
            self.sccache = None;
        }
        Ok(())
    }

    pub fn sccache(&self) -> bool {
        self.sccache.is_some()
    }

    // Writes Cargo.toml. Should be called before compile.
    pub(crate) fn write_cargo_toml(&self, eval_context: &EvalContext) -> Result<(), Error> {
        write_file(
            self.crate_dir(),
            "Cargo.toml",
            &self.get_cargo_toml_contents(eval_context),
        )
    }

    pub(crate) fn compile(&mut self, code_block: &CodeBlock) -> Result<SoFile, Error> {
        write_file(&self.src_dir(), "lib.rs", &code_block.code_string())?;

        // Our compiler errors should all be in JSON format, but for errors from Cargo errors, we
        // need to add explicit matching for those errors that we expect we might see.
        lazy_static! {
            static ref KNOWN_NON_JSON_ERRORS: Regex =
                Regex::new("(error: no matching package named)").unwrap();
        }

        let mut command = std::process::Command::new("cargo");
        if self.time_passes {
            command.arg("+nightly");
        }
        command
            .arg("rustc")
            .arg("--message-format=json")
            .arg("--")
            .arg("-C")
            .arg("prefer-dynamic")
            .env("CARGO_TARGET_DIR", "target")
            .current_dir(self.crate_dir());
        if self.linker != "system" {
            command
                .arg("-C")
                .arg(format!("link-arg=-fuse-ld={}", self.linker));
        }
        if let Some(sccache) = &self.sccache {
            command.env("RUSTC_WRAPPER", sccache);
        }
        if self.time_passes {
            command.arg("-Ztime-passes");
        }
        let cargo_output = match command.output() {
            Ok(out) => out,
            Err(err) => bail!("Error running cargo rustc: {}", err),
        };
        if cargo_output.status.success() {
            if self.time_passes {
                let stdout = String::from_utf8_lossy(&cargo_output.stdout);
                eprintln!("{}", stdout);
            }
        } else {
            let stderr = String::from_utf8_lossy(&cargo_output.stderr);
            let stdout = String::from_utf8_lossy(&cargo_output.stdout);
            let mut non_json_error = None;
            let errors: Vec<CompilationError> = stderr
                .lines()
                .chain(stdout.lines())
                .filter_map(|line| {
                    json::parse(&line)
                        .ok()
                        .and_then(|json| CompilationError::opt_new(json, code_block))
                        .or_else(|| {
                            if KNOWN_NON_JSON_ERRORS.is_match(line) {
                                non_json_error = Some(line);
                            }
                            None
                        })
                })
                .collect();
            if errors.is_empty() {
                if let Some(error) = non_json_error {
                    bail!(Error::Message(error.to_owned()));
                } else {
                    bail!(Error::Message(format!(
                        "Compilation failed, but no parsable errors were found. STDERR:\n\
                         {}\nSTDOUT:{}\n",
                        stderr, stdout
                    )));
                }
            } else {
                bail!(Error::CompilationErrors(errors));
            }
        }
        self.build_num += 1;
        let copied_so_file = self
            .deps_dir()
            .join(shared_object_name_from_crate_name(&format!(
                "code_{}",
                self.build_num
            )));
        // Every time we compile, the output file is the same. We need to
        // renamed it so that we have a unique filename, otherwise we wouldn't
        // be able to load the result of the next compilation. Also, on Windows,
        // a loaded dll gets locked, so we couldn't even compile a second time
        // if we didn't load a different file.
        rename_or_copy_so_file(&self.so_path(), &copied_so_file)?;
        Ok(SoFile {
            path: copied_so_file,
        })
    }

    fn get_cargo_toml_contents(&self, eval_context: &EvalContext) -> String {
        let crate_imports = eval_context.format_cargo_deps();
        format!(
            r#"
[package]
name = "{}"
version = "1.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[profile.dev]
opt-level = {}
debug = false
rpath = true
lto = false
debug-assertions = true
codegen-units = 16
panic = 'unwind'
incremental = true
overflow-checks = true

[dependencies]
{}
"#,
            CRATE_NAME,
            eval_context.opt_level(),
            crate_imports
        )
    }
}

pub(crate) struct SoFile {
    pub(crate) path: PathBuf,
}
<commit_msg>Make an error slightly clearer<commit_after>// Copyright 2020 The Evcxr Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use crate::code_block::CodeBlock;
use crate::errors::{bail, CompilationError, Error};
use crate::EvalContext;
use json;
use regex::Regex;
use std;
use std::fs;
use std::path::{Path, PathBuf};
use which;

fn shared_object_name_from_crate_name(crate_name: &str) -> String {
    if cfg!(target_os = "macos") {
        format!("lib{}.dylib", crate_name)
    } else if cfg!(target_os = "windows") {
        format!("{}.dll", crate_name)
    } else {
        format!("lib{}.so", crate_name)
    }
}

fn create_dir(dir: &Path) -> Result<(), Error> {
    if let Err(err) = fs::create_dir_all(dir) {
        bail!("Error creating directory '{:?}': {}", dir, err);
    }
    Ok(())
}

fn write_file(dir: &Path, basename: &str, contents: &str) -> Result<(), Error> {
    create_dir(dir)?;
    let filename = dir.join(basename);
    if let Err(err) = fs::write(&filename, contents) {
        bail!("Error writing '{:?}': {}", filename, err);
    }
    Ok(())
}

/// On Mac, if we copy the dylib, we get intermittent failures where we end up
/// with the previous version of the file when we shouldn't. On windows, if
/// rename the file, we get errors subsequently when something (perhaps the
/// Windows linker) tries to delete the file that it expects to still be there.
/// On Linux either renaming or copying works, but renaming should be more
/// efficient, so we do that.
#[cfg(windows)]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    // Copy file by reading and writing instead of using std::fs::copy. The src
    // is a hard-linked file and we want to make extra sure that we end up with
    // a completely indepent copy.
    fn alt_copy(src: &Path, dest: &Path) -> Result<(), std::io::Error> {
        use std::fs::File;
        std::io::copy(&mut File::open(src)?, &mut File::create(dest)?)?;
        Ok(())
    }
    if let Err(err) = alt_copy(src, dest) {
        bail!("Error copying '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

#[cfg(not(windows))]
fn rename_or_copy_so_file(src: &Path, dest: &Path) -> Result<(), Error> {
    if let Err(err) = fs::rename(src, dest) {
        bail!("Error renaming '{:?}' to '{:?}': {}", src, dest, err);
    }
    Ok(())
}

pub(crate) struct Module {
    pub(crate) tmpdir: PathBuf,
    build_num: i32,
    /// Whether to pass -Ztime-passes to the compiler and print the result.
    /// Causes the nightly compiler, which must be installed to be selected.
    pub(crate) time_passes: bool,
    pub(crate) linker: String,
    sccache: Option<PathBuf>,
}

const CRATE_NAME: &str = "ctx";

impl Module {
    pub(crate) fn new(tmpdir: PathBuf) -> Result<Module, Error> {
        let linker = if !cfg!(target_os = "macos") && which::which("lld").is_ok() {
            "lld".to_owned()
        } else {
            "system".to_owned()
        };
        let module = Module {
            tmpdir,
            build_num: 0,
            time_passes: false,
            linker,
            sccache: None,
        };
        Ok(module)
    }

    pub(crate) fn deps_dir(&self) -> PathBuf {
        self.target_dir().join("debug").join("deps")
    }

    fn target_dir(&self) -> PathBuf {
        self.tmpdir.join("target")
    }

    fn so_path(&self) -> PathBuf {
        self.deps_dir()
            .join(shared_object_name_from_crate_name(CRATE_NAME))
    }

    fn src_dir(&self) -> PathBuf {
        self.tmpdir.join("src")
    }

    pub(crate) fn crate_dir(&self) -> &Path {
        &self.tmpdir
    }

    pub fn set_sccache(&mut self, enabled: bool) -> Result<(), Error> {
        if enabled {
            if let Ok(path) = which::which("sccache") {
                self.sccache = Some(path);
            } else {
                bail!("Couldn't find sccache. Try running `cargo install sscache`.");
            }
        } else {
            self.sccache = None;
        }
        Ok(())
    }

    pub fn sccache(&self) -> bool {
        self.sccache.is_some()
    }

    // Writes Cargo.toml. Should be called before compile.
    pub(crate) fn write_cargo_toml(&self, eval_context: &EvalContext) -> Result<(), Error> {
        write_file(
            self.crate_dir(),
            "Cargo.toml",
            &self.get_cargo_toml_contents(eval_context),
        )
    }

    pub(crate) fn compile(&mut self, code_block: &CodeBlock) -> Result<SoFile, Error> {
        write_file(&self.src_dir(), "lib.rs", &code_block.code_string())?;

        // Our compiler errors should all be in JSON format, but for errors from Cargo errors, we
        // need to add explicit matching for those errors that we expect we might see.
        lazy_static! {
            static ref KNOWN_NON_JSON_ERRORS: Regex =
                Regex::new("(error: no matching package named)").unwrap();
        }

        let mut command = std::process::Command::new("cargo");
        if self.time_passes {
            command.arg("+nightly");
        }
        command
            .arg("rustc")
            .arg("--message-format=json")
            .arg("--")
            .arg("-C")
            .arg("prefer-dynamic")
            .env("CARGO_TARGET_DIR", "target")
            .current_dir(self.crate_dir());
        if self.linker != "system" {
            command
                .arg("-C")
                .arg(format!("link-arg=-fuse-ld={}", self.linker));
        }
        if let Some(sccache) = &self.sccache {
            command.env("RUSTC_WRAPPER", sccache);
        }
        if self.time_passes {
            command.arg("-Ztime-passes");
        }
        let cargo_output = match command.output() {
            Ok(out) => out,
            Err(err) => bail!("Error running 'cargo rustc': {}", err),
        };
        if cargo_output.status.success() {
            if self.time_passes {
                let stdout = String::from_utf8_lossy(&cargo_output.stdout);
                eprintln!("{}", stdout);
            }
        } else {
            let stderr = String::from_utf8_lossy(&cargo_output.stderr);
            let stdout = String::from_utf8_lossy(&cargo_output.stdout);
            let mut non_json_error = None;
            let errors: Vec<CompilationError> = stderr
                .lines()
                .chain(stdout.lines())
                .filter_map(|line| {
                    json::parse(&line)
                        .ok()
                        .and_then(|json| CompilationError::opt_new(json, code_block))
                        .or_else(|| {
                            if KNOWN_NON_JSON_ERRORS.is_match(line) {
                                non_json_error = Some(line);
                            }
                            None
                        })
                })
                .collect();
            if errors.is_empty() {
                if let Some(error) = non_json_error {
                    bail!(Error::Message(error.to_owned()));
                } else {
                    bail!(Error::Message(format!(
                        "Compilation failed, but no parsable errors were found. STDERR:\n\
                         {}\nSTDOUT:{}\n",
                        stderr, stdout
                    )));
                }
            } else {
                bail!(Error::CompilationErrors(errors));
            }
        }
        self.build_num += 1;
        let copied_so_file = self
            .deps_dir()
            .join(shared_object_name_from_crate_name(&format!(
                "code_{}",
                self.build_num
            )));
        // Every time we compile, the output file is the same. We need to
        // renamed it so that we have a unique filename, otherwise we wouldn't
        // be able to load the result of the next compilation. Also, on Windows,
        // a loaded dll gets locked, so we couldn't even compile a second time
        // if we didn't load a different file.
        rename_or_copy_so_file(&self.so_path(), &copied_so_file)?;
        Ok(SoFile {
            path: copied_so_file,
        })
    }

    fn get_cargo_toml_contents(&self, eval_context: &EvalContext) -> String {
        let crate_imports = eval_context.format_cargo_deps();
        format!(
            r#"
[package]
name = "{}"
version = "1.0.0"
edition = "2018"

[lib]
crate-type = ["cdylib"]

[profile.dev]
opt-level = {}
debug = false
rpath = true
lto = false
debug-assertions = true
codegen-units = 16
panic = 'unwind'
incremental = true
overflow-checks = true

[dependencies]
{}
"#,
            CRATE_NAME,
            eval_context.opt_level(),
            crate_imports
        )
    }
}

pub(crate) struct SoFile {
    pub(crate) path: PathBuf,
}
<|endoftext|>


<commit_before>use kbdgen::{bundle::Save, cldr::Keyboard};
use kbdgen_cli::{cldr_dir, update_cldr_repo};
use snafu::{OptionExt, ResultExt, Snafu};
use snafu_cli_debug::SnafuCliDebug;
use std::{collections::BTreeMap, path::PathBuf};
use structopt::StructOpt;

#[derive(Snafu, SnafuCliDebug)]
pub enum Error {
    #[snafu(display("Updating CLDR repo failed"))]
    CldrRepoUpdate {
        source: kbdgen_cli::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("No locale selected"))]
    NoLocaleSelected { backtrace: snafu::Backtrace },
    #[snafu(display("Could load kbdgen bundle: {}", source))]
    CannotLoad {
        source: kbdgen::LoadError,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could write kbdgen bundle: {}", source))]
    CannotSave {
        source: kbdgen::SaveError,
        backtrace: snafu::Backtrace,
    },
}

pub fn select_base_locale() -> Option<(String, BTreeMap<String, Vec<String>>)> {
    let kbd_path = cldr_dir().join("keyboards");
    let set: BTreeMap<String, BTreeMap<String, Vec<String>>> = BTreeMap::new();
    let mut locale_map = globwalk::GlobWalkerBuilder::new(kbd_path, "*.xml")
        .build()
        .unwrap()
        .filter_map(Result::ok)
        .filter(|entry| {
            !entry
                .path()
                .file_stem()
                .unwrap()
                .to_string_lossy()
                .starts_with('_')
        })
        .fold(set, |mut acc, cur| {
            let tag = (&*cur.path().file_stem().unwrap().to_string_lossy())
                .split("-t")
                .next()
                .unwrap()
                .to_string();
            let kbd_os = (&*cur
                .path()
                .parent()
                .unwrap()
                .file_name()
                .unwrap()
                .to_string_lossy())
                .to_string();
            let entry = acc
                .entry(tag)
                .or_insert_with(BTreeMap::new)
                .entry(kbd_os)
                .or_insert_with(Vec::new);
            (*entry).push(
                cur.path()
                    .file_name()
                    .unwrap()
                    .to_string_lossy()
                    .to_string(),
            );
            acc
        });
    let mut locales = locale_map.iter().collect::<Vec<_>>();
    locales.sort();

    let options = skim::SkimOptionsBuilder::default()
        .prompt(Some("Which locale to use as base? "))
        .exact(true)
        .ansi(true)
        .build()
        .unwrap();

    let cyan = console::Style::new().cyan().dim();
    let text = locales
        .iter()
        .map(|(locale, items)| {
            let x = items.keys().map(|x| &**x).collect::<Vec<_>>();
            format!("{}   {}", locale, cyan.apply_to(x.join(", ")))
        })
        .collect::<Vec<_>>()
        .join("\n")
        .as_bytes()
        .to_owned();
    let cur = std::io::Cursor::new(text);

    let result = skim::Skim::run_with(&options, Some(Box::new(cur)))?;
    let result = result
        .selected_items
        .first()?
        .get_text()
        .split("   ")
        .next()
        .unwrap();

    Some((result.to_string(), locale_map.remove(result).unwrap()))
}

pub fn parse_path(os: &str, file: &str) -> Keyboard {
    let fn_ = cldr_dir().join("keyboards").join(os).join(file);

    let f = std::fs::File::open(fn_).unwrap();
    let kbd: Keyboard = serde_xml_rs::from_reader(f).unwrap();
    kbd
}

#[derive(Debug, StructOpt)]
struct Cli {
    #[structopt(parse(from_os_str))]
    output: PathBuf,

    #[structopt(flatten)]
    verbose: clap_verbosity_flag::Verbosity,
}

fn main() -> Result<(), Error> {
    let opts = Cli::from_args();
    let _ = opts.verbose.setup_env_logger("cldr");

    update_cldr_repo().context(CldrRepoUpdate)?;
    let locale = select_base_locale().context(NoLocaleSelected)?;

    log::debug!("Selected locale: '{}'", &locale.0);
    log::debug!("Files: {:#?}", &locale.1);

    let mut modes = kbdgen::models::Modes::default();

    let xml_map = locale
        .1
        .into_iter()
        .map(|(key, mut v)| {
            v.sort();
            let last = v.last().unwrap();
            parse_path(&key, last)
        })
        .collect::<Vec<_>>();

    for keyboard in xml_map {
        match keyboard.mode_name() {
            "mobile" => modes.mobile = Some(keyboard.to_mobile_modes()),
            "mac" => modes.mac = Some(keyboard.to_desktop_modes()),
            "win" => modes.win = Some(keyboard.to_desktop_modes()),
            "chrome" => modes.chrome = Some(keyboard.to_desktop_modes()),
            _ => {}
        }
    }

    let mut layout = kbdgen::models::Layout::default();
    layout.modes = modes;

    let mut bundle = kbdgen::bundle::ProjectBundle::default();
    bundle.layouts.insert(locale.0, layout);

    let output = opts.output;
    bundle.save(&output).context(CannotSave)?;
    log::info!("New bundle written to `{}`.", output.display());

    Ok(())
}
<commit_msg>Pretty XML errors<commit_after>use kbdgen::{bundle::Save, cldr::Keyboard};
use kbdgen_cli::{cldr_dir, update_cldr_repo};
use snafu::{OptionExt, ResultExt, Snafu};
use snafu_cli_debug::SnafuCliDebug;
use std::{collections::BTreeMap, path::PathBuf};
use structopt::StructOpt;

#[derive(Snafu, SnafuCliDebug)]
pub enum Error {
    #[snafu(display("Updating CLDR repo failed"))]
    CldrRepoUpdate {
        source: kbdgen_cli::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("No locale selected"))]
    NoLocaleSelected { backtrace: snafu::Backtrace },
    #[snafu(display("Could load CLDR file"))]
    CannotOpenFile {
        source: std::io::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could load CLDR file"))]
    CannotReadXml {
        source: serde_xml_rs::Error,
        backtrace: snafu::Backtrace,
    },
    #[snafu(display("Could write kbdgen bundle"))]
    CannotSave {
        source: kbdgen::SaveError,
        backtrace: snafu::Backtrace,
    },
}

pub fn select_base_locale() -> Option<(String, BTreeMap<String, Vec<String>>)> {
    let kbd_path = cldr_dir().join("keyboards");
    let set: BTreeMap<String, BTreeMap<String, Vec<String>>> = BTreeMap::new();
    let mut locale_map = globwalk::GlobWalkerBuilder::new(kbd_path, "*.xml")
        .build()
        .unwrap()
        .filter_map(Result::ok)
        .filter(|entry| {
            !entry
                .path()
                .file_stem()
                .unwrap()
                .to_string_lossy()
                .starts_with('_')
        })
        .fold(set, |mut acc, cur| {
            let tag = (&*cur.path().file_stem().unwrap().to_string_lossy())
                .split("-t")
                .next()
                .unwrap()
                .to_string();
            let kbd_os = (&*cur
                .path()
                .parent()
                .unwrap()
                .file_name()
                .unwrap()
                .to_string_lossy())
                .to_string();
            let entry = acc
                .entry(tag)
                .or_insert_with(BTreeMap::new)
                .entry(kbd_os)
                .or_insert_with(Vec::new);
            (*entry).push(
                cur.path()
                    .file_name()
                    .unwrap()
                    .to_string_lossy()
                    .to_string(),
            );
            acc
        });
    let mut locales = locale_map.iter().collect::<Vec<_>>();
    locales.sort();

    let options = skim::SkimOptionsBuilder::default()
        .prompt(Some("Which locale to use as base? "))
        .exact(true)
        .ansi(true)
        .build()
        .unwrap();

    let cyan = console::Style::new().cyan().dim();
    let text = locales
        .iter()
        .map(|(locale, items)| {
            let x = items.keys().map(|x| &**x).collect::<Vec<_>>();
            format!("{}   {}", locale, cyan.apply_to(x.join(", ")))
        })
        .collect::<Vec<_>>()
        .join("\n")
        .as_bytes()
        .to_owned();
    let cur = std::io::Cursor::new(text);

    let result = skim::Skim::run_with(&options, Some(Box::new(cur)))?;
    let result = result
        .selected_items
        .first()?
        .get_text()
        .split("   ")
        .next()
        .unwrap();

    Some((result.to_string(), locale_map.remove(result).unwrap()))
}

pub fn parse_path(os: &str, file: &str) -> Result<Keyboard, Error> {
    let fn_ = cldr_dir().join("keyboards").join(os).join(file);

    let f = std::fs::File::open(fn_).context(CannotOpenFile)?;
    let kbd: Keyboard = serde_xml_rs::from_reader(f).context(CannotReadXml)?;
    Ok(kbd)
}

#[derive(Debug, StructOpt)]
struct Cli {
    #[structopt(parse(from_os_str))]
    output: PathBuf,

    #[structopt(flatten)]
    verbose: clap_verbosity_flag::Verbosity,
}

fn main() -> Result<(), Error> {
    let opts = Cli::from_args();
    let _ = opts.verbose.setup_env_logger("cldr-to-kbdgen");

    update_cldr_repo().context(CldrRepoUpdate)?;
    let locale = select_base_locale().context(NoLocaleSelected)?;

    log::debug!("Selected locale: '{}'", &locale.0);
    log::debug!("Files: {:#?}", &locale.1);

    let mut modes = kbdgen::models::Modes::default();

    let xml_map: Vec<Keyboard> = locale
        .1
        .into_iter()
        .map(|(key, mut v)| {
            v.sort();
            let last = v.last().unwrap();
            parse_path(&key, last)
        })
        .collect::<Result<_, _>>()?;

    for keyboard in dbg!(xml_map) {
        match keyboard.mode_name() {
            "mobile" => modes.mobile = Some(keyboard.to_mobile_modes()),
            "mac" => modes.mac = Some(keyboard.to_desktop_modes()),
            "win" => modes.win = Some(keyboard.to_desktop_modes()),
            "chrome" => modes.chrome = Some(keyboard.to_desktop_modes()),
            _ => {}
        }
    }

    let mut layout = kbdgen::models::Layout::default();
    layout.modes = modes;

    let mut bundle = kbdgen::bundle::ProjectBundle::default();
    bundle.layouts.insert(locale.0, layout);

    let output = opts.output;
    bundle.save(&output).context(CannotSave)?;
    log::info!("New bundle written to `{}`.", output.display());

    Ok(())
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate timely;
extern crate timely_sort;
extern crate differential_dataflow;
extern crate vec_map;

use timely::dataflow::*;
use timely::dataflow::operators::*;

use rand::{Rng, SeedableRng, StdRng};

use differential_dataflow::AsCollection;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::trace::{Cursor, Trace};

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();

    // define a new computational scope, in which to run BFS
    timely::execute_from_args(std::env::args().skip(5), move |computation| {

    	let index = computation.index();
    	let peers = computation.peers();

    	// create a a degree counting differential dataflow
    	let (mut input, probe, trace) = computation.scoped(|scope| {

    		// create edge input, count a few ways.
    		let (input, edges) = scope.new_input();

    		// pull off source, and count.
    		let arranged = edges.as_collection()
    							.arrange_by_key();

		    (input, arranged.stream.probe().0, arranged.trace.clone())
    	});

        let seed: &[_] = &[1, 2, 3, index];
        let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
        let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions

        // load up graph dataz
        let &time = input.time();
        for edge in 0..edges {
        	if edge % peers == index {
        		input.send(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), time, 1));
        	}

        	// move the data along a bit
        	if edge % 10000 == 9999 {
        		computation.step();
        	}
		}

		let timer = ::std::time::Instant::now();

		input.advance_to(1);
		computation.step_while(|| probe.lt(input.time()));

		if index == 0 {
			let timer = timer.elapsed();
			let nanos = timer.as_secs() * 1000000000 + timer.subsec_nanos() as u64;
			println!("Loading finished after {:?}", nanos);
		}

		// change graph, forever
		if batch > 0 {

			for edge in 0usize .. {
				let &time = input.time();
				if edge % peers == index {
	        		input.send(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), time, 1));
	        		input.send(((rng2.gen_range(0, nodes), rng2.gen_range(0, nodes)), time,-1));
				}

	        	if edge % batch == (batch - 1) {

	        		let timer = ::std::time::Instant::now();

	        		let next = input.epoch() + 1;
	        		input.advance_to(next);
					computation.step_while(|| probe.lt(input.time()));

					if index == 0 {
						let timer = timer.elapsed();
						let nanos = timer.as_secs() * 1000000000 + timer.subsec_nanos() as u64;
						println!("Round {} finished after {:?}", next - 1, nanos);

						let mut count = 0;
		        		let timer = ::std::time::Instant::now();
		        		let mut cursor = trace.borrow().trace.cursor();
		        		while cursor.key_valid() {
		        			while cursor.val_valid() {
								let mut sum = 0;
								cursor.map_times(|_,d| sum += d);
								if sum > 0 { count += 1; }
								cursor.step_val();
		        			}

		        			cursor.step_key()
		        		}

		        		println!("count: {} in {:?}", count, timer.elapsed());
					}
	        	}
	        }
	    }

    }).unwrap();
}<commit_msg>corrected arrange.rs<commit_after>extern crate rand;
extern crate timely;
extern crate timely_sort;
extern crate differential_dataflow;
extern crate vec_map;

use timely::dataflow::*;
use timely::dataflow::operators::*;

use rand::{Rng, SeedableRng, StdRng};

use differential_dataflow::AsCollection;
use differential_dataflow::operators::arrange::ArrangeByKey;
use differential_dataflow::trace::{Cursor, Trace};

fn main() {

    let nodes: u32 = std::env::args().nth(1).unwrap().parse().unwrap();
    let edges: usize = std::env::args().nth(2).unwrap().parse().unwrap();
    let batch: usize = std::env::args().nth(3).unwrap().parse().unwrap();

    // define a new computational scope, in which to run BFS
    timely::execute_from_args(std::env::args().skip(5), move |computation| {

    	let index = computation.index();
    	let peers = computation.peers();

    	// create a a degree counting differential dataflow
    	let (mut input, probe, trace) = computation.scoped(|scope| {

    		// create edge input, count a few ways.
    		let (input, edges) = scope.new_input();

    		// pull off source, and count.
    		let arranged = edges.as_collection()
    							.arrange_by_key();

		    (input, arranged.stream.probe().0, arranged.trace.clone())
    	});

        let seed: &[_] = &[1, 2, 3, index];
        let mut rng1: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions
        let mut rng2: StdRng = SeedableRng::from_seed(seed);    // rng for edge additions

        // load up graph dataz
        let &time = input.time();
        for edge in 0..edges {
        	if edge % peers == index {
        		input.send(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), time, 1));
        	}

        	// move the data along a bit
        	if edge % 10000 == 9999 {
        		computation.step();
        	}
		}

		let timer = ::std::time::Instant::now();

		input.advance_to(1);
		computation.step_while(|| probe.lt(input.time()));

		if index == 0 {
			let timer = timer.elapsed();
			let nanos = timer.as_secs() * 1000000000 + timer.subsec_nanos() as u64;
			println!("Loading finished after {:?}", nanos);
		}

		// change graph, forever
		if batch > 0 {

			for edge in 0usize .. {
				let &time = input.time();
				if edge % peers == index {
	        		input.send(((rng1.gen_range(0, nodes), rng1.gen_range(0, nodes)), time, 1));
	        		input.send(((rng2.gen_range(0, nodes), rng2.gen_range(0, nodes)), time,-1));
				}

	        	if edge % batch == (batch - 1) {

	        		let timer = ::std::time::Instant::now();

	        		let next = input.epoch() + 1;
	        		input.advance_to(next);
					computation.step_while(|| probe.lt(input.time()));

					if index == 0 {
						let timer = timer.elapsed();
						let nanos = timer.as_secs() * 1000000000 + timer.subsec_nanos() as u64;
						println!("Round {} finished after {:?}", next - 1, nanos);

						let mut count = 0;
		        		let timer = ::std::time::Instant::now();
		        		let mut cursor = trace.cursor();
		        		while cursor.key_valid() {
		        			while cursor.val_valid() {
								let mut sum = 0;
								cursor.map_times(|_,d| sum += d);
								if sum > 0 { count += 1; }
								cursor.step_val();
		        			}

		        			cursor.step_key()
		        		}

		        		println!("count: {} in {:?}", count, timer.elapsed());
					}
	        	}
	        }
	    }

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


<commit_before>extern crate synthrs;

use synthrs::filter::*;
use synthrs::synthesizer::{make_samples, quantize_samples};
use synthrs::wave::sine_wave;
use synthrs::writer::write_wav_file;

fn main() {
    // Lowpass/highpass filter convolution example
    let sample = make_samples(1.0, 44_100, |t: f64| -> f64 {
        0.33 * (sine_wave(6000.0)(t) + sine_wave(700.0)(t) + sine_wave(80.0)(t))
    });

    let lowpass = lowpass_filter(cutoff_from_frequency(400.0, 44_100), 0.01);
    let mut lowpass_samples = quantize_samples::<i16>(&sample);
    lowpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&lowpass, &sample)));
    write_wav_file("out/lowpass.wav", 44_100, &lowpass_samples).expect("failed");

    let highpass = highpass_filter(cutoff_from_frequency(2000.0, 44_100), 0.01);
    let mut highpass_samples = quantize_samples::<i16>(&sample);
    highpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&highpass, &sample)));
    write_wav_file("out/highpass.wav", 44_100, &highpass_samples).expect("failed");

    let bandpass = bandpass_filter(
        cutoff_from_frequency(500.0, 44_100),
        cutoff_from_frequency(3000.0, 44_100),
        0.01,
    );
    let mut bandpass_samples = quantize_samples::<i16>(&sample);
    bandpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandpass, &sample)));
    write_wav_file("out/bandpass.wav", 44_100, &bandpass_samples).expect("failed");

    let bandreject = bandreject_filter(
        cutoff_from_frequency(400.0, 44_100),
        cutoff_from_frequency(2000.0, 44_100),
        0.01,
    );
    let mut bandreject_samples = quantize_samples::<i16>(&sample);
    bandreject_samples
        .extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandreject, &sample)));
    write_wav_file("out/bandreject.wav", 44_100, &bandreject_samples).expect("failed");

    // Stateful filters
    let mut comb = Comb::new(0.2, 44_100, 0.5, 0.5, 0.5);
    let comb_samples: Vec<f64> = sample.clone().into_iter().map(|s| comb.tick(s)).collect();
    write_wav_file(
        "out/comb.wav",
        44_100,
        &*quantize_samples::<i16>(comb_samples.as_slice()),
    )
    .expect("failed");

    let mut allpass = AllPass::new(1.0, 44_100, 0.5);
    let allpass_samples: Vec<f64> = sample
        .into_iter()
        .map(|s| allpass.tick(s))
        .collect();
    write_wav_file(
        "out/allpass.wav",
        44_100,
        &*quantize_samples::<i16>(allpass_samples.as_slice()),
    )
    .expect("failed");
}
<commit_msg>Run cargo fmt on examples/filters.rs<commit_after>extern crate synthrs;

use synthrs::filter::*;
use synthrs::synthesizer::{make_samples, quantize_samples};
use synthrs::wave::sine_wave;
use synthrs::writer::write_wav_file;

fn main() {
    // Lowpass/highpass filter convolution example
    let sample = make_samples(1.0, 44_100, |t: f64| -> f64 {
        0.33 * (sine_wave(6000.0)(t) + sine_wave(700.0)(t) + sine_wave(80.0)(t))
    });

    let lowpass = lowpass_filter(cutoff_from_frequency(400.0, 44_100), 0.01);
    let mut lowpass_samples = quantize_samples::<i16>(&sample);
    lowpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&lowpass, &sample)));
    write_wav_file("out/lowpass.wav", 44_100, &lowpass_samples).expect("failed");

    let highpass = highpass_filter(cutoff_from_frequency(2000.0, 44_100), 0.01);
    let mut highpass_samples = quantize_samples::<i16>(&sample);
    highpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&highpass, &sample)));
    write_wav_file("out/highpass.wav", 44_100, &highpass_samples).expect("failed");

    let bandpass = bandpass_filter(
        cutoff_from_frequency(500.0, 44_100),
        cutoff_from_frequency(3000.0, 44_100),
        0.01,
    );
    let mut bandpass_samples = quantize_samples::<i16>(&sample);
    bandpass_samples.extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandpass, &sample)));
    write_wav_file("out/bandpass.wav", 44_100, &bandpass_samples).expect("failed");

    let bandreject = bandreject_filter(
        cutoff_from_frequency(400.0, 44_100),
        cutoff_from_frequency(2000.0, 44_100),
        0.01,
    );
    let mut bandreject_samples = quantize_samples::<i16>(&sample);
    bandreject_samples
        .extend_from_slice(&*quantize_samples::<i16>(&convolve(&bandreject, &sample)));
    write_wav_file("out/bandreject.wav", 44_100, &bandreject_samples).expect("failed");

    // Stateful filters
    let mut comb = Comb::new(0.2, 44_100, 0.5, 0.5, 0.5);
    let comb_samples: Vec<f64> = sample.clone().into_iter().map(|s| comb.tick(s)).collect();
    write_wav_file(
        "out/comb.wav",
        44_100,
        &*quantize_samples::<i16>(comb_samples.as_slice()),
    )
    .expect("failed");

    let mut allpass = AllPass::new(1.0, 44_100, 0.5);
    let allpass_samples: Vec<f64> = sample.into_iter().map(|s| allpass.tick(s)).collect();
    write_wav_file(
        "out/allpass.wav",
        44_100,
        &*quantize_samples::<i16>(allpass_samples.as_slice()),
    )
    .expect("failed");
}
<|endoftext|>


<commit_before>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use internals::ast::{Container, Data, Field, Style};
use internals::attr::{EnumTag, Identifier};
use internals::{Ctxt, Derive};
use syn::{Member, Type};

/// Cross-cutting checks that require looking at more than a single attrs
/// object. Simpler checks should happen when parsing and building the attrs.
pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
    check_getter(cx, cont);
    check_flatten(cx, cont);
    check_identifier(cx, cont);
    check_variant_skip_attrs(cx, cont);
    check_internal_tag_field_name_conflict(cx, cont);
    check_adjacent_tag_conflict(cx, cont);
    check_transparent(cx, cont, derive);
}

/// Getters are only allowed inside structs (not enums) with the `remote`
/// attribute.
fn check_getter(cx: &Ctxt, cont: &Container) {
    match cont.data {
        Data::Enum(_) => {
            if cont.data.has_getter() {
                cx.error("#[serde(getter = \"...\")] is not allowed in an enum");
            }
        }
        Data::Struct(_, _) => {
            if cont.data.has_getter() && cont.attrs.remote().is_none() {
                cx.error(
                    "#[serde(getter = \"...\")] can only be used in structs \
                     that have #[serde(remote = \"...\")]",
                );
            }
        }
    }
}

/// Flattening has some restrictions we can test.
fn check_flatten(cx: &Ctxt, cont: &Container) {
    match cont.data {
        Data::Enum(ref variants) => {
            for variant in variants {
                for field in &variant.fields {
                    check_flatten_field(cx, variant.style, field);
                }
            }
        }
        Data::Struct(style, ref fields) => {
            for field in fields {
                check_flatten_field(cx, style, field);
            }
        }
    }
}

fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
    if !field.attrs.flatten() {
        return;
    }
    match style {
        Style::Tuple => {
            cx.error("#[serde(flatten)] cannot be used on tuple structs");
        }
        Style::Newtype => {
            cx.error("#[serde(flatten)] cannot be used on newtype structs");
        }
        _ => {}
    }
    if field.attrs.skip_serializing() {
        cx.error(
            "#[serde(flatten] can not be combined with \
             #[serde(skip_serializing)]",
        );
    } else if field.attrs.skip_serializing_if().is_some() {
        cx.error(
            "#[serde(flatten] can not be combined with \
             #[serde(skip_serializing_if = \"...\")]",
        );
    } else if field.attrs.skip_deserializing() {
        cx.error(
            "#[serde(flatten] can not be combined with \
             #[serde(skip_deserializing)]",
        );
    }
}

/// The `other` attribute must be used at most once and it must be the last
/// variant of an enum.
///
/// Inside a `variant_identifier` all variants must be unit variants. Inside a
/// `field_identifier` all but possibly one variant must be unit variants. The
/// last variant may be a newtype variant which is an implicit "other" case.
fn check_identifier(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for (i, variant) in variants.iter().enumerate() {
        match (
            variant.style,
            cont.attrs.identifier(),
            variant.attrs.other(),
            cont.attrs.tag()
        ) {
            // The `other` attribute may not be used in a variant_identifier.
            (_, Identifier::Variant, true, _) => {
                cx.error("#[serde(other)] may not be used on a variant_identifier");
            },

            // Variant with `other` attribute cannot appear in untagged enum
            (_, Identifier::No, true, EnumTag::None) => {
                cx.error("#[serde(other)] cannot appear on untagged enum");
            }

            // Variant with `other` attribute must be the last one.
            (Style::Unit, Identifier::Field, true, _) | (_, Identifier::No, true, _) => {
                if i < variants.len() - 1 {
                    cx.error("#[serde(other)] must be the last variant");
                }
            }

            // Variant with `other` attribute must be a unit variant.
            // TODO: Only in field_identifier ?
            (_, Identifier::Field, true, _) => {
                cx.error("#[serde(other)] must be on a unit variant");
            },

            // Any sort of variant is allowed if this is not an identifier.
            (_, Identifier::No, false, _) => {}

            // Unit variant without `other` attribute is always fine.
            (Style::Unit, _, false, _) => {}

            // The last field is allowed to be a newtype catch-all.
            (Style::Newtype, Identifier::Field, false, _) => {
                if i < variants.len() - 1 {
                    cx.error(format!("`{}` must be the last variant", variant.ident));
                }
            }

            (_, Identifier::Field, false, _) => {
                cx.error("field_identifier may only contain unit variants");
            }

            (_, Identifier::Variant, false, _) => {
                cx.error("variant_identifier may only contain unit variants");
            }
        }
    }
}

/// Skip-(de)serializing attributes are not allowed on variants marked
/// (de)serialize_with.
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for variant in variants.iter() {
        if variant.attrs.serialize_with().is_some() {
            if variant.attrs.skip_serializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(serialize_with)] and \
                     #[serde(skip_serializing)]",
                    variant.ident
                ));
            }

            for field in &variant.fields {
                let member = member_message(&field.member);

                if field.attrs.skip_serializing() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing)]",
                        variant.ident, member
                    ));
                }

                if field.attrs.skip_serializing_if().is_some() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing_if)]",
                        variant.ident, member
                    ));
                }
            }
        }

        if variant.attrs.deserialize_with().is_some() {
            if variant.attrs.skip_deserializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(deserialize_with)] and \
                     #[serde(skip_deserializing)]",
                    variant.ident
                ));
            }

            for field in &variant.fields {
                if field.attrs.skip_deserializing() {
                    let member = member_message(&field.member);

                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(deserialize_with)] \
                         and a field {} marked with #[serde(skip_deserializing)]",
                        variant.ident, member
                    ));
                }
            }
        }
    }
}

/// The tag of an internally-tagged struct variant must not be
/// the same as either one of its fields, as this would result in
/// duplicate keys in the serialized output and/or ambiguity in
/// the to-be-deserialized input.
fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => return,
    };

    let tag = match *cont.attrs.tag() {
        EnumTag::Internal { ref tag } => tag.as_str(),
        EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return,
    };

    let diagnose_conflict = || {
        let message = format!("variant field name `{}` conflicts with internal tag", tag);
        cx.error(message);
    };

    for variant in variants {
        match variant.style {
            Style::Struct => {
                for field in &variant.fields {
                    let check_ser = !field.attrs.skip_serializing();
                    let check_de = !field.attrs.skip_deserializing();
                    let name = field.attrs.name();
                    let ser_name = name.serialize_name();
                    let de_name = name.deserialize_name();

                    if check_ser && ser_name == tag || check_de && de_name == tag {
                        diagnose_conflict();
                        return;
                    }
                }
            }
            Style::Unit | Style::Newtype | Style::Tuple => {}
        }
    }
}

/// In the case of adjacently-tagged enums, the type and the
/// contents tag must differ, for the same reason.
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
    let (type_tag, content_tag) = match *cont.attrs.tag() {
        EnumTag::Adjacent {
            ref tag,
            ref content,
        } => (tag, content),
        EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return,
    };

    if type_tag == content_tag {
        let message = format!(
            "enum tags `{}` for type and content conflict with each other",
            type_tag
        );
        cx.error(message);
    }
}

/// Enums and unit structs cannot be transparent.
fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
    if !cont.attrs.transparent() {
        return;
    }

    if cont.attrs.type_from().is_some() {
        cx.error("#[serde(transparent)] is not allowed with #[serde(from = \"...\")]");
    }

    if cont.attrs.type_into().is_some() {
        cx.error("#[serde(transparent)] is not allowed with #[serde(into = \"...\")]");
    }

    let fields = match cont.data {
        Data::Enum(_) => {
            cx.error("#[serde(transparent)] is not allowed on an enum");
            return;
        }
        Data::Struct(Style::Unit, _) => {
            cx.error("#[serde(transparent)] is not allowed on a unit struct");
            return;
        }
        Data::Struct(_, ref mut fields) => fields,
    };

    let mut transparent_field = None;

    for field in fields {
        if allow_transparent(field, derive) {
            if transparent_field.is_some() {
                cx.error(
                    "#[serde(transparent)] requires struct to have at most one transparent field",
                );
                return;
            }
            transparent_field = Some(field);
        }
    }

    match transparent_field {
        Some(transparent_field) => transparent_field.attrs.mark_transparent(),
        None => match derive {
            Derive::Serialize => {
                cx.error("#[serde(transparent)] requires at least one field that is not skipped");
            }
            Derive::Deserialize => {
                cx.error("#[serde(transparent)] requires at least one field that is neither skipped nor has a default");
            }
        },
    }
}

fn member_message(member: &Member) -> String {
    match *member {
        Member::Named(ref ident) => format!("`{}`", ident),
        Member::Unnamed(ref i) => i.index.to_string(),
    }
}

fn allow_transparent(field: &Field, derive: Derive) -> bool {
    if let Type::Path(ref ty) = *field.ty {
        if let Some(seg) = ty.path.segments.last() {
            if seg.into_value().ident == "PhantomData" {
                return false;
            }
        }
    }

    match derive {
        Derive::Serialize => !field.attrs.skip_serializing(),
        Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
    }
}
<commit_msg>Fix for rust 1.15<commit_after>// Copyright 2017 Serde Developers
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use internals::ast::{Container, Data, Field, Style};
use internals::attr::{EnumTag, Identifier};
use internals::{Ctxt, Derive};
use syn::{Member, Type};

/// Cross-cutting checks that require looking at more than a single attrs
/// object. Simpler checks should happen when parsing and building the attrs.
pub fn check(cx: &Ctxt, cont: &mut Container, derive: Derive) {
    check_getter(cx, cont);
    check_flatten(cx, cont);
    check_identifier(cx, cont);
    check_variant_skip_attrs(cx, cont);
    check_internal_tag_field_name_conflict(cx, cont);
    check_adjacent_tag_conflict(cx, cont);
    check_transparent(cx, cont, derive);
}

/// Getters are only allowed inside structs (not enums) with the `remote`
/// attribute.
fn check_getter(cx: &Ctxt, cont: &Container) {
    match cont.data {
        Data::Enum(_) => {
            if cont.data.has_getter() {
                cx.error("#[serde(getter = \"...\")] is not allowed in an enum");
            }
        }
        Data::Struct(_, _) => {
            if cont.data.has_getter() && cont.attrs.remote().is_none() {
                cx.error(
                    "#[serde(getter = \"...\")] can only be used in structs \
                     that have #[serde(remote = \"...\")]",
                );
            }
        }
    }
}

/// Flattening has some restrictions we can test.
fn check_flatten(cx: &Ctxt, cont: &Container) {
    match cont.data {
        Data::Enum(ref variants) => {
            for variant in variants {
                for field in &variant.fields {
                    check_flatten_field(cx, variant.style, field);
                }
            }
        }
        Data::Struct(style, ref fields) => {
            for field in fields {
                check_flatten_field(cx, style, field);
            }
        }
    }
}

fn check_flatten_field(cx: &Ctxt, style: Style, field: &Field) {
    if !field.attrs.flatten() {
        return;
    }
    match style {
        Style::Tuple => {
            cx.error("#[serde(flatten)] cannot be used on tuple structs");
        }
        Style::Newtype => {
            cx.error("#[serde(flatten)] cannot be used on newtype structs");
        }
        _ => {}
    }
    if field.attrs.skip_serializing() {
        cx.error(
            "#[serde(flatten] can not be combined with \
             #[serde(skip_serializing)]",
        );
    } else if field.attrs.skip_serializing_if().is_some() {
        cx.error(
            "#[serde(flatten] can not be combined with \
             #[serde(skip_serializing_if = \"...\")]",
        );
    } else if field.attrs.skip_deserializing() {
        cx.error(
            "#[serde(flatten] can not be combined with \
             #[serde(skip_deserializing)]",
        );
    }
}

/// The `other` attribute must be used at most once and it must be the last
/// variant of an enum.
///
/// Inside a `variant_identifier` all variants must be unit variants. Inside a
/// `field_identifier` all but possibly one variant must be unit variants. The
/// last variant may be a newtype variant which is an implicit "other" case.
fn check_identifier(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for (i, variant) in variants.iter().enumerate() {
        match (
            variant.style,
            cont.attrs.identifier(),
            variant.attrs.other(),
            cont.attrs.tag()
        ) {
            // The `other` attribute may not be used in a variant_identifier.
            (_, Identifier::Variant, true, _) => {
                cx.error("#[serde(other)] may not be used on a variant_identifier");
            },

            // Variant with `other` attribute cannot appear in untagged enum
            (_, Identifier::No, true, &EnumTag::None) => {
                cx.error("#[serde(other)] cannot appear on untagged enum");
            }

            // Variant with `other` attribute must be the last one.
            (Style::Unit, Identifier::Field, true, _) | (_, Identifier::No, true, _) => {
                if i < variants.len() - 1 {
                    cx.error("#[serde(other)] must be the last variant");
                }
            }

            // Variant with `other` attribute must be a unit variant.
            // TODO: Only in field_identifier ?
            (_, Identifier::Field, true, _) => {
                cx.error("#[serde(other)] must be on a unit variant");
            },

            // Any sort of variant is allowed if this is not an identifier.
            (_, Identifier::No, false, _) => {}

            // Unit variant without `other` attribute is always fine.
            (Style::Unit, _, false, _) => {}

            // The last field is allowed to be a newtype catch-all.
            (Style::Newtype, Identifier::Field, false, _) => {
                if i < variants.len() - 1 {
                    cx.error(format!("`{}` must be the last variant", variant.ident));
                }
            }

            (_, Identifier::Field, false, _) => {
                cx.error("field_identifier may only contain unit variants");
            }

            (_, Identifier::Variant, false, _) => {
                cx.error("variant_identifier may only contain unit variants");
            }
        }
    }
}

/// Skip-(de)serializing attributes are not allowed on variants marked
/// (de)serialize_with.
fn check_variant_skip_attrs(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => {
            return;
        }
    };

    for variant in variants.iter() {
        if variant.attrs.serialize_with().is_some() {
            if variant.attrs.skip_serializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(serialize_with)] and \
                     #[serde(skip_serializing)]",
                    variant.ident
                ));
            }

            for field in &variant.fields {
                let member = member_message(&field.member);

                if field.attrs.skip_serializing() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing)]",
                        variant.ident, member
                    ));
                }

                if field.attrs.skip_serializing_if().is_some() {
                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(serialize_with)] and \
                         a field {} marked with #[serde(skip_serializing_if)]",
                        variant.ident, member
                    ));
                }
            }
        }

        if variant.attrs.deserialize_with().is_some() {
            if variant.attrs.skip_deserializing() {
                cx.error(format!(
                    "variant `{}` cannot have both #[serde(deserialize_with)] and \
                     #[serde(skip_deserializing)]",
                    variant.ident
                ));
            }

            for field in &variant.fields {
                if field.attrs.skip_deserializing() {
                    let member = member_message(&field.member);

                    cx.error(format!(
                        "variant `{}` cannot have both #[serde(deserialize_with)] \
                         and a field {} marked with #[serde(skip_deserializing)]",
                        variant.ident, member
                    ));
                }
            }
        }
    }
}

/// The tag of an internally-tagged struct variant must not be
/// the same as either one of its fields, as this would result in
/// duplicate keys in the serialized output and/or ambiguity in
/// the to-be-deserialized input.
fn check_internal_tag_field_name_conflict(cx: &Ctxt, cont: &Container) {
    let variants = match cont.data {
        Data::Enum(ref variants) => variants,
        Data::Struct(_, _) => return,
    };

    let tag = match *cont.attrs.tag() {
        EnumTag::Internal { ref tag } => tag.as_str(),
        EnumTag::External | EnumTag::Adjacent { .. } | EnumTag::None => return,
    };

    let diagnose_conflict = || {
        let message = format!("variant field name `{}` conflicts with internal tag", tag);
        cx.error(message);
    };

    for variant in variants {
        match variant.style {
            Style::Struct => {
                for field in &variant.fields {
                    let check_ser = !field.attrs.skip_serializing();
                    let check_de = !field.attrs.skip_deserializing();
                    let name = field.attrs.name();
                    let ser_name = name.serialize_name();
                    let de_name = name.deserialize_name();

                    if check_ser && ser_name == tag || check_de && de_name == tag {
                        diagnose_conflict();
                        return;
                    }
                }
            }
            Style::Unit | Style::Newtype | Style::Tuple => {}
        }
    }
}

/// In the case of adjacently-tagged enums, the type and the
/// contents tag must differ, for the same reason.
fn check_adjacent_tag_conflict(cx: &Ctxt, cont: &Container) {
    let (type_tag, content_tag) = match *cont.attrs.tag() {
        EnumTag::Adjacent {
            ref tag,
            ref content,
        } => (tag, content),
        EnumTag::Internal { .. } | EnumTag::External | EnumTag::None => return,
    };

    if type_tag == content_tag {
        let message = format!(
            "enum tags `{}` for type and content conflict with each other",
            type_tag
        );
        cx.error(message);
    }
}

/// Enums and unit structs cannot be transparent.
fn check_transparent(cx: &Ctxt, cont: &mut Container, derive: Derive) {
    if !cont.attrs.transparent() {
        return;
    }

    if cont.attrs.type_from().is_some() {
        cx.error("#[serde(transparent)] is not allowed with #[serde(from = \"...\")]");
    }

    if cont.attrs.type_into().is_some() {
        cx.error("#[serde(transparent)] is not allowed with #[serde(into = \"...\")]");
    }

    let fields = match cont.data {
        Data::Enum(_) => {
            cx.error("#[serde(transparent)] is not allowed on an enum");
            return;
        }
        Data::Struct(Style::Unit, _) => {
            cx.error("#[serde(transparent)] is not allowed on a unit struct");
            return;
        }
        Data::Struct(_, ref mut fields) => fields,
    };

    let mut transparent_field = None;

    for field in fields {
        if allow_transparent(field, derive) {
            if transparent_field.is_some() {
                cx.error(
                    "#[serde(transparent)] requires struct to have at most one transparent field",
                );
                return;
            }
            transparent_field = Some(field);
        }
    }

    match transparent_field {
        Some(transparent_field) => transparent_field.attrs.mark_transparent(),
        None => match derive {
            Derive::Serialize => {
                cx.error("#[serde(transparent)] requires at least one field that is not skipped");
            }
            Derive::Deserialize => {
                cx.error("#[serde(transparent)] requires at least one field that is neither skipped nor has a default");
            }
        },
    }
}

fn member_message(member: &Member) -> String {
    match *member {
        Member::Named(ref ident) => format!("`{}`", ident),
        Member::Unnamed(ref i) => i.index.to_string(),
    }
}

fn allow_transparent(field: &Field, derive: Derive) -> bool {
    if let Type::Path(ref ty) = *field.ty {
        if let Some(seg) = ty.path.segments.last() {
            if seg.into_value().ident == "PhantomData" {
                return false;
            }
        }
    }

    match derive {
        Derive::Serialize => !field.attrs.skip_serializing(),
        Derive::Deserialize => !field.attrs.skip_deserializing() && field.attrs.default().is_none(),
    }
}
<|endoftext|>


<commit_before>//! A simple example which prints out information about available Metal devices

extern crate mtl;

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {
    println!("Metal is not supported on this platform");
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    let device = match mtl::Device::system_default_device() {
        Ok(device) => device,
        Err(e) => {
            use std::error::Error;
            println!("{}", e.description());
            return;
        }
    };

    println!("Device: {}", device.get_name());
    println!("Is low power: {}", device.is_low_power());
    println!("Is headless: {}", device.is_headless());
}
<commit_msg>Print out result of `Device::is_depth24_stencil8_pixel_format_supported`<commit_after>//! A simple example which prints out information about available Metal devices

extern crate mtl;

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {
    println!("Metal is not supported on this platform");
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    let device = match mtl::Device::system_default_device() {
        Ok(device) => device,
        Err(e) => {
            use std::error::Error;
            println!("{}", e.description());
            return;
        }
    };

    println!("Device: {}", device.get_name());
    println!("Supports 24 bit stencil, 8 bit depth: {}", device.is_depth24_stencil8_pixel_format_supported());
    println!("Is low power: {}", device.is_low_power());
    println!("Is headless: {}", device.is_headless());
}
<|endoftext|>


<commit_before>//! A simple example which prints out information about available Metal devices

extern crate mtl;

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {
    println!("Metal is not supported on this platform");
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    use mtl::ReadOnlyDevice;
    let device = match mtl::Device::system_default_device() {
        Ok(device) => device,
        Err(e) => {
            use std::error::Error;
            println!("{}", e.description());
            return;
        }
    };

    println!("Device: {}", device.get_name());
    println!("Supports 24 bit stencil, 8 bit depth: {}", device.is_depth24_stencil8_pixel_format_supported());
    println!("Is low power: {}", device.is_low_power());
    println!("Is headless: {}", device.is_headless());
}
<commit_msg>Output formatting and wording changes<commit_after>//! A simple example which prints out information about available Metal devices

extern crate mtl;

#[cfg(not(any(target_os = "macos", target_os = "ios")))]
fn main() {
    println!("Metal is not supported on this platform");
}

#[cfg(any(target_os = "macos", target_os = "ios"))]
fn main() {
    use mtl::ReadOnlyDevice;
    let device = match mtl::Device::system_default_device() {
        Ok(device) => device,
        Err(e) => {
            use std::error::Error;
            println!("{}", e.description());
            return;
        }
    };

    println!("Device:\t\t\t\t\t\t{}", device.get_name());
    println!("Supports 24 bit stencil and 8 bit depth:\t{}", device.is_depth24_stencil8_pixel_format_supported());
    println!("Is low power:\t\t\t\t\t{}", device.is_low_power());
    println!("Is headless:\t\t\t\t\t{}", device.is_headless());
}
<|endoftext|>


<commit_before>extern crate reqwest;
extern crate env_logger;

use std::env;

fn main() {
    env_logger::init().unwrap();

    let url = match env::args().nth(1) {
        Some(url) => url,
        None => {
            println!("Usage: reqwest <url>");
            return;
        }
    };

    let res = reqwest::get(&url).unwrap();

    println!("Status: {}", res.status());
    println!("Headers:\n{}", res.headers());
}
<commit_msg>Add --url option as command line argument<commit_after>extern crate reqwest;
extern crate env_logger;

use std::env;

pub struct Opt {
    url: Option<String>,
}

impl Opt {
    pub fn new() -> Opt {
        Opt {
            url: None,
        }
    }

    pub fn url(&mut self, url: String) {
        self.url = Some(url);
    }

    pub fn is_valid(&self) -> bool {
        return self.url.is_some();
    }
}

fn main() {
    env_logger::init().unwrap();

    let mut arguments = env::args();
    if arguments.len() < 2 {
        println!("reqwest: try 'reqwest --help' for more information");
        return;
    }
    arguments.next(); // execute name

    let mut opt = Opt::new();
    while let Some(arg) = arguments.next() {
        match arg.as_str() {
            "--url" => {
                match arguments.next() {
                    Some(url) => {
                        opt.url(url);
                    },
                    None => {
                        println!("reqwest: option --url: requires parameter");
                        println!("reqwest: try 'reqwest --help' for more information");
                        return;
                    }
                }
            },
            "--help" => {
                help();
                return;
            },
            url => {
                opt.url(url.to_string());
            }
        }
    }

    if !opt.is_valid() {
        help();
        return;
    }

    let res = reqwest::get(&opt.url.unwrap()).unwrap();

    println!("Status: {}", res.status());
    println!("Headers:\n{}", res.headers());
}

fn help() {
    println!("Usage: reqwest [options...] <url>");
    println!("  --url <url>");
}
<|endoftext|>


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

fn emit_greyscale(scale: i32) {
    let size: usize = 2*(scale as usize) + 1;
    let mut output = File::create("test.pgm").unwrap();
    // So apparently the size of arrays in rust have to be a compile time
    // constant at the moment. Hardcode the value because whatever.
    let mut buf: [u8; 301] = [0; 301];
    write!(output, "P5 {} {} 255\n", size, size).unwrap();
    for xi in -scale .. (scale + 1) {
        for yi in -scale .. (scale + 1) {
            let x = xi as f32 / scale as f32;
            let y = yi as f32 / scale as f32;
            let intensity = (127.5 + 127.5*(x + y)/2.0) as u8;
            if xi == 0 {
                println!("Writing index {}", yi + scale);
            }
            buf[(yi + scale) as usize] = intensity;
        }
        output.write(&buf).unwrap();
    }
}
fn main() {
    emit_greyscale(150);
}
<commit_msg>No longer allocate my own buffer.<commit_after>use std::io::prelude::*;
use std::fs::File;
use std::io::BufWriter;

fn emit_greyscale(scale: i32) {
    let size: usize = 2*(scale as usize) + 1;
    let mut output = BufWriter::new(File::create("test.pgm").unwrap());
    // So apparently the size of arrays in rust have to be a compile time
    // constant at the moment. Hardcode the value because whatever.
    write!(output, "P5 {} {} 255\n", size, size).unwrap();
    for xi in -scale .. (scale + 1) {
        for yi in -scale .. (scale + 1) {
            let x = xi as f32 / scale as f32;
            let y = yi as f32 / scale as f32;
            let intensity = (127.5 + 127.5*(x + y)/2.0) as u8;
            let buf: [u8; 1] = [intensity];
            output.write(&buf).unwrap();
        }
    }
}
fn main() {
    emit_greyscale(150);
}
<|endoftext|>


<commit_before>// check-pass

// Repeating a *constant* of non-Copy type (not just a constant expression) is already stable.

const EMPTY: Vec<i32> = Vec::new();

pub fn bar() -> [Vec<i32>; 2] {
    [EMPTY; 2]
}

fn main() {
    let x = bar();
}
<commit_msg>make sure [CONST; N] drops N times<commit_after>// run-pass

// Repeating a *constant* of non-Copy type (not just a constant expression) is already stable.

const EMPTY: Vec<i32> = Vec::new();

pub fn bar() -> [Vec<i32>; 2] {
    [EMPTY; 2]
}

struct Bomb;

impl Drop for Bomb {
    fn drop(&mut self) {
        panic!("BOOM!");
    }
}

const BOOM: Bomb = Bomb;

fn main() {
    let _x = bar();

    // Make sure the destructor does not get called for empty arrays. `[CONST; N]` should
    // instantiate (and then later drop) the const exactly `N` times.
    let _x = [BOOM; 0];
}
<|endoftext|>


<commit_before>extern crate mysql;
extern crate regex;

use mysql::conn::pool::MyPool;
use mysql::conn::pool::MyPooledConn;
use mysql::conn::Stmt;
use mysql::conn::QueryResult;
use mysql::error::MyError;

use std::process::{Command, Stdio};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::process::Child;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

pub struct DownloadDB {
	pub url: String,
	pub quality: i16,
	pub qid: i64,
	pub folder_format: String,
	pub pool: MyPool,
	pub download_limit: i16,
}

pub struct Downloader {
	ddb: DownloadDB,
	// pool: MyPool,
}

#[derive(Debug)]
pub enum DownloadError{
    ConsoleError(String),
    ReadError,
    DMCAError,
    InternalError,
    DBError(String),
}

impl From<MyError> for DownloadError {
	fn from(err: MyError) -> DownloadError {
		DownloadError::DBError(err.description().into())
	}
}

impl Downloader {
	pub fn new(ddb: DownloadDB) -> Downloader{
		Downloader {ddb: ddb}
	}

	///Regex: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
	///
	///Downloads a video, updates the DB
	///TODO: get the sql statements out of the class
	///TODO: wrap errors
	///Doesn't care about DMCAs, will emit errors on them
	pub fn download_video(&self) -> Result<bool,DownloadError> {
	    println!("{:?}", self.ddb.url);
	    let process = try!(self.run_ytdl_process());
	    let stdout = BufReader::new(process.stdout.unwrap());

	    let mut conn = self.ddb.pool.get_conn().unwrap();
	    let mut statement = self.prepare_progress_updater(&mut conn);
	    let mut conn = self.ddb.pool.get_conn().unwrap();
	    let re = regex!(r"\d+\.\d%");

	    try!(self.set_query_code(&mut conn, &1));

	    for line in &mut stdout.lines(){
	        match line{
	            Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
	            Ok(text) => {
	                    println!("Out: {}",text);
	                    match re.find(&text) {
	                        Some(s) => { println!("Match at {}", s.0);
	                                    println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
	                                    self.update_progress(&mut statement, &text[s.0..s.1].to_string());
	                                },
	                        None => println!("Detected no % match."),
	                    }
	                    //if re.is_match(&text) {println!("Match: {:?}", text);}
	                },
	        }
	    }

	    self.update_progress(&mut statement, &"Finished".to_string());
	    try!(self.set_query_code(&mut conn, &3));

	    Ok(true)
	}

	fn run_ytdl_process(&self) -> Result<Child,DownloadError> {
		match Command::new("youtube-dl")
	                                .arg("--newline")
	                                .arg(format!("-r {}M",self.ddb.download_limit))
	                                .arg(format!("-o {}",self.ddb.folder_format))
	                                .arg(&self.ddb.url)
	                                .stdin(Stdio::null())
	                                .stdout(Stdio::piped())
	                                .spawn() {
	        Err(why) => Err(DownloadError::ConsoleError(Error::description(&why).into())),
	        Ok(process) => Ok(process),
	    }
	}

	// fn create_regex(expression: & str) -> regex::Regex {
	//     match regex::Regex::new(expression) {
	//         Ok(re) => re,
	//         Err(err) => panic!("regex {}", err),
	//     }
	// }

	// MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
	fn prepare_progress_updater<'a>(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
	    conn.prepare("UPDATE querydetails SET status = ? WHERE qid = ?").unwrap()
	}

	fn set_query_code(&self,conn: & mut MyPooledConn, code: &i8) -> Result<(), DownloadError> { // same here
	    let mut stmt = conn.prepare("UPDATE querydetails SET code = ? WHERE qid = ?").unwrap();
	    let result = stmt.execute(&[code,&self.ddb.qid]);
	    match result {
	    	Ok(_) => Ok(()),
	    	Err(why) => Err(DownloadError::DBError(why.description().into())),
	    }

	}

	///updater called from the stdout progress
	fn update_progress(&self,stmt: &mut Stmt, progress: &String){
	    stmt.execute(&[progress,&self.ddb.qid]);
	}
}<commit_msg>+url_encode thanks to scott for the rust-idiomatic code<commit_after>extern crate mysql;
extern crate regex;

use mysql::conn::pool::MyPool;
use mysql::conn::pool::MyPooledConn;
use mysql::conn::Stmt;
use mysql::conn::QueryResult;
use mysql::error::MyError;

use std::process::{Command, Stdio};
use std::error::Error;
use std::io::prelude::*;
use std::io::BufReader;
use std::process::Child;

macro_rules! regex(
    ($s:expr) => (regex::Regex::new($s).unwrap());
);

pub struct DownloadDB {
	pub url: String,
	pub quality: i16,
	pub qid: i64,
	pub folder_format: String,
	pub pool: MyPool,
	pub download_limit: i16,
}

pub struct Downloader {
	ddb: DownloadDB,
	// pool: MyPool,
}

#[derive(Debug)]
pub enum DownloadError{
    ConsoleError(String),
    ReadError,
    DMCAError,
    InternalError,
    DBError(String),
}

impl From<MyError> for DownloadError {
	fn from(err: MyError) -> DownloadError {
		DownloadError::DBError(err.description().into())
	}
}

impl Downloader {
	pub fn new(ddb: DownloadDB) -> Downloader{
		Downloader {ddb: ddb}
	}

	///Regex: [download]  13.4% of 275.27MiB at 525.36KiB/s ETA 07:52
	///
	///Downloads a video, updates the DB
	///TODO: get the sql statements out of the class
	///TODO: wrap errors
	///Doesn't care about DMCAs, will emit errors on them
	pub fn download_video(&self) -> Result<bool,DownloadError> {
	    println!("{:?}", self.ddb.url);
	    let process = try!(self.run_ytdl_process());
	    let stdout = BufReader::new(process.stdout.unwrap());

	    let mut conn = self.ddb.pool.get_conn().unwrap();
	    let mut statement = self.prepare_progress_updater(&mut conn);
	    let mut conn = self.ddb.pool.get_conn().unwrap();
	    let re = regex!(r"\d+\.\d%");

	    try!(self.set_query_code(&mut conn, &1));

	    for line in &mut stdout.lines(){
	        match line{
	            Err(why) => panic!("couldn't read cmd stdout: {}", Error::description(&why)),
	            Ok(text) => {
	                    println!("Out: {}",text);
	                    match re.find(&text) {
	                        Some(s) => { println!("Match at {}", s.0);
	                                    println!("{}", &text[s.0..s.1]); // ONLY with ASCII chars makeable!
	                                    self.update_progress(&mut statement, &text[s.0..s.1].to_string());
	                                },
	                        None => println!("Detected no % match."),
	                    }
	                    //if re.is_match(&text) {println!("Match: {:?}", text);}
	                },
	        }
	    }

	    self.update_progress(&mut statement, &"Finished".to_string());
	    try!(self.set_query_code(&mut conn, &3));

	    Ok(true)
	}

	pub fn url_encode(input: &str) -> String {
		// iterator over input, apply function to each element(function
		input.chars().map(|char| {
	        match char {
	            ' ' => '_',
	            '&' => '-',
	            c if c.is_ascii() => c,
	            _ => '_'
	        }
    	}).collect()
    	// match for each char, then do collect: loop through the iterator, collect all elements
    	// into container FromIterator
	}

	fn run_ytdl_process(&self) -> Result<Child,DownloadError> {
		match Command::new("youtube-dl")
	                                .arg("--newline")
	                                .arg(format!("-r {}M",self.ddb.download_limit))
	                                .arg(format!("-o {}",self.ddb.folder_format))
	                                .arg(&self.ddb.url)
	                                .stdin(Stdio::null())
	                                .stdout(Stdio::piped())
	                                .spawn() {
	        Err(why) => Err(DownloadError::ConsoleError(Error::description(&why).into())),
	        Ok(process) => Ok(process),
	    }
	}

	// fn create_regex(expression: & str) -> regex::Regex {
	//     match regex::Regex::new(expression) {
	//         Ok(re) => re,
	//         Err(err) => panic!("regex {}", err),
	//     }
	// }

	// MyPooledConn does only live when MyOpts is alive -> lifetime needs to be declared
	fn prepare_progress_updater<'a>(&'a self,conn: &'a mut MyPooledConn) -> Stmt<'a> { // no livetime needed: struct livetime used
	    conn.prepare("UPDATE querydetails SET status = ? WHERE qid = ?").unwrap()
	}

	fn set_query_code(&self,conn: & mut MyPooledConn, code: &i8) -> Result<(), DownloadError> { // same here
	    let mut stmt = conn.prepare("UPDATE querydetails SET code = ? WHERE qid = ?").unwrap();
	    let result = stmt.execute(&[code,&self.ddb.qid]); // why is this var needed ?!
	    match result {
	    	Ok(_) => Ok(()),
	    	Err(why) => Err(DownloadError::DBError(why.description().into())),
	    }

	}

	///updater called from the stdout progress
	fn update_progress(&self,stmt: &mut Stmt, progress: &String){
	    stmt.execute(&[progress,&self.ddb.qid]);
	}
}<|endoftext|>