<commit_before>#[macro_use]
extern crate cfg_if;
extern crate gcc;

use std::cmp::Ordering;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
use std::process::{self, Child, Command, Stdio};
use std::str;

cfg_if! {
    if #[cfg(feature = "v1_8_0")] {
        const TARGET_VERSION: &'static str = "1.8.0";
    } else if #[cfg(feature = "v1_7_1")] {
        const TARGET_VERSION: &'static str = "1.7.1";
    } else if #[cfg(feature = "v1_7_0")] {
        const TARGET_VERSION: &'static str = "1.7.0";
    } else if #[cfg(feature = "v1_6_0")] {
        const TARGET_VERSION: &'static str = "1.6.0";
    } else if #[cfg(feature = "v1_5_1")] {
        const TARGET_VERSION: &'static str = "1.5.1";
    } else if #[cfg(feature = "v1_5_0")] {
        const TARGET_VERSION: &'static str = "1.5.0";
    } else if #[cfg(feature = "v1_4_3")] {
        const TARGET_VERSION: &'static str = "1.4.3";
    } else if #[cfg(feature = "v1_4_2")] {
        const TARGET_VERSION: &'static str = "1.4.2";
    } else if #[cfg(feature = "v1_4_0")] {
        const TARGET_VERSION: &'static str = "1.4.0";
    } else if #[cfg(feature = "v1_3_1")] {
        const TARGET_VERSION: &'static str = "1.3.1";
    } else if #[cfg(feature = "v1_3_0")] {
        const TARGET_VERSION: &'static str = "1.3.0";
    } else {
        const TARGET_VERSION: &'static str = "1.2.0";
    }
}

fn main() {
    if let Ok(lib) = env::var("GPGME_LIB") {
        let mode = match env::var_os("GPGME_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };
        println!("cargo:rustc-link-lib={0}={1}", mode, lib);
        return;
    } else if let Some(path) = env::var_os("GPGME_CONFIG") {
        if !try_config(path) {
            process::exit(1);
        }
        return;
    }

    if !Path::new("libassuan/.git").exists() || !Path::new("gpgme/.git").exists() {
        run(Command::new("git").args(&["submodule", "update", "--init"]));
    }

    if try_build() || try_config("gpgme-config") {
        return;
    }
    process::exit(1);
}

fn try_config<S: AsRef<OsStr>>(path: S) -> bool {
    let path = path.as_ref();

    if let Some(output) = output(Command::new(&path).arg("--version")) {
        test_version(&output);
    } else {
        return false;
    }

    if let Some(output) = output(Command::new(&path).arg("--prefix")) {
        println!("cargo:root={}", output);
    }

    let mut command = Command::new(&path);
    if cfg!(unix) {
        command.arg("--thread=pthread");
    }
    command.arg("--libs");
    if let Some(output) = output(&mut command) {
        parse_config_output(&output);
        return true;
    }
    false
}

fn parse_config_output(output: &str) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| if p.len() > 2 {
        Some(p.split_at(2))
    } else {
        None
    });

    for (flag, val) in parts {
        match flag {
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            }
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            }
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            }
            _ => {}
        }
    }
}

fn try_build() -> bool {
    let src = PathBuf::from(env::current_dir().unwrap()).join("libassuan");
    let dst = env::var("OUT_DIR").unwrap();
    let build = PathBuf::from(&dst).join("build/assuan");
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let gpgerror_root = env::var("DEP_GPG_ERROR_ROOT").unwrap();
    let compiler = gcc::Config::new().get_compiler();
    let cflags = compiler.args().iter().fold(OsString::new(), |mut c, a| {
        c.push(a);
        c.push(" ");
        c
    });

    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(src.join("configure"))
        .args(&["--build", &host,
                "--host", &target,
                "--enable-static",
                "--disable-shared",
                "--disable-doc",
                &format!("--with-libgpg-error-prefix={}", &gpgerror_root),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("install")) {
        return false;
    }

    let src = PathBuf::from(env::current_dir().unwrap()).join("gpgme");
    let build = PathBuf::from(&dst).join("build/gpgme");
    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(src.join("configure"))
        .args(&["--build", &host,
                "--host", &target,
                "--enable-static",
                "--disable-shared",
                "--disable-languages",
                "--disable-doc",
                &format!("--with-libgpg-error-prefix={}", &gpgerror_root),
                &format!("--with-libassuan-prefix={}", &dst),
                &format!("--prefix={}", &dst)])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make").current_dir(&build).arg("install")) {
        return false;
    }

    println!("cargo:rustc-link-search=native={}",
             PathBuf::from(dst.clone()).join("lib").display());
    println!("cargo:rustc-link-lib=static=assuan");
    println!("cargo:rustc-link-lib=static=gpgme");
    println!("cargo:root={}", &dst);
    true
}

fn test_version(version: &str) {
    let version = version.trim();
    for (x, y) in TARGET_VERSION.split('.').zip(version.split('.').chain(iter::repeat("0"))) {
        let (x, y): (u8, u8) = (x.parse().unwrap(), y.parse().unwrap());
        match x.cmp(&y) {
            Ordering::Less => break,
            Ordering::Greater => {
                panic!("GPGME version `{}` is less than requested `{}`",
                       version,
                       TARGET_VERSION)
            }
            _ => (),
        }
    }
}

fn spawn(cmd: &mut Command) -> Option<Child> {
    println!("running: {:?}", cmd);
    match cmd.stdin(Stdio::null()).spawn() {
        Ok(child) => Some(child),
        Err(e) => {
            println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            None
        }
    }
}

fn run(cmd: &mut Command) -> bool {
    if let Some(mut child) = spawn(cmd) {
        match child.wait() {
            Ok(status) => {
                if !status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status);
                } else {
                    return true;
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    false
}

fn output(cmd: &mut Command) -> Option<String> {
    if let Some(child) = spawn(cmd.stdout(Stdio::piped())) {
        match child.wait_with_output() {
            Ok(output) => {
                if !output.status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, output.status);
                } else {
                    return String::from_utf8(output.stdout).ok();
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    None
}
<commit_msg>Make build.rs msys compatible<commit_after>#[macro_use]
extern crate cfg_if;
extern crate gcc;

use std::cmp::Ordering;
use std::env;
use std::ffi::{OsStr, OsString};
use std::fs;
use std::iter;
use std::path::{Path, PathBuf};
use std::process::{self, Child, Command, Stdio};
use std::str;

cfg_if! {
    if #[cfg(feature = "v1_8_0")] {
        const TARGET_VERSION: &'static str = "1.8.0";
    } else if #[cfg(feature = "v1_7_1")] {
        const TARGET_VERSION: &'static str = "1.7.1";
    } else if #[cfg(feature = "v1_7_0")] {
        const TARGET_VERSION: &'static str = "1.7.0";
    } else if #[cfg(feature = "v1_6_0")] {
        const TARGET_VERSION: &'static str = "1.6.0";
    } else if #[cfg(feature = "v1_5_1")] {
        const TARGET_VERSION: &'static str = "1.5.1";
    } else if #[cfg(feature = "v1_5_0")] {
        const TARGET_VERSION: &'static str = "1.5.0";
    } else if #[cfg(feature = "v1_4_3")] {
        const TARGET_VERSION: &'static str = "1.4.3";
    } else if #[cfg(feature = "v1_4_2")] {
        const TARGET_VERSION: &'static str = "1.4.2";
    } else if #[cfg(feature = "v1_4_0")] {
        const TARGET_VERSION: &'static str = "1.4.0";
    } else if #[cfg(feature = "v1_3_1")] {
        const TARGET_VERSION: &'static str = "1.3.1";
    } else if #[cfg(feature = "v1_3_0")] {
        const TARGET_VERSION: &'static str = "1.3.0";
    } else {
        const TARGET_VERSION: &'static str = "1.2.0";
    }
}

fn main() {
    if let Ok(lib) = env::var("GPGME_LIB") {
        let mode = match env::var_os("GPGME_STATIC") {
            Some(_) => "static",
            _ => "dylib",
        };
        println!("cargo:rustc-link-lib={0}={1}", mode, lib);
        return;
    } else if let Some(path) = env::var_os("GPGME_CONFIG") {
        if !try_config(path) {
            process::exit(1);
        }
        return;
    }

    if !Path::new("libassuan/.git").exists() || !Path::new("gpgme/.git").exists() {
        run(Command::new("git").args(&["submodule", "update", "--init"]));
    }

    if try_build() || try_config("gpgme-config") {
        return;
    }
    process::exit(1);
}

fn try_config<S: AsRef<OsStr>>(path: S) -> bool {
    let path = path.as_ref();

    if let Some(output) = output(Command::new(&path).arg("--version")) {
        test_version(&output);
    } else {
        return false;
    }

    if let Some(output) = output(Command::new(&path).arg("--prefix")) {
        println!("cargo:root={}", output);
    }

    let mut command = Command::new(&path);
    if cfg!(unix) {
        command.arg("--thread=pthread");
    }
    command.arg("--libs");
    if let Some(output) = output(&mut command) {
        parse_config_output(&output);
        return true;
    }
    false
}

fn parse_config_output(output: &str) {
    let parts = output.split(|c: char| c.is_whitespace()).filter_map(|p| if p.len() > 2 {
        Some(p.split_at(2))
    } else {
        None
    });

    for (flag, val) in parts {
        match flag {
            "-L" => {
                println!("cargo:rustc-link-search=native={}", val);
            }
            "-F" => {
                println!("cargo:rustc-link-search=framework={}", val);
            }
            "-l" => {
                println!("cargo:rustc-link-lib={}", val);
            }
            _ => {}
        }
    }
}

fn try_build() -> bool {
    let src = PathBuf::from(env::current_dir().unwrap()).join("libassuan");
    let dst = env::var("OUT_DIR").unwrap();
    let build = PathBuf::from(&dst).join("build/assuan");
    let target = env::var("TARGET").unwrap();
    let host = env::var("HOST").unwrap();
    let gpgerror_root = env::var("DEP_GPG_ERROR_ROOT").unwrap();
    let compiler = gcc::Config::new().get_compiler();
    let cflags = compiler.args().iter().fold(OsString::new(), |mut c, a| {
        c.push(a);
        c.push(" ");
        c
    });

    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(msys_compatible(src.join("configure")))
        .args(&["--build", gnu_target(&host),
                "--host", gnu_target(&target),
                "--enable-static",
                "--disable-shared",
                "--disable-doc",
                &format!("--with-libgpg-error-prefix={}", msys_compatible(&gpgerror_root)),
                &format!("--prefix={}", msys_compatible(&dst))])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("install")) {
        return false;
    }

    let src = PathBuf::from(env::current_dir().unwrap()).join("gpgme");
    let build = PathBuf::from(&dst).join("build/gpgme");
    let _ = fs::create_dir_all(&build);

    if !run(Command::new("sh").current_dir(&src).arg("autogen.sh")) {
        return false;
    }
    if !run(Command::new("sh")
        .current_dir(&build)
        .env("CC", compiler.path())
        .env("CFLAGS", &cflags)
        .arg(msys_compatible(src.join("configure")))
        .args(&["--build", gnu_target(&host),
                "--host", gnu_target(&target),
                "--enable-static",
                "--disable-shared",
                "--disable-languages",
                "--disable-doc",
                &format!("--with-libgpg-error-prefix={}", msys_compatible(&gpgerror_root)),
                &format!("--with-libassuan-prefix={}", msys_compatible(&dst)),
                &format!("--prefix={}", msys_compatible(&dst))])) {
        return false;
    }
    if !run(Command::new("make")
        .current_dir(&build)
        .arg("-j").arg(env::var("NUM_JOBS").unwrap())) {
        return false;
    }
    if !run(Command::new("make").current_dir(&build).arg("install")) {
        return false;
    }

    println!("cargo:rustc-link-search=native={}",
             PathBuf::from(dst.clone()).join("lib").display());
    println!("cargo:rustc-link-lib=static=assuan");
    println!("cargo:rustc-link-lib=static=gpgme");
    println!("cargo:root={}", &dst);
    true
}

fn test_version(version: &str) {
    let version = version.trim();
    for (x, y) in TARGET_VERSION.split('.').zip(version.split('.').chain(iter::repeat("0"))) {
        let (x, y): (u8, u8) = (x.parse().unwrap(), y.parse().unwrap());
        match x.cmp(&y) {
            Ordering::Less => break,
            Ordering::Greater => {
                panic!("GPGME version `{}` is less than requested `{}`",
                       version,
                       TARGET_VERSION)
            }
            _ => (),
        }
    }
}

fn spawn(cmd: &mut Command) -> Option<Child> {
    println!("running: {:?}", cmd);
    match cmd.stdin(Stdio::null()).spawn() {
        Ok(child) => Some(child),
        Err(e) => {
            println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            None
        }
    }
}

fn run(cmd: &mut Command) -> bool {
    if let Some(mut child) = spawn(cmd) {
        match child.wait() {
            Ok(status) => {
                if !status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, status);
                } else {
                    return true;
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    false
}

fn output(cmd: &mut Command) -> Option<String> {
    if let Some(child) = spawn(cmd.stdout(Stdio::piped())) {
        match child.wait_with_output() {
            Ok(output) => {
                if !output.status.success() {
                    println!("command did not execute successfully: {:?}\n\
                       expected success, got: {}", cmd, output.status);
                } else {
                    return String::from_utf8(output.stdout).ok();
                }
            }
            Err(e) => {
                println!("failed to execute command: {:?}\nerror: {}", cmd, e);
            }
        }
    }
    None
}

fn msys_compatible<P: AsRef<Path>>(path: P) -> String {
    let path = path.as_ref().to_str().unwrap();
    if !cfg!(windows) {
        return path.to_string();
    }
    path.replace("C:\\", "/c/").replace("\\", "/")
}

fn gnu_target(target: &str) -> &str {
    match target {
        "i686-pc-windows-gnu" => "i686-w64-mingw32",
        "x86_64-pc-windows-gnu" => "x86_64-w64-mingw32",
        s => s,
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod create_table_query {
    use expectest::prelude::be_ok;

    use sql::lexer::tokenize;
    use sql::parser::parse;
    use sql::query_typer::type_inferring;
    use sql::ast::{TypedStatement, Type};
    use sql::ast::create_table::{CreateTableQuery, ColumnTable};
    use sql::catalog_manager::LockBasedCatalogManager;

    #[test]
    fn default_size_for_char_should_be_255() {
        let catalog_manager = LockBasedCatalogManager::default();

        expect!(
            tokenize("create table tab1 (col1 char);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Create(
                    CreateTableQuery::new(
                        "tab1",
                        vec![ColumnTable::new("col1", Type::Character(Option::from(255)), false, None, true, None)]
                    )
                )
            )
        );
    }

    #[test]
    fn list_of_columns_with_default_char_size() {
        let catalog_manager = LockBasedCatalogManager::default();

        expect!(
            tokenize("create table tab1 (col1 char, col2 char, col3 char);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Create(
                    CreateTableQuery::new(
                        "tab1",
                        vec![
                            ColumnTable::new("col1", Type::Character(Option::from(255)), false, None, true, None),
                            ColumnTable::new("col2", Type::Character(Option::from(255)), false, None, true, None),
                            ColumnTable::new("col3", Type::Character(Option::from(255)), false, None, true, None)
                        ]
                    )
                )
            )
        );
    }
}

#[cfg(test)]
mod insert_query {
    use expectest::prelude::be_ok;

    use sql::lexer::tokenize;
    use sql::parser::parse;
    use sql::query_typer::type_inferring;
    use sql::ast::{Type, TypedStatement};
    use sql::ast::insert_query::{InsertQuery, Value, ValueSource};
    use sql::catalog_manager::LockBasedCatalogManager;

    #[test]
    fn populates_columns_for_insert_query() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table2");
        catalog_manager.add_column_to("table2", ("col1", Type::Integer, None));
        catalog_manager.add_column_to("table2", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table2", ("col3", Type::Integer, None));

        expect!(
            tokenize("insert into table2 values (1, 2, 3);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Insert(
                    InsertQuery::new(
                        "table2",
                        vec!["col1", "col2", "col3"],
                        ValueSource::Row(vec![Value::num("1"), Value::num("2"), Value::num("3")])
                    )
                )
            )
        );
    }

    #[test]
    fn populates_only_missed_column() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_1");
        catalog_manager.add_column_to("table_1", ("col1", Type::Integer, Some("1")));
        catalog_manager.add_column_to("table_1", ("col2", Type::Integer, None));

        expect!(
            tokenize("insert into table_1 (col2) values (2);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Insert(
                    InsertQuery::new(
                        "table_1",
                        vec!["col2", "col1"],
                        ValueSource::Row(vec![Value::num("2"), Value::num("1")])
                    )
                )
            )
        );
    }

    #[test]
    fn populates_default_value_for_different_types() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_2");
        catalog_manager.add_column_to("table_2", ("col1", Type::Integer, Some("1")));
        catalog_manager.add_column_to("table_2", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table_2", ("col3", Type::Character(Option::from(3)), Some("str")));

        expect!(
            tokenize("insert into table_2 (col2) values (2);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Insert(
                    InsertQuery::new(
                        "table_2",
                        vec!["col2", "col1", "col3"],
                        ValueSource::Row(vec![Value::num("2"), Value::num("1"), Value::str("str")])
                    )
                )
            )
        );
    }
}

#[cfg(test)]
mod select_query {
    use expectest::prelude::be_ok;

    use sql::catalog_manager::LockBasedCatalogManager;

    use sql::lexer::tokenize;
    use sql::parser::parse;
    use sql::query_typer::type_inferring;

    use sql::ast::{Type, TypedStatement};
    use sql::ast::select_query::TypedSelectQuery;

    #[test]
    fn single_column_query() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_1");
        catalog_manager.add_column_to("table_1", ("col1", Type::Integer, None));

        expect!(
            tokenize("select col1 from table_1;")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Select(
                    TypedSelectQuery::new("table_1", vec![("col1", Type::Integer)], None)
                )
            )
        );
    }

    #[test]
    fn multiple_columns_query() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_3");
        catalog_manager.add_column_to("table_3", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table_3", ("col3", Type::Character(Some(10)), None));
        catalog_manager.add_column_to("table_3", ("col5", Type::Integer, None));

        expect!(
            tokenize("select col2, col3, col5 from table_3;")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Select(
                    TypedSelectQuery::new(
                        "table_3",
                        vec![
                            ("col2", Type::Integer),
                            ("col3", Type::Character(Some(10))),
                            ("col5", Type::Integer)
                        ],
                        None
                    )
                )
            )
        );
    }

    #[test]
    fn not_all_columns() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_2");
        catalog_manager.add_column_to("table_2", ("col1", Type::Integer, None));
        catalog_manager.add_column_to("table_2", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table_2", ("col3", Type::Integer, None));

        expect!(
            tokenize("select col1, col3 from table_2;")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Select(
                    TypedSelectQuery::new(
                        "table_2",
                        vec![
                            ("col1", Type::Integer),
                            ("col3", Type::Integer)
                        ],
                        None
                    )
                )
            )
        );
    }
}
<commit_msg>insert select query test<commit_after>#[cfg(test)]
mod create_table_query {
    use expectest::prelude::be_ok;

    use sql::lexer::tokenize;
    use sql::parser::parse;
    use sql::query_typer::type_inferring;
    use sql::ast::{TypedStatement, Type};
    use sql::ast::create_table::{CreateTableQuery, ColumnTable};
    use sql::catalog_manager::LockBasedCatalogManager;

    #[test]
    fn default_size_for_char_should_be_255() {
        let catalog_manager = LockBasedCatalogManager::default();

        expect!(
            tokenize("create table tab1 (col1 char);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Create(
                    CreateTableQuery::new(
                        "tab1",
                        vec![ColumnTable::new("col1", Type::Character(Option::from(255)), false, None, true, None)]
                    )
                )
            )
        );
    }

    #[test]
    fn list_of_columns_with_default_char_size() {
        let catalog_manager = LockBasedCatalogManager::default();

        expect!(
            tokenize("create table tab1 (col1 char, col2 char, col3 char);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Create(
                    CreateTableQuery::new(
                        "tab1",
                        vec![
                            ColumnTable::new("col1", Type::Character(Option::from(255)), false, None, true, None),
                            ColumnTable::new("col2", Type::Character(Option::from(255)), false, None, true, None),
                            ColumnTable::new("col3", Type::Character(Option::from(255)), false, None, true, None)
                        ]
                    )
                )
            )
        );
    }
}

#[cfg(test)]
mod insert_query {
    use expectest::prelude::be_ok;

    use sql::lexer::tokenize;
    use sql::parser::parse;
    use sql::query_typer::type_inferring;
    use sql::ast::{Type, TypedStatement};
    use sql::ast::insert_query::{InsertQuery, Value, ValueSource};
    use sql::catalog_manager::LockBasedCatalogManager;

    #[test]
    fn populates_columns_for_insert_query() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table2");
        catalog_manager.add_column_to("table2", ("col1", Type::Integer, None));
        catalog_manager.add_column_to("table2", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table2", ("col3", Type::Integer, None));

        expect!(
            tokenize("insert into table2 values (1, 2, 3);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Insert(
                    InsertQuery::new(
                        "table2",
                        vec!["col1", "col2", "col3"],
                        ValueSource::Row(vec![Value::num("1"), Value::num("2"), Value::num("3")])
                    )
                )
            )
        );
    }

    #[test]
    fn populates_only_missed_column() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_1");
        catalog_manager.add_column_to("table_1", ("col1", Type::Integer, Some("1")));
        catalog_manager.add_column_to("table_1", ("col2", Type::Integer, None));

        expect!(
            tokenize("insert into table_1 (col2) values (2);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Insert(
                    InsertQuery::new(
                        "table_1",
                        vec!["col2", "col1"],
                        ValueSource::Row(vec![Value::num("2"), Value::num("1")])
                    )
                )
            )
        );
    }

    #[test]
    fn populates_default_value_for_different_types() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_2");
        catalog_manager.add_column_to("table_2", ("col1", Type::Integer, Some("1")));
        catalog_manager.add_column_to("table_2", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table_2", ("col3", Type::Character(Option::from(3)), Some("str")));

        expect!(
            tokenize("insert into table_2 (col2) values (2);")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Insert(
                    InsertQuery::new(
                        "table_2",
                        vec!["col2", "col1", "col3"],
                        ValueSource::Row(vec![Value::num("2"), Value::num("1"), Value::str("str")])
                    )
                )
            )
        );
    }

    #[test]
    #[ignore]
    fn populates_types_of_columns_in_select_sub_query() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_1");
        catalog_manager.add_column_to("table_1", ("col1", Type::Integer, Some("1")));
        catalog_manager.add_column_to("table_1", ("col2", Type::Integer, Some("2")));

        /*
        expect!(
            tokenize("insert into table_1 (col1, col2) select col1, col2 from table_1;")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Insert(
                    InsertQuery::new(
                        "table_1",
                        vec!["col1", "col2"],
                        ValueSource::SubQuery()
                    )
                )
            )
        );
        */
    }
}

#[cfg(test)]
mod select_query {
    use expectest::prelude::be_ok;

    use sql::catalog_manager::LockBasedCatalogManager;

    use sql::lexer::tokenize;
    use sql::parser::parse;
    use sql::query_typer::type_inferring;

    use sql::ast::{Type, TypedStatement};
    use sql::ast::select_query::TypedSelectQuery;

    #[test]
    fn single_column_query() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_1");
        catalog_manager.add_column_to("table_1", ("col1", Type::Integer, None));

        expect!(
            tokenize("select col1 from table_1;")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Select(
                    TypedSelectQuery::new("table_1", vec![("col1", Type::Integer)], None)
                )
            )
        );
    }

    #[test]
    fn multiple_columns_query() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_3");
        catalog_manager.add_column_to("table_3", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table_3", ("col3", Type::Character(Some(10)), None));
        catalog_manager.add_column_to("table_3", ("col5", Type::Integer, None));

        expect!(
            tokenize("select col2, col3, col5 from table_3;")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Select(
                    TypedSelectQuery::new(
                        "table_3",
                        vec![
                            ("col2", Type::Integer),
                            ("col3", Type::Character(Some(10))),
                            ("col5", Type::Integer)
                        ],
                        None
                    )
                )
            )
        );
    }

    #[test]
    fn not_all_columns() {
        let catalog_manager = LockBasedCatalogManager::default();

        catalog_manager.add_table("table_2");
        catalog_manager.add_column_to("table_2", ("col1", Type::Integer, None));
        catalog_manager.add_column_to("table_2", ("col2", Type::Integer, None));
        catalog_manager.add_column_to("table_2", ("col3", Type::Integer, None));

        expect!(
            tokenize("select col1, col3 from table_2;")
                .and_then(parse)
                .and_then(|statement| type_inferring(catalog_manager, statement))
        ).to(
            be_ok().value(
                TypedStatement::Select(
                    TypedSelectQuery::new(
                        "table_2",
                        vec![
                            ("col1", Type::Integer),
                            ("col3", Type::Integer)
                        ],
                        None
                    )
                )
            )
        );
    }
}
<|endoftext|>


<commit_before>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

extern crate bragi;
extern crate rustless;
extern crate iron;
extern crate iron_test;
use std::process::Command;

fn get_handler(url: String) -> rustless::Application {
    let api = bragi::api::ApiEndPoint{es_cnx_string: url}.root();
    rustless::Application::new(api)
}

pub fn bragi_tests(es_wrapper: ::ElasticSearchWrapper) {

    let bano2mimir = concat!(env!("OUT_DIR"), "/../../../bano2mimir");
    info!("Launching {}", bano2mimir);
    let status = Command::new(bano2mimir)
                     .args(&["--input=./tests/fixtures/sample-bano.csv".into(),
                             format!("--connection-string={}", es_wrapper.host())])
                     .status()
                     .unwrap();
    assert!(status.success(), "`bano2mimir` failed {}", &status);
    es_wrapper.refresh();

	let handler = get_handler(format!("{}/munin", es_wrapper.host()));

    // Call status
    let resp = iron_test::request::get("http://localhost:3000/status",
                                iron::Headers::new(),
                                &handler).unwrap();
    let result_body = iron_test::response::extract_body_to_string(resp);

    assert_eq!(result_body, r#"{"version":"1.0.0","es":"http://localhost:9242/munin","status":"good"}"#);

    // Call autocomplete
    let resp = iron_test::request::get("http://localhost:3000/autocomplete?q=15 Rue Hector Malot, (Paris)",
                                iron::Headers::new(),
                                &handler).unwrap();
    let result_body = iron_test::response::extract_body_to_string(resp);
    let result = vec![r#"{"Autocomplete":{"type":"FeatureCollection","geocoding":{"version":"0.1.0","query":""},"#,
                  r#""features":[{"type":"Feature","geometry":{"coordinates":[2.3763789999999996,48.846495],"type":"Point"},"#,
                  r#""properties":{"geocoding":{"id":"addr:2.376379;48.846495","type":"house","label":"15 Rue Hector Malot, (Paris)","#,
                  r#""name":"15 Rue Hector Malot, (Paris)","housenumber":"15","street":"Rue Hector Malot, (Paris)","postcode":null,"city":null}}}]}}"#];
    assert_eq!(result_body, result.join(""));
}
<commit_msg>corrections<commit_after>// Copyright © 2016, Canal TP and/or its affiliates. All rights reserved.
//
// This file is part of Navitia,
//     the software to build cool stuff with public transport.
//
// Hope you'll enjoy and contribute to this project,
//     powered by Canal TP (www.canaltp.fr).
// Help us simplify mobility and open public transport:
//     a non ending quest to the responsive locomotion way of traveling!
//
// LICENCE: This program is free software; you can redistribute it
// and/or modify it under the terms of the GNU Affero General Public
// License as published by the Free Software Foundation, either
// version 3 of the License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public
// License along with this program. If not, see
// <http://www.gnu.org/licenses/>.
//
// Stay tuned using
// twitter @navitia
// IRC #navitia on freenode
// https://groups.google.com/d/forum/navitia
// www.navitia.io

extern crate bragi;
extern crate rustless;
extern crate iron;
extern crate iron_test;
use std::process::Command;

fn get_handler(url: String) -> rustless::Application {
    let api = bragi::api::ApiEndPoint{es_cnx_string: url}.root();
    rustless::Application::new(api)
}

pub fn bragi_tests(es_wrapper: ::ElasticSearchWrapper) {

    let bano2mimir = concat!(env!("OUT_DIR"), "/../../../bano2mimir");
    info!("Launching {}", bano2mimir);
    let status = Command::new(bano2mimir)
                     .args(&["--input=./tests/fixtures/sample-bano.csv".into(),
                             format!("--connection-string={}", es_wrapper.host())])
                     .status()
                     .unwrap();
    assert!(status.success(), "`bano2mimir` failed {}", &status);
    es_wrapper.refresh();

	let handler = get_handler(format!("{}/munin", es_wrapper.host()));

    // Call status
    let resp = iron_test::request::get("http://localhost:3000/status",
                                iron::Headers::new(),
                                &handler).unwrap();
    let result_body = iron_test::response::extract_body_to_string(resp);

    assert_eq!(result_body, r#"{"version":"1.0.0","es":"http://localhost:9242/munin","status":"good"}"#);

    // Call autocomplete
    let resp = iron_test::request::get("http://localhost:3000/autocomplete?q=15 Rue Hector Malot, (Paris)",
                                iron::Headers::new(),
                                &handler).unwrap();
    let result_body = iron_test::response::extract_body_to_string(resp);
    let result = concat!(r#"{"Autocomplete":{"type":"FeatureCollection","#,
    	                 r#""geocoding":{"version":"0.1.0","query":""},"#,
                         r#""features":[{"type":"Feature","geometry":{"coordinates":"#,
                         r#"[2.3763789999999996,48.846495],"type":"Point"},"#,
                         r#""properties":{"geocoding":{"id":"addr:2.376379;48.846495","#,
                         r#""type":"house","label":"15 Rue Hector Malot, (Paris)","#,
                         r#""name":"15 Rue Hector Malot, (Paris)","housenumber":"15","#,
                         r#""street":"Rue Hector Malot, (Paris)","postcode":null,"#
                         r#""city":null}}}]}}"#);
    assert_eq!(result_body, result);
}
<|endoftext|>


<commit_before>use std::io;
use std::io::stdio;
use std::collections;

struct Difficulty {
    min: uint,
    max: uint
}

fn main() {
    start();
}

fn start() {
    println!("WELCOME TO HANGMAN");
    println!("Type 'bye' at any time to quit.");
    println!("Please enter a difficulty: easy, medium, or hard.");

    loop {
        io::print(">");
        let input = &stdio::stdin()
                            .read_line()
                            .ok()
                            .expect("Failed to read line.");

        if input.as_slice() == "bye\n" {
            break;
        }

        let difficulty_opt = match input.as_slice() {
            "easy\n"   => Some(Difficulty { min: 3, max: 5 }),
            "medium\n" => Some(Difficulty { min: 5, max: 7 }),
            "hard\n"   => Some(Difficulty { min: 7, max: 9 }),
            _          => None
        };

        let difficulty = match difficulty_opt {
            Some(d) => d,
            None    => {
                println!("Invalid input.");
                println!("Please enter a difficulty: easy, medium, or hard:");
                continue
            }
        };

        println!("Minimum word length set to {}", difficulty.min);
        println!("Maximum word length set to {}", difficulty.max);

        let words: Box<Vec<String>> = get_words(&difficulty);
    }
}

fn get_words(difficulty: &Difficulty) -> Box<Vec<String>> {
    let mut words = Vec::new();

    let path = Path::new("/usr/share/dict/words");
    let words_file = io::File::open(&path).ok().expect("Error opening file.");
    let mut reader = io::BufferedReader::new(words_file);
    let mut counter = 0u;
    loop {
        match reader.read_line() {
            Ok(line) => {
                if difficulty.min < line.len() && line.len() < difficulty.max {
                    words.push(line
                               .as_slice()
                               .trim_right_chars('\n')
                               .to_string());
                    counter += 1;
                }
            }
            Err(why) => match why.kind {
                io::EndOfFile => break,
                _             => panic!("Error reading file: {}", why)
            }
        }
    }
    println!("Read in {} words.", counter);
    box words
}

<commit_msg>Need to learn more about lifetimes to get this to work i think<commit_after>use std::io;
use std::io::stdio;
use std::collections;
use std::rand::{Rng, task_rng};

struct Difficulty {
    min: uint,
    max: uint
}

struct Hangman {
    state: int,
    word: String,
    letters: collections::HashSet<char>,
    matched: collections::HashSet<char>
}

fn main() {
    let hangman = Hangman::new();
}

impl Hangman {
    fn new() -> Hangman {
        println!("WELCOME TO HANGMAN");
        println!("Type 'bye' at any time to quit.");
        println!("Please enter a difficulty: easy, medium, or hard.");

        let difficulty = Hangman::get_difficulty();

        println!("Minimum word length set to {}", difficulty.min);
        println!("Maximum word length set to {}", difficulty.max);

        let words = Hangman::get_words(&difficulty);

        // pick a word

        let mut rng = task_rng();
        let rand_word: &String = rng.choose(&*words.as_slice()).expect("No word.");
        let mut letters = collections::HashSet::new();
        println!("{}", rand_word);
        for letter in rand_word.as_slice().chars() {
            letters.insert(letter);
        }

        Hangman {
            state: 0,
            word: *rand_word,
            letters: letters,
            matched: collections::HashSet::new()
        }

    }

    fn get_difficulty() -> Difficulty {
        let mut difficulty: Difficulty;
        loop {
            io::print(">");
            let input = &stdio::stdin()
                .read_line()
                .ok()
                .expect("Failed to read line.");

            let difficulty_opt = match input.as_slice() {
                "easy\n"   => Some(Difficulty { min: 3, max: 5 }),
                "medium\n" => Some(Difficulty { min: 5, max: 7 }),
                "hard\n"   => Some(Difficulty { min: 7, max: 9 }),
                _          => None
            };

            match input.as_slice() {
                "easy\n"   => difficulty = Difficulty { min: 3, max: 5 },
                "medium\n" => difficulty = Difficulty { min: 5, max: 7 },
                "hard\n"   => difficulty = Difficulty { min: 7, max: 9 },
                _          => continue
            }

//            difficulty = match difficulty_opt {
//                Some(d) => d,
//                None    => {
//                    println!("Invalid input.");
//                    println!("Please enter a difficulty: easy, medium, or hard:");
//                    continue
//                }
//            };
        }
        difficulty
    }

    fn get_words(difficulty: &Difficulty) -> Box<Vec<String>> {
        let mut words = Vec::new();

        let path = Path::new("/usr/share/dict/words");
        let words_file = io::File::open(&path).ok().expect("Error opening file.");
        let mut reader = io::BufferedReader::new(words_file);
        let mut counter = 0u;
        loop {
            match reader.read_line() {
                Ok(line) => {
                    if difficulty.min < line.len() && line.len() < difficulty.max {
                        words.push(line
                                   .as_slice()
                                   .trim_right_chars('\n')
                                   .to_string());
                        counter += 1;
                    }
                }
                Err(why) => match why.kind {
                    io::EndOfFile => break,
                    _             => fail!("Error reading file: {}", why)
                }
            }
        }
        println!("Read in {} words.", counter);
        box words
    }
}

<|endoftext|>


<commit_before>//! Future producer
//!
//! A high level producer that returns a Future for every produced message.
// TODO: extend docs

use crate::client::{ClientContext, DefaultClientContext};
use crate::config::{ClientConfig, FromClientConfig, FromClientConfigAndContext, RDKafkaLogLevel};
use crate::producer::{BaseRecord, DeliveryResult, ProducerContext, ThreadedProducer};
use crate::statistics::Statistics;
use crate::error::{KafkaError, KafkaResult, RDKafkaError};
use crate::message::{Message, OwnedMessage, Timestamp, OwnedHeaders, ToBytes};
use crate::util::IntoOpaque;

use futures::{self, Canceled, Complete, Future, Poll, Oneshot};

use std::sync::Arc;
use std::time::{Duration, Instant};

//
// ********** FUTURE PRODUCER **********
//

/// Same as [BaseRecord] but specific to the [FutureProducer]. The only difference is that
/// the [FutureRecord] doesn't provide custom delivery opaque object.
#[derive(Debug)]
pub struct FutureRecord<'a, K: ToBytes + ?Sized + 'a, P: ToBytes + ?Sized + 'a> {
    /// Required destination topic
    pub topic: &'a str,
    /// Optional destination partition
    pub partition: Option<i32>,
    /// Optional payload
    pub payload: Option<&'a P>,
    /// Optional key
    pub key: Option<&'a K>,
    /// Optional timestamp
    pub timestamp: Option<i64>,
    /// Optional message headers
    pub headers: Option<OwnedHeaders>,
}

impl<'a, K: ToBytes + ?Sized, P: ToBytes + ?Sized> FutureRecord<'a, K, P> {
    /// Create a new record with the specified topic name.
    pub fn to(topic: &'a str) -> FutureRecord<'a, K, P> {
        FutureRecord {
            topic,
            partition: None,
            payload: None,
            key: None,
            timestamp: None,
            headers: None,
        }
    }

    fn from_base_record<D: IntoOpaque>(base_record: BaseRecord<'a, K, P, D>) -> FutureRecord<'a, K, P> {
        FutureRecord {
            topic: base_record.topic,
            partition: base_record.partition,
            key: base_record.key,
            payload: base_record.payload,
            timestamp: base_record.timestamp,
            headers: base_record.headers,
        }
    }

    /// Set the destination partition of the record.
    pub fn partition(mut self, partition: i32) -> FutureRecord<'a, K, P> {
        self.partition = Some(partition);
        self
    }

    /// Set the destination payload of the record.
    pub fn payload(mut self, payload: &'a P) -> FutureRecord<'a, K, P> {
        self.payload = Some(payload);
        self
    }

    /// Set the destination key of the record.
    pub fn key(mut self, key: &'a K) -> FutureRecord<'a, K, P> {
        self.key = Some(key);
        self
    }

    /// Set the destination timestamp of the record.
    pub fn timestamp(mut self, timestamp: i64) -> FutureRecord<'a, K, P> {
        self.timestamp = Some(timestamp);
        self
    }

    /// Set the headers of the record.
    pub fn headers(mut self, headers: OwnedHeaders) -> FutureRecord<'a, K, P> {
        self.headers = Some(headers);
        self
    }

    fn into_base_record<D: IntoOpaque>(self, delivery_opaque: D) -> BaseRecord<'a, K, P, D> {
        BaseRecord {
            topic: self.topic,
            partition: self.partition,
            key: self.key,
            payload: self.payload,
            timestamp: self.timestamp,
            headers: self.headers,
            delivery_opaque,
        }
    }
}

/// The `ProducerContext` used by the `FutureProducer`. This context will use a Future as its
/// `DeliveryOpaque` and will complete the future when the message is delivered (or failed to).
#[derive(Clone)]
struct FutureProducerContext<C: ClientContext + 'static> {
    wrapped_context: C,
}

/// Represents the result of message production as performed from the `FutureProducer`.
///
/// If message delivery was successful, `OwnedDeliveryResult` will return the partition and offset
/// of the message. If the message failed to be delivered an error will be returned, together with
/// an owned copy of the original message.
type OwnedDeliveryResult = Result<(i32, i64), (KafkaError, OwnedMessage)>;

// Delegates all the methods calls to the wrapped context.
impl<C: ClientContext + 'static> ClientContext for FutureProducerContext<C> {
    fn log(&self, level: RDKafkaLogLevel, fac: &str, log_message: &str) {
        self.wrapped_context.log(level, fac, log_message);
    }

    fn stats(&self, statistics: Statistics) {
        self.wrapped_context.stats(statistics);
    }

    fn error(&self, error: KafkaError, reason: &str) {
        self.wrapped_context.error(error, reason);
    }
}

impl<C: ClientContext + 'static> ProducerContext for FutureProducerContext<C> {
    type DeliveryOpaque = Box<Complete<OwnedDeliveryResult>>;

    fn delivery(&self, delivery_result: &DeliveryResult, tx: Box<Complete<OwnedDeliveryResult>>) {
        let owned_delivery_result = match *delivery_result {
            Ok(ref message) => Ok((message.partition(), message.offset())),
            Err((ref error, ref message)) => Err((error.clone(), message.detach())),
        };
        let _ = tx.send(owned_delivery_result); // TODO: handle error
    }
}

/// A producer that returns a `Future` for every message being produced.
///
/// Since message production in rdkafka is asynchronous, the caller cannot immediately know if the
/// delivery of the message was successful or not. The `FutureProducer` provides this information in
/// a `Future`, that will be completed once the information becomes available. This producer has an
/// internal polling thread and as such it doesn't need to be polled. It can be cheaply cloned to
/// get a reference to the same underlying producer. The internal will be terminated once the
/// the `FutureProducer` goes out of scope.
#[must_use = "Producer polling thread will stop immediately if unused"]
pub struct FutureProducer<C: ClientContext + 'static = DefaultClientContext> {
    producer: Arc<ThreadedProducer<FutureProducerContext<C>>>,
}

impl<C: ClientContext + 'static> Clone for FutureProducer<C> {
    fn clone(&self) -> FutureProducer<C> {
        FutureProducer { producer: self.producer.clone() }
    }
}

impl FromClientConfig for FutureProducer {
    fn from_config(config: &ClientConfig) -> KafkaResult<FutureProducer> {
        FutureProducer::from_config_and_context(config, DefaultClientContext)
    }
}

impl<C: ClientContext + 'static> FromClientConfigAndContext<C> for FutureProducer<C> {
    fn from_config_and_context(config: &ClientConfig, context: C) -> KafkaResult<FutureProducer<C>> {
        let future_context = FutureProducerContext { wrapped_context: context };
        let threaded_producer = ThreadedProducer::from_config_and_context(config, future_context)?;
        Ok(FutureProducer { producer: Arc::new(threaded_producer) })
    }
}

/// A [Future] wrapping the result of the message production.
///
/// Once completed, the future will contain an `OwnedDeliveryResult` with information on the
/// delivery status of the message.
pub struct DeliveryFuture {
    rx: Oneshot<OwnedDeliveryResult>,
}

impl Future for DeliveryFuture {
    type Item = OwnedDeliveryResult;
    type Error = Canceled;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.rx.poll()
    }
}

impl<C: ClientContext + 'static> FutureProducer<C> {
    /// Sends the provided [FutureRecord]. Returns a [DeliveryFuture] that will eventually contain the
    /// result of the send. The `block_ms` parameter will control for how long the producer
    /// is allowed to block if the queue is full. Set it to -1 to block forever, or 0 to never block.
    /// If `block_ms` is reached and the queue is still full, a [RDKafkaError::QueueFull] will be
    /// reported in the [DeliveryFuture].
    pub fn send<P, K>(&self, record: FutureRecord<P, K>, block_ms: i64) -> DeliveryFuture
        where K: ToBytes + ?Sized,
              P: ToBytes + ?Sized {
        let start_time = Instant::now();

        let (tx, rx) = futures::oneshot();
        let mut base_record = record.into_base_record(Box::new(tx));

        loop {
            match self.producer.send(base_record) {
                Ok(_) => break DeliveryFuture { rx },
                Err((KafkaError::MessageProduction(RDKafkaError::QueueFull), record)) => {
                    base_record = record;
                    if block_ms == -1 {
                        continue;
                    } else if block_ms > 0 && start_time.elapsed() < Duration::from_millis(block_ms as u64) {
                        self.poll(Duration::from_millis(100));
                        continue;
                    }
                },
                Err((e, record)) => {
                    let owned_message = OwnedMessage::new(
                        record.payload.map(|p| p.to_bytes().to_vec()),
                        record.key.map(|k| k.to_bytes().to_vec()),
                        record.topic.to_owned(),
                        record.timestamp.map_or(Timestamp::NotAvailable, Timestamp::CreateTime),
                        record.partition.unwrap_or(-1),
                        0,
                        record.headers,
                    );
                    let _ = record.delivery_opaque.send(Err((e, owned_message)));
                    break DeliveryFuture { rx };
                }
            }
        }
    }

    /// Same as [FutureProducer::send], with the only difference that if enqueuing fails, an
    /// error will be returned immediately, alongside the [FutureRecord] provided.
    pub fn send_result<'a, K, P>(&self, record: FutureRecord<'a, K, P>) -> Result<DeliveryFuture, (KafkaError, FutureRecord<'a, K, P>)>
    where K: ToBytes + ?Sized,
          P: ToBytes + ?Sized {
        let (tx, rx) = futures::oneshot();
        let base_record = record.into_base_record(Box::new(tx));
        self.producer.send(base_record)
            .map(|()| DeliveryFuture { rx })
            .map_err(|(e, record)| (e, FutureRecord::from_base_record(record)))
    }

    /// Polls the internal producer. This is not normally required since the `ThreadedProducer` had
    /// a thread dedicated to calling `poll` regularly.
    pub fn poll<T: Into<Option<Duration>>>(&self, timeout: T) {
        self.producer.poll(timeout);
    }

    /// Flushes the producer. Should be called before termination.
    pub fn flush<T: Into<Option<Duration>>>(&self, timeout: T) {
        self.producer.flush(timeout);
    }

    /// Returns the number of messages waiting to be sent, or send but not acknowledged yet.
    pub fn in_flight_count(&self) -> i32 {
        self.producer.in_flight_count()
    }
}

#[cfg(test)]
mod tests {
    // Just test that there are no panics, and that each struct implements the expected
    // traits (Clone, Send, Sync etc.). Behavior is tested in the integrations tests.
    use super::*;
    use crate::config::ClientConfig;

    struct TestContext;

    impl ClientContext for TestContext {}
    impl ProducerContext for TestContext {
        type DeliveryOpaque = Box<i32>;

        fn delivery(&self, _: &DeliveryResult, _: Self::DeliveryOpaque) {
            unimplemented!()
        }
    }

    // Verify that the future producer is clone, according to documentation.
    #[test]
    fn test_future_producer_clone() {
        let producer = ClientConfig::new().create::<FutureProducer>().unwrap();
        let _producer_clone = producer.clone();
    }

    // Test that the future producer can be cloned even if the context is not Clone.
    #[test]
    fn test_base_future_topic_send_sync() {
        let test_context = TestContext;
        let producer = ClientConfig::new()
            .create_with_context::<_, FutureProducer<TestContext>>(test_context)
            .unwrap();
        let _producer_clone = producer.clone();
    }
}
<commit_msg>Fix ordering of generics in FutureProducer::send<commit_after>//! Future producer
//!
//! A high level producer that returns a Future for every produced message.
// TODO: extend docs

use crate::client::{ClientContext, DefaultClientContext};
use crate::config::{ClientConfig, FromClientConfig, FromClientConfigAndContext, RDKafkaLogLevel};
use crate::producer::{BaseRecord, DeliveryResult, ProducerContext, ThreadedProducer};
use crate::statistics::Statistics;
use crate::error::{KafkaError, KafkaResult, RDKafkaError};
use crate::message::{Message, OwnedMessage, Timestamp, OwnedHeaders, ToBytes};
use crate::util::IntoOpaque;

use futures::{self, Canceled, Complete, Future, Poll, Oneshot};

use std::sync::Arc;
use std::time::{Duration, Instant};

//
// ********** FUTURE PRODUCER **********
//

/// Same as [BaseRecord] but specific to the [FutureProducer]. The only difference is that
/// the [FutureRecord] doesn't provide custom delivery opaque object.
#[derive(Debug)]
pub struct FutureRecord<'a, K: ToBytes + ?Sized + 'a, P: ToBytes + ?Sized + 'a> {
    /// Required destination topic
    pub topic: &'a str,
    /// Optional destination partition
    pub partition: Option<i32>,
    /// Optional payload
    pub payload: Option<&'a P>,
    /// Optional key
    pub key: Option<&'a K>,
    /// Optional timestamp
    pub timestamp: Option<i64>,
    /// Optional message headers
    pub headers: Option<OwnedHeaders>,
}

impl<'a, K: ToBytes + ?Sized, P: ToBytes + ?Sized> FutureRecord<'a, K, P> {
    /// Create a new record with the specified topic name.
    pub fn to(topic: &'a str) -> FutureRecord<'a, K, P> {
        FutureRecord {
            topic,
            partition: None,
            payload: None,
            key: None,
            timestamp: None,
            headers: None,
        }
    }

    fn from_base_record<D: IntoOpaque>(base_record: BaseRecord<'a, K, P, D>) -> FutureRecord<'a, K, P> {
        FutureRecord {
            topic: base_record.topic,
            partition: base_record.partition,
            key: base_record.key,
            payload: base_record.payload,
            timestamp: base_record.timestamp,
            headers: base_record.headers,
        }
    }

    /// Set the destination partition of the record.
    pub fn partition(mut self, partition: i32) -> FutureRecord<'a, K, P> {
        self.partition = Some(partition);
        self
    }

    /// Set the destination payload of the record.
    pub fn payload(mut self, payload: &'a P) -> FutureRecord<'a, K, P> {
        self.payload = Some(payload);
        self
    }

    /// Set the destination key of the record.
    pub fn key(mut self, key: &'a K) -> FutureRecord<'a, K, P> {
        self.key = Some(key);
        self
    }

    /// Set the destination timestamp of the record.
    pub fn timestamp(mut self, timestamp: i64) -> FutureRecord<'a, K, P> {
        self.timestamp = Some(timestamp);
        self
    }

    /// Set the headers of the record.
    pub fn headers(mut self, headers: OwnedHeaders) -> FutureRecord<'a, K, P> {
        self.headers = Some(headers);
        self
    }

    fn into_base_record<D: IntoOpaque>(self, delivery_opaque: D) -> BaseRecord<'a, K, P, D> {
        BaseRecord {
            topic: self.topic,
            partition: self.partition,
            key: self.key,
            payload: self.payload,
            timestamp: self.timestamp,
            headers: self.headers,
            delivery_opaque,
        }
    }
}

/// The `ProducerContext` used by the `FutureProducer`. This context will use a Future as its
/// `DeliveryOpaque` and will complete the future when the message is delivered (or failed to).
#[derive(Clone)]
struct FutureProducerContext<C: ClientContext + 'static> {
    wrapped_context: C,
}

/// Represents the result of message production as performed from the `FutureProducer`.
///
/// If message delivery was successful, `OwnedDeliveryResult` will return the partition and offset
/// of the message. If the message failed to be delivered an error will be returned, together with
/// an owned copy of the original message.
type OwnedDeliveryResult = Result<(i32, i64), (KafkaError, OwnedMessage)>;

// Delegates all the methods calls to the wrapped context.
impl<C: ClientContext + 'static> ClientContext for FutureProducerContext<C> {
    fn log(&self, level: RDKafkaLogLevel, fac: &str, log_message: &str) {
        self.wrapped_context.log(level, fac, log_message);
    }

    fn stats(&self, statistics: Statistics) {
        self.wrapped_context.stats(statistics);
    }

    fn error(&self, error: KafkaError, reason: &str) {
        self.wrapped_context.error(error, reason);
    }
}

impl<C: ClientContext + 'static> ProducerContext for FutureProducerContext<C> {
    type DeliveryOpaque = Box<Complete<OwnedDeliveryResult>>;

    fn delivery(&self, delivery_result: &DeliveryResult, tx: Box<Complete<OwnedDeliveryResult>>) {
        let owned_delivery_result = match *delivery_result {
            Ok(ref message) => Ok((message.partition(), message.offset())),
            Err((ref error, ref message)) => Err((error.clone(), message.detach())),
        };
        let _ = tx.send(owned_delivery_result); // TODO: handle error
    }
}

/// A producer that returns a `Future` for every message being produced.
///
/// Since message production in rdkafka is asynchronous, the caller cannot immediately know if the
/// delivery of the message was successful or not. The `FutureProducer` provides this information in
/// a `Future`, that will be completed once the information becomes available. This producer has an
/// internal polling thread and as such it doesn't need to be polled. It can be cheaply cloned to
/// get a reference to the same underlying producer. The internal will be terminated once the
/// the `FutureProducer` goes out of scope.
#[must_use = "Producer polling thread will stop immediately if unused"]
pub struct FutureProducer<C: ClientContext + 'static = DefaultClientContext> {
    producer: Arc<ThreadedProducer<FutureProducerContext<C>>>,
}

impl<C: ClientContext + 'static> Clone for FutureProducer<C> {
    fn clone(&self) -> FutureProducer<C> {
        FutureProducer { producer: self.producer.clone() }
    }
}

impl FromClientConfig for FutureProducer {
    fn from_config(config: &ClientConfig) -> KafkaResult<FutureProducer> {
        FutureProducer::from_config_and_context(config, DefaultClientContext)
    }
}

impl<C: ClientContext + 'static> FromClientConfigAndContext<C> for FutureProducer<C> {
    fn from_config_and_context(config: &ClientConfig, context: C) -> KafkaResult<FutureProducer<C>> {
        let future_context = FutureProducerContext { wrapped_context: context };
        let threaded_producer = ThreadedProducer::from_config_and_context(config, future_context)?;
        Ok(FutureProducer { producer: Arc::new(threaded_producer) })
    }
}

/// A [Future] wrapping the result of the message production.
///
/// Once completed, the future will contain an `OwnedDeliveryResult` with information on the
/// delivery status of the message.
pub struct DeliveryFuture {
    rx: Oneshot<OwnedDeliveryResult>,
}

impl Future for DeliveryFuture {
    type Item = OwnedDeliveryResult;
    type Error = Canceled;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        self.rx.poll()
    }
}

impl<C: ClientContext + 'static> FutureProducer<C> {
    /// Sends the provided [FutureRecord]. Returns a [DeliveryFuture] that will eventually contain the
    /// result of the send. The `block_ms` parameter will control for how long the producer
    /// is allowed to block if the queue is full. Set it to -1 to block forever, or 0 to never block.
    /// If `block_ms` is reached and the queue is still full, a [RDKafkaError::QueueFull] will be
    /// reported in the [DeliveryFuture].
    pub fn send<K, P>(&self, record: FutureRecord<K, P>, block_ms: i64) -> DeliveryFuture
        where K: ToBytes + ?Sized,
              P: ToBytes + ?Sized {
        let start_time = Instant::now();

        let (tx, rx) = futures::oneshot();
        let mut base_record = record.into_base_record(Box::new(tx));

        loop {
            match self.producer.send(base_record) {
                Ok(_) => break DeliveryFuture { rx },
                Err((KafkaError::MessageProduction(RDKafkaError::QueueFull), record)) => {
                    base_record = record;
                    if block_ms == -1 {
                        continue;
                    } else if block_ms > 0 && start_time.elapsed() < Duration::from_millis(block_ms as u64) {
                        self.poll(Duration::from_millis(100));
                        continue;
                    }
                },
                Err((e, record)) => {
                    let owned_message = OwnedMessage::new(
                        record.payload.map(|p| p.to_bytes().to_vec()),
                        record.key.map(|k| k.to_bytes().to_vec()),
                        record.topic.to_owned(),
                        record.timestamp.map_or(Timestamp::NotAvailable, Timestamp::CreateTime),
                        record.partition.unwrap_or(-1),
                        0,
                        record.headers,
                    );
                    let _ = record.delivery_opaque.send(Err((e, owned_message)));
                    break DeliveryFuture { rx };
                }
            }
        }
    }

    /// Same as [FutureProducer::send], with the only difference that if enqueuing fails, an
    /// error will be returned immediately, alongside the [FutureRecord] provided.
    pub fn send_result<'a, K, P>(&self, record: FutureRecord<'a, K, P>) -> Result<DeliveryFuture, (KafkaError, FutureRecord<'a, K, P>)>
    where K: ToBytes + ?Sized,
          P: ToBytes + ?Sized {
        let (tx, rx) = futures::oneshot();
        let base_record = record.into_base_record(Box::new(tx));
        self.producer.send(base_record)
            .map(|()| DeliveryFuture { rx })
            .map_err(|(e, record)| (e, FutureRecord::from_base_record(record)))
    }

    /// Polls the internal producer. This is not normally required since the `ThreadedProducer` had
    /// a thread dedicated to calling `poll` regularly.
    pub fn poll<T: Into<Option<Duration>>>(&self, timeout: T) {
        self.producer.poll(timeout);
    }

    /// Flushes the producer. Should be called before termination.
    pub fn flush<T: Into<Option<Duration>>>(&self, timeout: T) {
        self.producer.flush(timeout);
    }

    /// Returns the number of messages waiting to be sent, or send but not acknowledged yet.
    pub fn in_flight_count(&self) -> i32 {
        self.producer.in_flight_count()
    }
}

#[cfg(test)]
mod tests {
    // Just test that there are no panics, and that each struct implements the expected
    // traits (Clone, Send, Sync etc.). Behavior is tested in the integrations tests.
    use super::*;
    use crate::config::ClientConfig;

    struct TestContext;

    impl ClientContext for TestContext {}
    impl ProducerContext for TestContext {
        type DeliveryOpaque = Box<i32>;

        fn delivery(&self, _: &DeliveryResult, _: Self::DeliveryOpaque) {
            unimplemented!()
        }
    }

    // Verify that the future producer is clone, according to documentation.
    #[test]
    fn test_future_producer_clone() {
        let producer = ClientConfig::new().create::<FutureProducer>().unwrap();
        let _producer_clone = producer.clone();
    }

    // Test that the future producer can be cloned even if the context is not Clone.
    #[test]
    fn test_base_future_topic_send_sync() {
        let test_context = TestContext;
        let producer = ClientConfig::new()
            .create_with_context::<_, FutureProducer<TestContext>>(test_context)
            .unwrap();
        let _producer_clone = producer.clone();
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    completion::on_completion,
    goto_definition::on_goto_definition,
    hover::on_hover,
    lsp::{
        set_initializing_status, CompletionOptions, Connection, GotoDefinition, HoverRequest,
        InitializeParams, Message, ServerCapabilities, ServerResponse, TextDocumentSyncCapability,
        TextDocumentSyncKind, WorkDoneProgressOptions,
    },
    lsp_process_error::LSPProcessResult,
    references::on_references,
    shutdown::{on_exit, on_shutdown},
    text_documents::{
        on_did_change_text_document, on_did_close_text_document, on_did_open_text_document,
    },
};
use common::PerfLogger;
use crossbeam::Sender;
use log::info;
use lsp_server::{ErrorCode, Notification, ResponseError};
use lsp_types::{
    notification::{DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument, Exit},
    request::{Completion, References, Shutdown},
};
use relay_compiler::config::Config;
use std::sync::Arc;

mod lsp_request_dispatch;
use lsp_request_dispatch::LSPRequestDispatch;
mod lsp_notification_dispatch;
use lsp_notification_dispatch::LSPNotificationDispatch;
mod lsp_state;
pub(crate) use lsp_state::LSPState;

/// Initializes an LSP connection, handling the `initialize` message and `initialized` notification
/// handshake.
pub fn initialize(connection: &Connection) -> LSPProcessResult<InitializeParams> {
    let mut server_capabilities = ServerCapabilities::default();
    // Enable text document syncing so we can know when files are opened/changed/saved/closed
    server_capabilities.text_document_sync =
        Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::Full));

    server_capabilities.completion_provider = Some(CompletionOptions {
        resolve_provider: Some(true),
        trigger_characters: None,
        work_done_progress_options: WorkDoneProgressOptions {
            work_done_progress: None,
        },
    });

    server_capabilities.hover_provider = Some(true);
    server_capabilities.definition_provider = Some(true);
    server_capabilities.references_provider = Some(true);

    let server_capabilities = serde_json::to_value(&server_capabilities)?;
    let params = connection.initialize(server_capabilities)?;
    let params: InitializeParams = serde_json::from_value(params)?;
    Ok(params)
}

/// Run the main server loop
pub async fn run<TPerfLogger: PerfLogger + 'static>(
    connection: Connection,
    config: Config,
    _params: InitializeParams,
    perf_logger: Arc<TPerfLogger>,
) -> LSPProcessResult<()>
where
    TPerfLogger: PerfLogger + 'static,
{
    info!(
        "Running language server with config root {:?}",
        config.root_dir
    );

    let mut lsp_state = LSPState::new(config, &connection.sender, perf_logger);

    set_initializing_status(&connection.sender);

    for msg in connection.receiver {
        info!("LSP message received {:?}", msg);
        match msg {
            Message::Request(req) => {
                handle_request(&mut lsp_state, req, &connection.sender);
            }
            Message::Notification(notification) => {
                handle_notification(&mut lsp_state, notification);
            }
            _ => {
                // Ignore responses for now
            }
        }
    }

    Ok(())
}

fn handle_request<TPerfLogger: PerfLogger + 'static>(
    lsp_state: &mut LSPState<TPerfLogger>,
    req: lsp_server::Request,
    sender: &Sender<Message>,
) {
    let get_server_response = || -> Result<_, ServerResponse> {
        let request = LSPRequestDispatch::new(req, lsp_state)
            .on_request_sync::<HoverRequest>(on_hover)?
            .on_request_sync::<GotoDefinition>(on_goto_definition)?
            .on_request_sync::<References>(on_references)?
            .on_request_sync::<Completion>(on_completion)?
            .on_request_sync::<Shutdown>(on_shutdown)?
            .request();

        // If we have gotten here, we have not handled the request
        Ok(ServerResponse {
            id: request.id,
            result: None,
            error: Some(ResponseError {
                code: ErrorCode::MethodNotFound as i32,
                data: None,
                message: format!("No handler registered for method '{}'", request.method),
            }),
        })
    };

    let response = get_server_response().unwrap_or_else(|response| response);

    // TODO handle these errors
    let _ = sender.send(Message::Response(response));
}

fn handle_notification<TPerfLogger: PerfLogger + 'static>(
    lsp_state: &mut LSPState<TPerfLogger>,
    notification: Notification,
) {
    let get_notification_handling_response = || -> Result<(), ()> {
        let notification = LSPNotificationDispatch::new(notification, lsp_state)
            .on_notification_sync::<DidOpenTextDocument>(on_did_open_text_document)?
            .on_notification_sync::<DidCloseTextDocument>(on_did_close_text_document)?
            .on_notification_sync::<DidChangeTextDocument>(on_did_change_text_document)?
            .on_notification_sync::<Exit>(on_exit)?
            .notification();

        // If we have gotten here, we have not handled the notification
        // TODO report this back to the LSP somehow
        // TODO add a DidSaveTextDocument handler or change the server capabilities
        info!(
            "Error: no handler registered for notification '{}'",
            notification.method
        );
        Ok(())
    };

    // TODO handle these errors
    let _ = get_notification_handling_response();
}
<commit_msg>Add perf logging to message and notification handling<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::{
    completion::on_completion,
    goto_definition::on_goto_definition,
    hover::on_hover,
    lsp::{
        set_initializing_status, CompletionOptions, Connection, GotoDefinition, HoverRequest,
        InitializeParams, Message, ServerCapabilities, ServerResponse, TextDocumentSyncCapability,
        TextDocumentSyncKind, WorkDoneProgressOptions,
    },
    lsp_process_error::LSPProcessResult,
    references::on_references,
    shutdown::{on_exit, on_shutdown},
    text_documents::{
        on_did_change_text_document, on_did_close_text_document, on_did_open_text_document,
    },
};
use common::{PerfLogEvent, PerfLogger};
use crossbeam::Sender;
use log::info;
use lsp_server::{ErrorCode, Notification, ResponseError};
use lsp_types::{
    notification::{DidChangeTextDocument, DidCloseTextDocument, DidOpenTextDocument, Exit},
    request::{Completion, References, Shutdown},
};
use relay_compiler::config::Config;
use std::sync::Arc;

mod lsp_request_dispatch;
use lsp_request_dispatch::LSPRequestDispatch;
mod lsp_notification_dispatch;
use lsp_notification_dispatch::LSPNotificationDispatch;
mod lsp_state;
pub(crate) use lsp_state::LSPState;

/// Initializes an LSP connection, handling the `initialize` message and `initialized` notification
/// handshake.
pub fn initialize(connection: &Connection) -> LSPProcessResult<InitializeParams> {
    let mut server_capabilities = ServerCapabilities::default();
    // Enable text document syncing so we can know when files are opened/changed/saved/closed
    server_capabilities.text_document_sync =
        Some(TextDocumentSyncCapability::Kind(TextDocumentSyncKind::Full));

    server_capabilities.completion_provider = Some(CompletionOptions {
        resolve_provider: Some(true),
        trigger_characters: None,
        work_done_progress_options: WorkDoneProgressOptions {
            work_done_progress: None,
        },
    });

    server_capabilities.hover_provider = Some(true);
    server_capabilities.definition_provider = Some(true);
    server_capabilities.references_provider = Some(true);

    let server_capabilities = serde_json::to_value(&server_capabilities)?;
    let params = connection.initialize(server_capabilities)?;
    let params: InitializeParams = serde_json::from_value(params)?;
    Ok(params)
}

/// Run the main server loop
pub async fn run<TPerfLogger: PerfLogger + 'static>(
    connection: Connection,
    config: Config,
    _params: InitializeParams,
    perf_logger: Arc<TPerfLogger>,
) -> LSPProcessResult<()>
where
    TPerfLogger: PerfLogger + 'static,
{
    info!(
        "Running language server with config root {:?}",
        config.root_dir
    );

    let mut lsp_state = LSPState::new(config, &connection.sender, Arc::clone(&perf_logger));

    set_initializing_status(&connection.sender);

    for msg in connection.receiver {
        info!("LSP message received {:?}", msg);
        match msg {
            Message::Request(req) => {
                handle_request(&mut lsp_state, req, &connection.sender, &perf_logger);
            }
            Message::Notification(notification) => {
                handle_notification(&mut lsp_state, notification, &perf_logger);
            }
            _ => {
                // Ignore responses for now
            }
        }
    }

    Ok(())
}

fn handle_request<TPerfLogger: PerfLogger + 'static>(
    lsp_state: &mut LSPState<TPerfLogger>,
    request: lsp_server::Request,
    sender: &Sender<Message>,
    perf_logger: &Arc<TPerfLogger>,
) {
    let get_server_response_bound = |req| dispatch_request(req, lsp_state);
    let get_response = with_request_logging(perf_logger, get_server_response_bound);

    // TODO handle these errors
    let _ = sender.send(Message::Response(get_response(request)));
}

fn dispatch_request<TPerfLogger: PerfLogger + 'static>(
    request: lsp_server::Request,
    lsp_state: &mut LSPState<TPerfLogger>,
) -> ServerResponse {
    let get_response = || -> Result<_, ServerResponse> {
        let request = LSPRequestDispatch::new(request, lsp_state)
            .on_request_sync::<HoverRequest>(on_hover)?
            .on_request_sync::<GotoDefinition>(on_goto_definition)?
            .on_request_sync::<References>(on_references)?
            .on_request_sync::<Completion>(on_completion)?
            .on_request_sync::<Shutdown>(on_shutdown)?
            .request();

        // If we have gotten here, we have not handled the request
        Ok(ServerResponse {
            id: request.id,
            result: None,
            error: Some(ResponseError {
                code: ErrorCode::MethodNotFound as i32,
                data: None,
                message: format!("No handler registered for method '{}'", request.method),
            }),
        })
    };
    get_response().unwrap_or_else(|response| response)
}

fn with_request_logging<'a, TPerfLogger: PerfLogger + 'static>(
    perf_logger: &'a Arc<TPerfLogger>,
    get_response: impl FnOnce(lsp_server::Request) -> ServerResponse + 'a,
) -> impl FnOnce(lsp_server::Request) -> ServerResponse + 'a {
    move |request| {
        let lsp_request_event = perf_logger.create_event("lsp_message");
        lsp_request_event.string("lsp_method", request.method.clone());
        lsp_request_event.string("lsp_type", "request".to_string());
        let lsp_request_processing_time = lsp_request_event.start("lsp_message_processing_time");

        let response = get_response(request);

        if response.result.is_some() {
            lsp_request_event.string("lsp_outcome", "success".to_string());
        } else if let Some(error) = &response.error {
            lsp_request_event.string("lsp_outcome", "error".to_string());
            lsp_request_event.number("lsp_error_code", error.code as usize);
            if let Some(data) = &error.data {
                lsp_request_event.string("lsp_error_data", data.to_string());
            }
        }
        // N.B. we don't handle the case where the ServerResponse has neither a result nor
        // an error, which is an invalid state.

        lsp_request_event.stop(lsp_request_processing_time);
        perf_logger.complete_event(lsp_request_event);

        response
    }
}

fn handle_notification<TPerfLogger: PerfLogger + 'static>(
    lsp_state: &mut LSPState<TPerfLogger>,
    notification: Notification,
    perf_logger: &Arc<TPerfLogger>,
) {
    let lsp_notification_event = perf_logger.create_event("lsp_message");
    lsp_notification_event.string("lsp_method", notification.method.clone());
    lsp_notification_event.string("lsp_type", "notification".to_string());
    let lsp_notification_processing_time =
        lsp_notification_event.start("lsp_message_processing_time");

    let notification_result = dispatch_notification(notification, lsp_state);

    // The only possible error (for now) is not handling the notification.
    // N.B. is_ok is correct here.
    if notification_result.is_ok() {
        lsp_notification_event.string("lsp_outcome", "error".to_string());
        lsp_notification_event.number("lsp_error_code", ErrorCode::MethodNotFound as usize);
    }

    lsp_notification_event.stop(lsp_notification_processing_time);
    perf_logger.complete_event(lsp_notification_event);
}

fn dispatch_notification<TPerfLogger: PerfLogger + 'static>(
    notification: lsp_server::Notification,
    lsp_state: &mut LSPState<TPerfLogger>,
) -> Result<(), ()> {
    let notification = LSPNotificationDispatch::new(notification, lsp_state)
        .on_notification_sync::<DidOpenTextDocument>(on_did_open_text_document)?
        .on_notification_sync::<DidCloseTextDocument>(on_did_close_text_document)?
        .on_notification_sync::<DidChangeTextDocument>(on_did_change_text_document)?
        .on_notification_sync::<Exit>(on_exit)?
        .notification();

    // If we have gotten here, we have not handled the notification
    // TODO add a DidSaveTextDocument handler or change the server capabilities
    info!(
        "Error: no handler registered for notification '{}'",
        notification.method
    );
    Ok(())
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use image::base::ImageMetadata;
use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::Image;
use std::sync::Arc;
use url::Url;

/// This is optionally passed to the image cache when requesting
/// and image, and returned to the specified event loop when the
/// image load completes. It is typically used to trigger a reflow
/// and/or repaint.
#[derive(Clone, Deserialize, Serialize)]
pub struct ImageResponder {
    sender: IpcSender<ImageResponse>,
}

impl ImageResponder {
    pub fn new(sender: IpcSender<ImageResponse>) -> ImageResponder {
        ImageResponder {
            sender: sender,
        }
    }

    pub fn respond(&self, response: ImageResponse) {
        self.sender.send(response).unwrap()
    }
}

/// The current state of an image in the cache.
#[derive(PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum ImageState {
    Pending,
    LoadError,
    NotRequested,
}

/// The returned image.
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
pub enum ImageResponse {
    /// The requested image was loaded.
    Loaded(Arc<Image>),
    /// The request image metadata was loaded.
    MetadataLoaded(ImageMetadata),
    /// The requested image failed to load, so a placeholder was loaded instead.
    PlaceholderLoaded(Arc<Image>),
    /// Neither the requested image nor the placeholder could be loaded.
    None
}

/// Indicating either entire image or just metadata availability
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
pub enum ImageOrMetadataAvailable {
    ImageAvailable(Arc<Image>),
    MetadataAvailable(ImageMetadata),
}

/// Channel used by the image cache to send results.
#[derive(Clone, Deserialize, Serialize)]
pub struct ImageCacheChan(pub IpcSender<ImageCacheResult>);

/// The result of an image cache command that is returned to the
/// caller.
#[derive(Deserialize, Serialize)]
pub struct ImageCacheResult {
    pub responder: Option<ImageResponder>,
    pub image_response: ImageResponse,
}

/// Commands that the image cache understands.
#[derive(Deserialize, Serialize)]
pub enum ImageCacheCommand {
    /// Request an image asynchronously from the cache. Supply a channel
    /// to receive the result, and optionally an image responder
    /// that is passed to the result channel.
    RequestImage(Url, ImageCacheChan, Option<ImageResponder>),

    /// Requests an image and a "metadata-ready" notification message asynchronously from the
    /// cache. The cache will make an effort to send metadata before the image is completely
    /// loaded. Supply a channel to receive the results, and optionally an image responder
    /// that is passed to the result channel.
    RequestImageAndMetadata(Url, ImageCacheChan, Option<ImageResponder>),

    /// Synchronously check the state of an image in the cache.
    /// TODO(gw): Profile this on some real world sites and see
    /// if it's worth caching the results of this locally in each
    /// layout / paint thread.
    GetImageIfAvailable(Url, UsePlaceholder, IpcSender<Result<Arc<Image>, ImageState>>),

    /// Synchronously check the state of an image in the cache. If the image is in a loading
    /// state and but its metadata has been made available, it will be sent as a response.
    GetImageOrMetadataIfAvailable(Url, UsePlaceholder, IpcSender<Result<ImageOrMetadataAvailable, ImageState>>),

    /// Instruct the cache to store this data as a newly-complete network request and continue
    /// decoding the result into pixel data
    StoreDecodeImage(Url, Vec<u8>),

    /// Clients must wait for a response before shutting down the ResourceThread
    Exit(IpcSender<()>),
}

#[derive(Copy, Clone, PartialEq, Hash, Eq, Deserialize, Serialize)]
pub enum UsePlaceholder {
    No,
    Yes,
}

/// The client side of the image cache thread. This can be safely cloned
/// and passed to different threads.
#[derive(Clone, Deserialize, Serialize)]
pub struct ImageCacheThread {
    chan: IpcSender<ImageCacheCommand>,
}

/// The public API for the image cache thread.
impl ImageCacheThread {
    /// Construct a new image cache
    pub fn new(chan: IpcSender<ImageCacheCommand>) -> ImageCacheThread {
        ImageCacheThread {
            chan: chan,
        }
    }

    /// Asynchronously request an image. See ImageCacheCommand::RequestImage.
    pub fn request_image(&self,
                         url: Url,
                         result_chan: ImageCacheChan,
                         responder: Option<ImageResponder>) {
        let msg = ImageCacheCommand::RequestImage(url, result_chan, responder);
        self.chan.send(msg).unwrap();
    }

    /// Asynchronously request an image and metadata.
    /// See ImageCacheCommand::RequestImageAndMetadata
    pub fn request_image_and_metadata(&self,
                                      url: Url,
                                      result_chan: ImageCacheChan,
                                      responder: Option<ImageResponder>) {
        let msg = ImageCacheCommand::RequestImageAndMetadata(url, result_chan, responder);
        self.chan.send(msg).unwrap();
    }

    /// Get the current state of an image. See ImageCacheCommand::GetImageIfAvailable.
    pub fn find_image(&self, url: Url, use_placeholder: UsePlaceholder)
                                  -> Result<Arc<Image>, ImageState> {
        let (sender, receiver) = ipc::channel().unwrap();
        let msg = ImageCacheCommand::GetImageIfAvailable(url, use_placeholder, sender);
        self.chan.send(msg).unwrap();
        receiver.recv().unwrap()
    }

    /// Get the current state of an image, returning its metadata if available.
    /// See ImageCacheCommand::GetImageOrMetadataIfAvailable.
    pub fn find_image_or_metadata(&self, url: Url, use_placeholder: UsePlaceholder)
                                  -> Result<ImageOrMetadataAvailable, ImageState> {
        let (sender, receiver) = ipc::channel().unwrap();
        let msg = ImageCacheCommand::GetImageOrMetadataIfAvailable(url, use_placeholder, sender);
        self.chan.send(msg).unwrap();
        receiver.recv().unwrap()
    }

    /// Decode the given image bytes and cache the result for the given URL.
    pub fn store_complete_image_bytes(&self,
                                   url: Url,
                                   image_data: Vec<u8>) {
        let msg = ImageCacheCommand::StoreDecodeImage(url, image_data);
        self.chan.send(msg).unwrap();
    }

    /// Shutdown the image cache thread.
    pub fn exit(&self) {
        let (response_chan, response_port) = ipc::channel().unwrap();
        self.chan.send(ImageCacheCommand::Exit(response_chan)).unwrap();
        response_port.recv().unwrap();
    }
}
<commit_msg>Auto merge of #12431 - ice9js:fix/remove-unwrap-calls, r=Wafflespeanut<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use image::base::ImageMetadata;
use ipc_channel::ipc::{self, IpcSender};
use msg::constellation_msg::Image;
use std::sync::Arc;
use url::Url;

/// This is optionally passed to the image cache when requesting
/// and image, and returned to the specified event loop when the
/// image load completes. It is typically used to trigger a reflow
/// and/or repaint.
#[derive(Clone, Deserialize, Serialize)]
pub struct ImageResponder {
    sender: IpcSender<ImageResponse>,
}

impl ImageResponder {
    pub fn new(sender: IpcSender<ImageResponse>) -> ImageResponder {
        ImageResponder {
            sender: sender,
        }
    }

    pub fn respond(&self, response: ImageResponse) {
        self.sender.send(response).unwrap()
    }
}

/// The current state of an image in the cache.
#[derive(PartialEq, Copy, Clone, Deserialize, Serialize)]
pub enum ImageState {
    Pending,
    LoadError,
    NotRequested,
}

/// The returned image.
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
pub enum ImageResponse {
    /// The requested image was loaded.
    Loaded(Arc<Image>),
    /// The request image metadata was loaded.
    MetadataLoaded(ImageMetadata),
    /// The requested image failed to load, so a placeholder was loaded instead.
    PlaceholderLoaded(Arc<Image>),
    /// Neither the requested image nor the placeholder could be loaded.
    None
}

/// Indicating either entire image or just metadata availability
#[derive(Clone, Deserialize, Serialize, HeapSizeOf)]
pub enum ImageOrMetadataAvailable {
    ImageAvailable(Arc<Image>),
    MetadataAvailable(ImageMetadata),
}

/// Channel used by the image cache to send results.
#[derive(Clone, Deserialize, Serialize)]
pub struct ImageCacheChan(pub IpcSender<ImageCacheResult>);

/// The result of an image cache command that is returned to the
/// caller.
#[derive(Deserialize, Serialize)]
pub struct ImageCacheResult {
    pub responder: Option<ImageResponder>,
    pub image_response: ImageResponse,
}

/// Commands that the image cache understands.
#[derive(Deserialize, Serialize)]
pub enum ImageCacheCommand {
    /// Request an image asynchronously from the cache. Supply a channel
    /// to receive the result, and optionally an image responder
    /// that is passed to the result channel.
    RequestImage(Url, ImageCacheChan, Option<ImageResponder>),

    /// Requests an image and a "metadata-ready" notification message asynchronously from the
    /// cache. The cache will make an effort to send metadata before the image is completely
    /// loaded. Supply a channel to receive the results, and optionally an image responder
    /// that is passed to the result channel.
    RequestImageAndMetadata(Url, ImageCacheChan, Option<ImageResponder>),

    /// Synchronously check the state of an image in the cache.
    /// TODO(gw): Profile this on some real world sites and see
    /// if it's worth caching the results of this locally in each
    /// layout / paint thread.
    GetImageIfAvailable(Url, UsePlaceholder, IpcSender<Result<Arc<Image>, ImageState>>),

    /// Synchronously check the state of an image in the cache. If the image is in a loading
    /// state and but its metadata has been made available, it will be sent as a response.
    GetImageOrMetadataIfAvailable(Url, UsePlaceholder, IpcSender<Result<ImageOrMetadataAvailable, ImageState>>),

    /// Instruct the cache to store this data as a newly-complete network request and continue
    /// decoding the result into pixel data
    StoreDecodeImage(Url, Vec<u8>),

    /// Clients must wait for a response before shutting down the ResourceThread
    Exit(IpcSender<()>),
}

#[derive(Copy, Clone, PartialEq, Hash, Eq, Deserialize, Serialize)]
pub enum UsePlaceholder {
    No,
    Yes,
}

/// The client side of the image cache thread. This can be safely cloned
/// and passed to different threads.
#[derive(Clone, Deserialize, Serialize)]
pub struct ImageCacheThread {
    chan: IpcSender<ImageCacheCommand>,
}

/// The public API for the image cache thread.
impl ImageCacheThread {
    /// Construct a new image cache
    pub fn new(chan: IpcSender<ImageCacheCommand>) -> ImageCacheThread {
        ImageCacheThread {
            chan: chan,
        }
    }

    /// Asynchronously request an image. See ImageCacheCommand::RequestImage.
    pub fn request_image(&self,
                         url: Url,
                         result_chan: ImageCacheChan,
                         responder: Option<ImageResponder>) {
        let msg = ImageCacheCommand::RequestImage(url, result_chan, responder);
        let _ = self.chan.send(msg);
    }

    /// Asynchronously request an image and metadata.
    /// See ImageCacheCommand::RequestImageAndMetadata
    pub fn request_image_and_metadata(&self,
                                      url: Url,
                                      result_chan: ImageCacheChan,
                                      responder: Option<ImageResponder>) {
        let msg = ImageCacheCommand::RequestImageAndMetadata(url, result_chan, responder);
        let _ = self.chan.send(msg);
    }

    /// Get the current state of an image. See ImageCacheCommand::GetImageIfAvailable.
    pub fn find_image(&self, url: Url, use_placeholder: UsePlaceholder)
                                  -> Result<Arc<Image>, ImageState> {
        let (sender, receiver) = ipc::channel().unwrap();
        let msg = ImageCacheCommand::GetImageIfAvailable(url, use_placeholder, sender);
        let _ = self.chan.send(msg);
        try!(receiver.recv().map_err(|_| ImageState::LoadError))
    }

    /// Get the current state of an image, returning its metadata if available.
    /// See ImageCacheCommand::GetImageOrMetadataIfAvailable.
    pub fn find_image_or_metadata(&self, url: Url, use_placeholder: UsePlaceholder)
                                  -> Result<ImageOrMetadataAvailable, ImageState> {
        let (sender, receiver) = ipc::channel().unwrap();
        let msg = ImageCacheCommand::GetImageOrMetadataIfAvailable(url, use_placeholder, sender);
        let _ = self.chan.send(msg);
        try!(receiver.recv().map_err(|_| ImageState::LoadError))
    }

    /// Decode the given image bytes and cache the result for the given URL.
    pub fn store_complete_image_bytes(&self,
                                   url: Url,
                                   image_data: Vec<u8>) {
        let msg = ImageCacheCommand::StoreDecodeImage(url, image_data);
        let _ = self.chan.send(msg);
    }

    /// Shutdown the image cache thread.
    pub fn exit(&self) {
        let (response_chan, response_port) = ipc::channel().unwrap();
        let _ = self.chan.send(ImageCacheCommand::Exit(response_chan));
        let _ = response_port.recv();
    }
}
<|endoftext|>