<commit_before>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn compile_fail(sysroot: &str) {
    let flags = format!("--sysroot {} -Dwarnings", sysroot);
    for_all_targets(sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = "compile-fail".parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from("tests/compile-fail".to_string());
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn run_pass() {
    let mut config = compiletest::default_config();
    config.mode = "run-pass".parse().expect("Invalid mode");
    config.src_base = PathBuf::from("tests/run-pass".to_string());
    config.target_rustcflags = Some("-Dwarnings".to_string());
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn for_all_targets<F: FnMut(String)>(sysroot: &str, mut f: F) {
    for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() {
        let target = target.unwrap();
        if !target.metadata().unwrap().is_dir() {
            continue;
        }
        let target = target.file_name().into_string().unwrap();
        if target == "etc" {
            continue;
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sysroot = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("RUST_SYSROOT")
            .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
            .to_owned(),
    };
    compile_fail(&sysroot);
    run_pass();
    for_all_targets(&sysroot, |target| {
        for file in std::fs::read_dir("tests/run-pass").unwrap() {
            let file = file.unwrap();
            let path = file.path();

            if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") {
                continue;
            }

            let stderr = std::io::stderr();
            write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap();
            let mut cmd = std::process::Command::new("target/debug/miri");
            cmd.arg(path);
            cmd.arg(format!("--target={}", target));
            let libs = Path::new(&sysroot).join("lib");
            let sysroot = libs.join("rustlib").join(&target).join("lib");
            let paths = std::env::join_paths(&[libs, sysroot]).unwrap();
            cmd.env(compiletest::procsrv::dylib_env_var(), paths);

            match cmd.output() {
                Ok(ref output) if output.status.success() => writeln!(stderr.lock(), "ok").unwrap(),
                Ok(output) => {
                    writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap();
                    writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap();
                    writeln!(stderr.lock(), "stderr: \n {}", std::str::from_utf8(&output.stderr).unwrap()).unwrap();
                    panic!("some tests failed");
                }
                Err(e) => {
                    writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
                    panic!("some tests failed");
                },
            }
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "").unwrap();
    });
}
<commit_msg>run all tests found in folder given by MIRI_RUSTC_TEST env var<commit_after>extern crate compiletest_rs as compiletest;

use std::path::{PathBuf, Path};
use std::io::Write;

fn compile_fail(sysroot: &str) {
    let flags = format!("--sysroot {} -Dwarnings", sysroot);
    for_all_targets(sysroot, |target| {
        let mut config = compiletest::default_config();
        config.host_rustcflags = Some(flags.clone());
        config.mode = "compile-fail".parse().expect("Invalid mode");
        config.run_lib_path = Path::new(sysroot).join("lib").join("rustlib").join(&target).join("lib");
        config.rustc_path = "target/debug/miri".into();
        config.src_base = PathBuf::from("tests/compile-fail".to_string());
        config.target = target.to_owned();
        config.target_rustcflags = Some(flags.clone());
        compiletest::run_tests(&config);
    });
}

fn run_pass() {
    let mut config = compiletest::default_config();
    config.mode = "run-pass".parse().expect("Invalid mode");
    config.src_base = PathBuf::from("tests/run-pass".to_string());
    config.target_rustcflags = Some("-Dwarnings".to_string());
    config.host_rustcflags = Some("-Dwarnings".to_string());
    compiletest::run_tests(&config);
}

fn for_all_targets<F: FnMut(String)>(sysroot: &str, mut f: F) {
    for target in std::fs::read_dir(format!("{}/lib/rustlib/", sysroot)).unwrap() {
        let target = target.unwrap();
        if !target.metadata().unwrap().is_dir() {
            continue;
        }
        let target = target.file_name().into_string().unwrap();
        if target == "etc" {
            continue;
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "running tests for target {}", target).unwrap();
        f(target);
    }
}

#[test]
fn compile_test() {
    // Taken from https://github.com/Manishearth/rust-clippy/pull/911.
    let home = option_env!("RUSTUP_HOME").or(option_env!("MULTIRUST_HOME"));
    let toolchain = option_env!("RUSTUP_TOOLCHAIN").or(option_env!("MULTIRUST_TOOLCHAIN"));
    let sysroot = match (home, toolchain) {
        (Some(home), Some(toolchain)) => format!("{}/toolchains/{}", home, toolchain),
        _ => option_env!("RUST_SYSROOT")
            .expect("need to specify RUST_SYSROOT env var or use rustup or multirust")
            .to_owned(),
    };
    compile_fail(&sysroot);
    run_pass();
    for_all_targets(&sysroot, |target| {
        let files = std::fs::read_dir("tests/run-pass").unwrap();
        let files: Box<Iterator<Item=_>> = if let Ok(path) = std::env::var("MIRI_RUSTC_TEST") {
            Box::new(files.chain(std::fs::read_dir(path).unwrap()))
        } else {
            Box::new(files)
        };
        let mut mir_not_found = 0;
        let mut crate_not_found = 0;
        let mut success = 0;
        let mut failed = 0;
        for file in files {
            let file = file.unwrap();
            let path = file.path();

            if !file.metadata().unwrap().is_file() || !path.to_str().unwrap().ends_with(".rs") {
                continue;
            }

            let stderr = std::io::stderr();
            write!(stderr.lock(), "test [miri-pass] {} ... ", path.display()).unwrap();
            let mut cmd = std::process::Command::new("target/debug/miri");
            cmd.arg(path);
            cmd.arg(format!("--target={}", target));
            let libs = Path::new(&sysroot).join("lib");
            let sysroot = libs.join("rustlib").join(&target).join("lib");
            let paths = std::env::join_paths(&[libs, sysroot]).unwrap();
            cmd.env(compiletest::procsrv::dylib_env_var(), paths);

            match cmd.output() {
                Ok(ref output) if output.status.success() => {
                    success += 1;
                    writeln!(stderr.lock(), "ok").unwrap()
                },
                Ok(output) => {
                    let output_err = std::str::from_utf8(&output.stderr).unwrap();
                    if let Some(text) = output_err.splitn(2, "thread 'main' panicked at 'no mir for `").nth(1) {
                        mir_not_found += 1;
                        let end = text.find('`').unwrap();
                        writeln!(stderr.lock(), "NO MIR FOR `{}`", &text[..end]).unwrap();
                    } else if let Some(text) = output_err.splitn(2, "can't find crate for `").nth(1) {
                        crate_not_found += 1;
                        let end = text.find('`').unwrap();
                        writeln!(stderr.lock(), "CAN'T FIND CRATE FOR `{}`", &text[..end]).unwrap();
                    } else {
                        failed += 1;
                        writeln!(stderr.lock(), "FAILED with exit code {:?}", output.status.code()).unwrap();
                        writeln!(stderr.lock(), "stdout: \n {}", std::str::from_utf8(&output.stdout).unwrap()).unwrap();
                        writeln!(stderr.lock(), "stderr: \n {}", output_err).unwrap();
                    }
                }
                Err(e) => {
                    writeln!(stderr.lock(), "FAILED: {}", e).unwrap();
                    panic!("failed to execute miri");
                },
            }
        }
        let stderr = std::io::stderr();
        writeln!(stderr.lock(), "{} success, {} mir not found, {} crate not found, {} failed", success, mir_not_found, crate_not_found, failed).unwrap();
    });
}
<|endoftext|>


<commit_before>extern crate quick_xml;

use std::io::{BufRead};

use quick_xml::{XmlReader, Event, AsStr};
use quick_xml::error::ResultPos;
use std::fmt;

#[test]
fn sample_1_short() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_short.txt"),
        true
    );
}

// #[test]
// fn sample_1_full() {
//     test(
//         include_bytes!("documents/sample_1.xml"),
//         include_bytes!("documents/sample_1_full.txt"),
//         false
//     );
// }

// #[test]
// fn sample_2_short() {
//     test(
//         include_bytes!("documents/sample_2.xml"),
//         include_bytes!("documents/sample_2_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_2_full() {
//     test(
//         include_bytes!("documents/sample_2.xml"),
//         include_bytes!("documents/sample_2_full.txt"),
//         false
//     );
// }

// #[test]
// fn sample_3_short() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_3_full() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_full.txt"),
//         false
//     );
// }

// #[test]
// fn sample_4_short() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_4_full() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_full.txt"),
//         false
//     );
//
// }
//

#[test]
fn eof_1() {
    test(
        br#"<?xml"#,
        br#"Malformed xml: Unescaped XmlDecl event"#,
        true
    );
}

// #[test]
// fn bad_1() {
//     test(
//         br#"<?xml&.,"#,
//         br#"1:6 Unexpected token: <?xml&"#,
//         true
//     );
// }
//
// #[test]
// fn dashes_in_comments() {
//     test(
//         br#"<hello/>"#,
//         br#"
//             |1:14 Unexpected token '--' before ' '
//         "#,
//         false
//     );
//
//     test(
//         br#"<hello/>"#,
//         br#"
//             |1:14 Unexpected token '--' before '-'
//         "#,
//         true
//     );
// }

#[test]
fn tabs_1() {
    test(
        b"\t\t",
        br#"
            StartElement(a)
            StartElement(b)
            EndElement(b)
            EndElement(a)
            EndDocument
        "#,
        true
    );
}
//
// #[test]
// fn issue_83_duplicate_attributes() {
//     test(
//         br#"<hello><some-tag a='10' a="20"></hello>"#,
//         br#"
//             |StartElement(hello)
//             |1:30 Attribute 'a' is redefined
//         "#,
//         true
//     );
// }

#[test]
fn issue_93_large_characters_in_entity_references() {
    test(
        r#"<hello>&𤶼;</hello>"#.as_bytes(),
        r#"
            |StartElement(hello)
            |1:10 Malformed xml: Unexpected entity: 𤶼
            |EndElement(hello)
        "#.as_bytes(),
        true
    )
}

#[test]
fn issue_98_cdata_ending_with_right_bracket() {
    test(
        br#"<hello><![CDATA[Foo [Bar]]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData("Foo [Bar]")
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false
    )
}

#[test]
fn issue_105_unexpected_double_dash() {
    test(
        br#"<hello>-- </hello>"#,
        br#"
            |StartElement(hello)
            |Characters("-- ")
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );

    test(
        br#"<hello>--</hello>"#,
        br#"
            |StartElement(hello)
            |Characters("--")
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );

    test(
        br#"<hello>--></hello>"#,
        br#"
            |StartElement(hello)
            |Characters("-->")
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );

    test(
        br#"<hello><![CDATA[--]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData("--")
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );
}

// #[test]
// fn issue_attribues_have_no_default_namespace () {
//     test(
//         br#"<hello xmlns="urn:foo" x="y"/>"#,
//         br#"
//             |StartDocument(1.0, UTF-8)
//             |StartElement({urn:foo}hello [x="y"])
//             |EndElement({urn:foo}hello)
//             |EndDocument
//         "#,
//         true
//     );
// }

// clones a lot but that's fine
fn convert_to_quick_xml(s: &str) -> String {

    let mut s = match s.trim() {
        ts if ts.starts_with('|') => &ts[1..],
        s => s
    };

    if !s.is_empty() && s.as_bytes()[0] >= b'0' && s.as_bytes()[0] <= b'9' {
        let p = s.chars().position(|c| c == ' ').unwrap();
        s = &s[(p + 1) ..];
    }

    if s.starts_with("Whitespace") {
        format!("Character{}", s)
    } else {
        s.to_owned()
    }
}

fn test(input: &[u8], output: &[u8], is_short: bool) {

    let mut reader = XmlReader::from_reader(input).trim_text(is_short);

    let mut spec_lines = output.lines()
        .map(|line| line.unwrap())
        .enumerate()
        .map(|(i, line)| (i, convert_to_quick_xml(&line)))
        .filter(|&(_, ref line)| !line.trim().is_empty());

    if !is_short {
        reader.next();
    }

    loop {
        let e = reader.next();

        let line = format!("{}", OptEvent(e));

            if let Some((n, spec)) = spec_lines.next() {
                if line != spec {
                    const SPLITTER: &'static str = "-------------------";
                    panic!("\n{}\nUnexpected event at line {}:\nExpected: {}\nFound:    {}\n{}\n",
                           SPLITTER, n + 1, spec, line, SPLITTER);
                }
            } else {
                if line == "EndDocument" {
                    break;
                }
                panic!("Unexpected event: {}", line);
            }
    }
}

struct OptEvent(Option<ResultPos<Event>>);

impl fmt::Display for OptEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Some(Ok(Event::Start(ref e))) => {
                let atts: String = e.attributes().unescaped().map(|a| a.unwrap())
                    .map(|(k, v)| format!("{}={:?}", k.as_str().unwrap(), v.as_str().unwrap()))
                    .collect::<Vec<_>>().join(", ");

                if atts.is_empty() {
                    write!(f, "StartElement({})", e.name().as_str().unwrap())
                } else {
                    write!(f, "StartElement({} [{}])", e.name().as_str().unwrap(), &atts)
                }
            },
            Some(Ok(Event::End(ref e))) =>
                write!(f, "EndElement({})", e.name().as_str().unwrap()),
            Some(Ok(Event::Comment(ref e))) =>
                write!(f, "Comment({:?})", e.content().as_str().unwrap()),
            Some(Ok(Event::CData(ref e))) =>
                write!(f, "CData({:?})", e.content().as_str().unwrap()),
            Some(Ok(Event::DocType(ref e))) =>
                write!(f, "DocType({:?})", e.content().as_str().unwrap()),
            Some(Ok(Event::Text(ref e))) => {
                match e.unescaped_content() {
                    Ok(c) => {
                        if c.is_empty() {
                            write!(f, "Characters()")
                        } else {
                            write!(f, "Characters({:?})", c.as_str().unwrap())
                        }
                    },
                    Err((ref e, _)) => write!(f, "{}", e),
                }
            },
            Some(Ok(Event::Decl(ref e))) => {
                let version = e.version().unwrap().as_str().unwrap();
                let encoding = e.encoding().unwrap().unwrap().as_str().unwrap();
                write!(f, "StartDocument({}, {})", version, encoding)
            },
            None => write!(f, "EndDocument"),
            Some(Ok(Event::PI(ref e))) =>
                write!(f, "ProcessingInstruction({}={:?})",
                    e.name().as_str().unwrap(), e.content().as_str().unwrap()),
            Some(Err((ref e, _))) => write!(f, "{}", e),
        }
    }
}
<commit_msg>use namespaces<commit_after>extern crate quick_xml;

use std::io::{BufRead};

use quick_xml::{XmlReader, Event, AsStr, Element};
use quick_xml::error::ResultPos;
use std::fmt;

#[test]
fn sample_1_short() {
    test(
        include_bytes!("documents/sample_1.xml"),
        include_bytes!("documents/sample_1_short.txt"),
        true
    );
}

// #[test]
// fn sample_1_full() {
//     test(
//         include_bytes!("documents/sample_1.xml"),
//         include_bytes!("documents/sample_1_full.txt"),
//         false
//     );
// }

#[test]
fn sample_2_short() {
    test(
        include_bytes!("documents/sample_2.xml"),
        include_bytes!("documents/sample_2_short.txt"),
        true
    );
}

// #[test]
// fn sample_2_full() {
//     test(
//         include_bytes!("documents/sample_2.xml"),
//         include_bytes!("documents/sample_2_full.txt"),
//         false
//     );
// }

// #[test]
// fn sample_3_short() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_3_full() {
//     test(
//         include_bytes!("documents/sample_3.xml"),
//         include_bytes!("documents/sample_3_full.txt"),
//         false
//     );
// }

// #[test]
// fn sample_4_short() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_short.txt"),
//         true
//     );
// }

// #[test]
// fn sample_4_full() {
//     test(
//         include_bytes!("documents/sample_4.xml"),
//         include_bytes!("documents/sample_4_full.txt"),
//         false
//     );
//
// }
//

#[test]
fn eof_1() {
    test(
        br#"<?xml"#,
        br#"Malformed xml: Unescaped XmlDecl event"#,
        true
    );
}

// #[test]
// fn bad_1() {
//     test(
//         br#"<?xml&.,"#,
//         br#"1:6 Unexpected token: <?xml&"#,
//         true
//     );
// }
//
// #[test]
// fn dashes_in_comments() {
//     test(
//         br#"<hello/>"#,
//         br#"
//             |1:14 Unexpected token '--' before ' '
//         "#,
//         false
//     );
//
//     test(
//         br#"<hello/>"#,
//         br#"
//             |1:14 Unexpected token '--' before '-'
//         "#,
//         true
//     );
// }

#[test]
fn tabs_1() {
    test(
        b"\t\t",
        br#"
            StartElement(a)
            StartElement(b)
            EndElement(b)
            EndElement(a)
            EndDocument
        "#,
        true
    );
}
//
// #[test]
// fn issue_83_duplicate_attributes() {
//     test(
//         br#"<hello><some-tag a='10' a="20"></hello>"#,
//         br#"
//             |StartElement(hello)
//             |1:30 Attribute 'a' is redefined
//         "#,
//         true
//     );
// }

#[test]
fn issue_93_large_characters_in_entity_references() {
    test(
        r#"<hello>&𤶼;</hello>"#.as_bytes(),
        r#"
            |StartElement(hello)
            |1:10 Malformed xml: Unexpected entity: 𤶼
            |EndElement(hello)
        "#.as_bytes(),
        true
    )
}

#[test]
fn issue_98_cdata_ending_with_right_bracket() {
    test(
        br#"<hello><![CDATA[Foo [Bar]]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData("Foo [Bar]")
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false
    )
}

#[test]
fn issue_105_unexpected_double_dash() {
    test(
        br#"<hello>-- </hello>"#,
        br#"
            |StartElement(hello)
            |Characters("-- ")
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );

    test(
        br#"<hello>--</hello>"#,
        br#"
            |StartElement(hello)
            |Characters("--")
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );

    test(
        br#"<hello>--></hello>"#,
        br#"
            |StartElement(hello)
            |Characters("-->")
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );

    test(
        br#"<hello><![CDATA[--]]></hello>"#,
        br#"
            |StartElement(hello)
            |Characters()
            |CData("--")
            |Characters()
            |EndElement(hello)
            |EndDocument
        "#,
        false
    );
}

// #[test]
// fn issue_attribues_have_no_default_namespace () {
//     test(
//         br#"<hello xmlns="urn:foo" x="y"/>"#,
//         br#"
//             |StartDocument(1.0, UTF-8)
//             |StartElement({urn:foo}hello [x="y"])
//             |EndElement({urn:foo}hello)
//             |EndDocument
//         "#,
//         true
//     );
// }

// clones a lot but that's fine
fn convert_to_quick_xml(s: &str) -> String {

    let mut s = match s.trim() {
        ts if ts.starts_with('|') => &ts[1..],
        s => s
    };

    if !s.is_empty() && s.as_bytes()[0] >= b'0' && s.as_bytes()[0] <= b'9' {
        let p = s.chars().position(|c| c == ' ').unwrap();
        s = &s[(p + 1) ..];
    }

    if s.starts_with("Whitespace") {
        format!("Character{}", s)
    } else {
        s.to_owned()
    }
}

fn test(input: &[u8], output: &[u8], is_short: bool) {

    let mut reader = XmlReader::from_reader(input).trim_text(is_short)
        .namespaced();

    let mut spec_lines = output.lines()
        .map(|line| line.unwrap())
        .enumerate()
        .map(|(i, line)| (i, convert_to_quick_xml(&line)))
        .filter(|&(_, ref line)| !line.trim().is_empty());

    if !is_short {
        reader.next();
    }

    loop {
        let e = reader.next();

        let line = format!("{}", OptEvent(e));

            if let Some((n, spec)) = spec_lines.next() {
                if line != spec {
                    const SPLITTER: &'static str = "-------------------";
                    panic!("\n{}\nUnexpected event at line {}:\nExpected: {}\nFound:    {}\n{}\n",
                           SPLITTER, n + 1, spec, line, SPLITTER);
                }
            } else {
                if line == "EndDocument" {
                    break;
                }
                panic!("Unexpected event: {}", line);
            }
    }
}

fn namespace_name(n: &Option<Vec<u8>>, e: &Element) -> String {
    match n {
        &Some(ref n) =>
            format!("{{{}}}{}", n.as_str().unwrap(), e.name().as_str().unwrap()),
        &None => e.name().as_str().unwrap().to_owned(),
    }
}

struct OptEvent(Option<ResultPos<(Option<Vec<u8>>, Event)>>);

impl fmt::Display for OptEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self.0 {
            Some(Ok((ref n, Event::Start(ref e)))) => {
                let atts: String = e.attributes().unescaped().map(|a| a.unwrap())
                    .filter(|&(k, _)| k.len() < 5 || &k[..5] != b"xmlns")
                    .map(|(k, v)| format!("{}={:?}", k.as_str().unwrap(), v.as_str().unwrap()))
                    .collect::<Vec<_>>().join(", ");

                let name = namespace_name(n, e);

                if atts.is_empty() {
                    write!(f, "StartElement({})", &name)
                } else {
                    write!(f, "StartElement({} [{}])", &name, &atts)
                }
            },
            Some(Ok((ref n, Event::End(ref e)))) =>
                write!(f, "EndElement({})", namespace_name(n, e)),
            Some(Ok((ref n, Event::Comment(ref e)))) =>
                write!(f, "Comment({:?})", e.content().as_str().unwrap()),
            Some(Ok((ref n, Event::CData(ref e)))) =>
                write!(f, "CData({:?})", e.content().as_str().unwrap()),
            Some(Ok((ref n, Event::DocType(ref e)))) =>
                write!(f, "DocType({:?})", e.content().as_str().unwrap()),
            Some(Ok((ref n, Event::Text(ref e)))) => {
                match e.unescaped_content() {
                    Ok(c) => {
                        if c.is_empty() {
                            write!(f, "Characters()")
                        } else {
                            write!(f, "Characters({:?})", c.as_str().unwrap())
                        }
                    },
                    Err((ref e, _)) => write!(f, "{}", e),
                }
            },
            Some(Ok((ref n, Event::Decl(ref e)))) => {
                let version = e.version().unwrap().as_str().unwrap();
                let encoding = e.encoding().unwrap().unwrap().as_str().unwrap();
                write!(f, "StartDocument({}, {})", version, encoding)
            },
            None => write!(f, "EndDocument"),
            Some(Ok((ref n, Event::PI(ref e)))) =>
                write!(f, "ProcessingInstruction({}={:?})",
                    e.name().as_str().unwrap(), e.content().as_str().unwrap()),
            Some(Err((ref e, _))) => write!(f, "{}", e),
        }
    }
}
<|endoftext|>


<commit_before>#![deny(clippy::all, clippy::pedantic)]

use std::error::Error as StdError;
use std::io;
use thiserror::Error;

#[derive(Error, Debug)]
#[error("implicit source")]
pub struct ImplicitSource {
    source: io::Error,
}

#[derive(Error, Debug)]
#[error("explicit source")]
pub struct ExplicitSource {
    source: String,
    #[source]
    io: io::Error,
}

#[derive(Error, Debug)]
#[error("boxed source")]
pub struct BoxedSource {
    #[source]
    source: Box<dyn StdError + Send + 'static>,
}

#[test]
fn test_implicit_source() {
    let io = io::Error::new(io::ErrorKind::Other, "oh no!");
    let error = ImplicitSource { source: io };
    error.source().unwrap().downcast_ref::<io::Error>().unwrap();
}

#[test]
fn test_explicit_source() {
    let io = io::Error::new(io::ErrorKind::Other, "oh no!");
    let error = ExplicitSource {
        source: String::new(),
        io,
    };
    error.source().unwrap().downcast_ref::<io::Error>().unwrap();
}

#[test]
fn test_boxed_source() {
    let source = Box::new(io::Error::new(io::ErrorKind::Other, "oh no!"));
    let error = BoxedSource { source };
    error.source().unwrap().downcast_ref::<io::Error>().unwrap();
}

macro_rules! error_from_macro {
    ($($variants:tt)*) => {
        #[derive(Error)]
        #[derive(Debug)]
        pub enum MacroSource {
            $($variants)*
        }
    }
}

// Test that we generate impls with the proper hygiene
error_from_macro!(#[error("Something")] Variant(#[from] io::Error));
<commit_msg>Adjust macro hygiene test formatting<commit_after>#![deny(clippy::all, clippy::pedantic)]

use std::error::Error as StdError;
use std::io;
use thiserror::Error;

#[derive(Error, Debug)]
#[error("implicit source")]
pub struct ImplicitSource {
    source: io::Error,
}

#[derive(Error, Debug)]
#[error("explicit source")]
pub struct ExplicitSource {
    source: String,
    #[source]
    io: io::Error,
}

#[derive(Error, Debug)]
#[error("boxed source")]
pub struct BoxedSource {
    #[source]
    source: Box<dyn StdError + Send + 'static>,
}

#[test]
fn test_implicit_source() {
    let io = io::Error::new(io::ErrorKind::Other, "oh no!");
    let error = ImplicitSource { source: io };
    error.source().unwrap().downcast_ref::<io::Error>().unwrap();
}

#[test]
fn test_explicit_source() {
    let io = io::Error::new(io::ErrorKind::Other, "oh no!");
    let error = ExplicitSource {
        source: String::new(),
        io,
    };
    error.source().unwrap().downcast_ref::<io::Error>().unwrap();
}

#[test]
fn test_boxed_source() {
    let source = Box::new(io::Error::new(io::ErrorKind::Other, "oh no!"));
    let error = BoxedSource { source };
    error.source().unwrap().downcast_ref::<io::Error>().unwrap();
}

macro_rules! error_from_macro {
    ($($variants:tt)*) => {
        #[derive(Error)]
        #[derive(Debug)]
        pub enum MacroSource {
            $($variants)*
        }
    }
}

// Test that we generate impls with the proper hygiene
#[rustfmt::skip]
error_from_macro! {
    #[error("Something")]
    Variant(#[from] io::Error)
}
<|endoftext|>


<commit_before>mod thread_guard;

use std::fmt;
use std::io;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, Instant};
use std::ops::Deref;

use serial::prelude::*;
use serial;
use self::thread_guard::*;
use super::Packet;

pub enum ConnectionEvent {
    Packet(Packet),
    InvalidPacket(Packet),
    ReadError(io::Error),
    WriteError(io::Error),
    Heartbeat
}

impl fmt::Display for ConnectionEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionEvent::Packet(ref p) => write!(f, "{}", p),
            &ConnectionEvent::InvalidPacket(ref p) => write!(f, "Invalid! {}", p),
            &ConnectionEvent::ReadError(ref err) => write!(f, "READ ERROR! [{:?}]", err),
            &ConnectionEvent::WriteError(ref err) => write!(f, "WRITE ERROR! [{:?}]", err),
            &ConnectionEvent::Heartbeat => write!(f, "Tick Tock")
        }
    }
}

pub struct Connection {
    pub tty_path: String,
    event_receiver: Receiver<ConnectionEvent>,
    packet_sender: Sender<Packet>,
    kill_thread_signal: Arc<Mutex<bool>>,
    reader_thread_handle: Option<ThreadHandle<()>>,
}

impl Connection {
    pub fn new(tty_path: &str, heartbeat_milliseconds: Option<u64>) -> io::Result<Connection> {
        let (event_sender, event_receiver) = channel::<ConnectionEvent>();
        let (packet_sender, packet_receiver) = channel::<Packet>();

        let kill_signal = Arc::new(Mutex::new(false));

        let reader_thread = build_connection_read_thread(tty_path.to_string(), event_sender, packet_receiver, heartbeat_milliseconds, kill_signal.clone());

        Ok(Connection {
            tty_path: tty_path.to_string(),
            event_receiver: event_receiver,
            packet_sender: packet_sender,
            kill_thread_signal: kill_signal,
            reader_thread_handle: Some(reader_thread)
        })
    }

    pub fn send(&mut self, p: Packet) -> io::Result<()> {
        match self.is_ok() {
            true => {
                self.packet_sender.send(p).unwrap();
                Ok(())
            },
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped"))
        }
    }

    pub fn wait(&self) -> io::Result<ConnectionEvent> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                Ok(self.event_receiver.recv().unwrap())
            }
        }
    }

    pub fn get_events(&self) -> io::Result<Vec<ConnectionEvent>> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                let mut data: Vec<ConnectionEvent> = vec!();

                loop {
                    match self.event_receiver.try_recv() {
                        Ok(evt) => { data.push(evt) },
                        Err(TryRecvError::Empty) => { break; },
                        Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
                    }
                }

                Ok(data)
            }
        }
    }

    pub fn is_ok(&self) -> bool {
        let mut ret = true;

        match &self.reader_thread_handle {
            &Some(ref h) => {
                ret = ret && match h.status.lock().unwrap().deref() {
                    &ThreadStatus::Ok => true,
                    _ => false
                }
            },
            _ => {}
        }
        ret
    }
}

impl Drop for Connection {
    fn drop(&mut self) {

        {
            *self.kill_thread_signal.lock().unwrap() = true;
        }

        match self.reader_thread_handle.take() {
            Some(th) => {
                th.handle.join().unwrap();
            },
            None => {}
        }
    }
}

fn build_connection_read_thread(tty_path: String, event_sender: Sender<ConnectionEvent>, packet_receiver: Receiver<Packet>, heartbeat: Option<u64>, kill_signal: Arc<Mutex<bool>>) -> ThreadHandle<()> {
    guard_thread("reader_thread", move || {
        let mut serial = match serial::open(&tty_path) {
            Err(e) => {panic!(format!("Unable to create serial port: {}", e)) },
            Ok(s) => s
        };

        match serial.set_timeout(Duration::from_millis(500)) {
            Err(e) => { panic!(format!("Unable to set serial timeout: {}", e)) },
            Ok(_) => {}
        };

        let mut packet_buffer: Vec<u8> = Vec::new();
        let mut read_buffer: Vec<u8> = Vec::new();
        let mut last_read = Instant::now();
        let mut last_heartbeat = Instant::now();

        while !*kill_signal.lock().unwrap() {

            if packet_buffer.len() > 0 && last_read.elapsed().as_secs() > 2 {
                let evt = ConnectionEvent::ReadError(io::Error::new(io::ErrorKind::InvalidData, format!("Invalid packet data: {:?}", packet_buffer.clone())));
                event_sender.send(evt).unwrap();
                packet_buffer.clear();
            };

            match packet_receiver.try_recv() {
                Ok(p) => {
                    println!("going to write...");
                    match serial.write_all(&p.data) {
                        Ok(_) => {},
                        Err(e) => {
                            let evt = ConnectionEvent::WriteError(e);
                            event_sender.send(evt).unwrap();
                        }
                    }
                },
                Err(TryRecvError::Empty) => {  },
                Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
            }

            match serial.read(&mut read_buffer) {
                Err(e) if e.kind() == io::ErrorKind::Timeout => {},
                Err(e) => {
                    let evt = ConnectionEvent::ReadError(e);
                    event_sender.send(evt).unwrap();
                },
                Ok(bytes) if bytes > 0 => {
                    last_read = Instant::now();
                    for x in 0 .. bytes {
                        packet_buffer.push(read_buffer[x]);
                    }
                },
                Ok(_) => {} // do nothing for 0 bytes read
            };

            while packet_buffer.len() >= 128 {
                let data: Vec<u8> = packet_buffer.drain(0..128).collect();
                let p = Packet::from_bytes(&data);

                if p.is_checksum_valid() {
                    let evt = ConnectionEvent::Packet(p);
                    event_sender.send(evt).unwrap();
                } else {
                    let evt = ConnectionEvent::InvalidPacket(p);
                    event_sender.send(evt).unwrap();
                }
            }

            match heartbeat {
                Some(ms) => {
                    if last_heartbeat.elapsed() >= Duration::from_millis(ms) {
                        let evt = ConnectionEvent::Heartbeat;
                        event_sender.send(evt).unwrap();
                        last_heartbeat = Instant::now();
                    }
                },
                None => {}
            }

            thread::sleep(Duration::from_millis(250));
        }
        ()
    })
}
<commit_msg>debug<commit_after>mod thread_guard;

use std::fmt;
use std::io;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::{channel, Sender, Receiver, TryRecvError};
use std::thread;
use std::time::{Duration, Instant};
use std::ops::Deref;

use serial::prelude::*;
use serial;
use self::thread_guard::*;
use super::Packet;

pub enum ConnectionEvent {
    Packet(Packet),
    InvalidPacket(Packet),
    ReadError(io::Error),
    WriteError(io::Error),
    Heartbeat
}

impl fmt::Display for ConnectionEvent {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            &ConnectionEvent::Packet(ref p) => write!(f, "{}", p),
            &ConnectionEvent::InvalidPacket(ref p) => write!(f, "Invalid! {}", p),
            &ConnectionEvent::ReadError(ref err) => write!(f, "READ ERROR! [{:?}]", err),
            &ConnectionEvent::WriteError(ref err) => write!(f, "WRITE ERROR! [{:?}]", err),
            &ConnectionEvent::Heartbeat => write!(f, "Tick Tock")
        }
    }
}

pub struct Connection {
    pub tty_path: String,
    event_receiver: Receiver<ConnectionEvent>,
    packet_sender: Sender<Packet>,
    kill_thread_signal: Arc<Mutex<bool>>,
    reader_thread_handle: Option<ThreadHandle<()>>,
}

impl Connection {
    pub fn new(tty_path: &str, heartbeat_milliseconds: Option<u64>) -> io::Result<Connection> {
        let (event_sender, event_receiver) = channel::<ConnectionEvent>();
        let (packet_sender, packet_receiver) = channel::<Packet>();

        let kill_signal = Arc::new(Mutex::new(false));

        let reader_thread = build_connection_read_thread(tty_path.to_string(), event_sender, packet_receiver, heartbeat_milliseconds, kill_signal.clone());

        Ok(Connection {
            tty_path: tty_path.to_string(),
            event_receiver: event_receiver,
            packet_sender: packet_sender,
            kill_thread_signal: kill_signal,
            reader_thread_handle: Some(reader_thread)
        })
    }

    pub fn send(&mut self, p: Packet) -> io::Result<()> {
        match self.is_ok() {
            true => {
                self.packet_sender.send(p).unwrap();
                Ok(())
            },
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped"))
        }
    }

    pub fn wait(&self) -> io::Result<ConnectionEvent> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                Ok(self.event_receiver.recv().unwrap())
            }
        }
    }

    pub fn get_events(&self) -> io::Result<Vec<ConnectionEvent>> {
        match self.is_ok() {
            false => Err(io::Error::new(io::ErrorKind::ConnectionAborted, "worker threads have stopped")),
            true => {
                let mut data: Vec<ConnectionEvent> = vec!();

                loop {
                    match self.event_receiver.try_recv() {
                        Ok(evt) => { data.push(evt) },
                        Err(TryRecvError::Empty) => { break; },
                        Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
                    }
                }

                Ok(data)
            }
        }
    }

    pub fn is_ok(&self) -> bool {
        let mut ret = true;

        match &self.reader_thread_handle {
            &Some(ref h) => {
                ret = ret && match h.status.lock().unwrap().deref() {
                    &ThreadStatus::Ok => true,
                    _ => false
                }
            },
            _ => {}
        }
        ret
    }
}

impl Drop for Connection {
    fn drop(&mut self) {

        {
            *self.kill_thread_signal.lock().unwrap() = true;
        }

        match self.reader_thread_handle.take() {
            Some(th) => {
                th.handle.join().unwrap();
            },
            None => {}
        }
    }
}

fn build_connection_read_thread(tty_path: String, event_sender: Sender<ConnectionEvent>, packet_receiver: Receiver<Packet>, heartbeat: Option<u64>, kill_signal: Arc<Mutex<bool>>) -> ThreadHandle<()> {
    guard_thread("reader_thread", move || {
        let mut serial = match serial::open(&tty_path) {
            Err(e) => {panic!(format!("Unable to create serial port: {}", e)) },
            Ok(s) => s
        };

        match serial.set_timeout(Duration::from_millis(500)) {
            Err(e) => { panic!(format!("Unable to set serial timeout: {}", e)) },
            Ok(_) => {}
        };

        let mut packet_buffer: Vec<u8> = Vec::new();
        let mut read_buffer: Vec<u8> = Vec::new();
        let mut last_read = Instant::now();
        let mut last_heartbeat = Instant::now();

        while !*kill_signal.lock().unwrap() {

            if packet_buffer.len() > 0 && last_read.elapsed().as_secs() > 2 {
                let evt = ConnectionEvent::ReadError(io::Error::new(io::ErrorKind::InvalidData, format!("Invalid packet data: {:?}", packet_buffer.clone())));
                event_sender.send(evt).unwrap();
                packet_buffer.clear();
            };

            match packet_receiver.try_recv() {
                Ok(p) => {
                    println!("going to write...");
                    match serial.write_all(&p.data) {
                        Ok(_) => {},
                        Err(e) => {
                            let evt = ConnectionEvent::WriteError(e);
                            event_sender.send(evt).unwrap();
                        }
                    }
                },
                Err(TryRecvError::Empty) => {  },
                Err(TryRecvError::Disconnected) => { panic!("channels should not disconnect") }
            }

            match serial.read(&mut read_buffer) {
                Err(e) if e.kind() == io::ErrorKind::TimedOut => {},
                Err(e) => {
                    let evt = ConnectionEvent::ReadError(e);
                    event_sender.send(evt).unwrap();
                },
                Ok(bytes) if bytes > 0 => {
                    last_read = Instant::now();
                    for x in 0 .. bytes {
                        packet_buffer.push(read_buffer[x]);
                    }
                },
                Ok(_) => {} // do nothing for 0 bytes read
            };

            while packet_buffer.len() >= 128 {
                let data: Vec<u8> = packet_buffer.drain(0..128).collect();
                let p = Packet::from_bytes(&data);

                if p.is_checksum_valid() {
                    let evt = ConnectionEvent::Packet(p);
                    event_sender.send(evt).unwrap();
                } else {
                    let evt = ConnectionEvent::InvalidPacket(p);
                    event_sender.send(evt).unwrap();
                }
            }

            match heartbeat {
                Some(ms) => {
                    if last_heartbeat.elapsed() >= Duration::from_millis(ms) {
                        let evt = ConnectionEvent::Heartbeat;
                        event_sender.send(evt).unwrap();
                        last_heartbeat = Instant::now();
                    }
                },
                None => {}
            }

            thread::sleep(Duration::from_millis(250));
        }
        ()
    })
}
<|endoftext|>


<commit_before>#![feature(core)]
#![feature(unboxed_closures)]

extern crate lua52_sys as ffi;
extern crate libc;

use std::ffi::{CStr, CString};
use std::io::Read;
use std::io::Error as IoError;
use std::borrow::Borrow;
use std::marker::PhantomData;

pub use functions_read::LuaFunction;
pub use functions_write::{function, InsideCallback};
pub use lua_tables::LuaTable;

pub mod any;
pub mod functions_read;
pub mod lua_tables;
pub mod userdata;

mod functions_write;
mod macros;
mod rust_tables;
mod values;
mod tuples;

/// Main object of the library.
///
/// The lifetime parameter corresponds to the lifetime of the content of the Lua context.
pub struct Lua<'lua> {
    lua: LuaContext,
    must_be_closed: bool,
    marker: PhantomData<&'lua ()>,
}

/// RAII guard for a value pushed on the stack.
pub struct PushGuard<L> where L: AsMutLua {
    lua: L,
    size: i32,
}

impl<L> PushGuard<L> where L: AsMutLua {
    /// Prevents the value from being poped when the `PushGuard` is destroyed, and returns the
    /// number of elements on the stack.
    fn forget(mut self) -> i32 {
        let size = self.size;
        self.size = 0;
        size
    }
}

/// Trait for objects that have access to a Lua context. When using a context returned by a
/// `AsLua`, you are not allowed to modify the stack.
pub unsafe trait AsLua {
    fn as_lua(&self) -> LuaContext;
}

/// Trait for objects that have access to a Lua context. You are allowed to modify the stack, but
/// it must be in the same state as it was when you started.
pub unsafe trait AsMutLua: AsLua {
    fn as_mut_lua(&mut self) -> LuaContext;
}

/// Opaque type that contains the raw Lua context.
#[derive(Copy, Clone)]
#[allow(raw_pointer_derive)]
pub struct LuaContext(*mut ffi::lua_State);
unsafe impl Send for LuaContext {}

unsafe impl<'a, 'lua> AsLua for &'a Lua<'lua> {
    fn as_lua(&self) -> LuaContext {
        self.lua
    }
}

unsafe impl<'a, 'lua> AsLua for &'a mut Lua<'lua> {
    fn as_lua(&self) -> LuaContext {
        self.lua
    }
}

unsafe impl<'a, 'lua> AsMutLua for &'a mut Lua<'lua> {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.lua
    }
}

unsafe impl<L> AsLua for PushGuard<L> where L: AsMutLua {
    fn as_lua(&self) -> LuaContext {
        self.lua.as_lua()
    }
}

unsafe impl<L> AsMutLua for PushGuard<L> where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.lua.as_mut_lua()
    }
}

unsafe impl<'a, L> AsLua for &'a L where L: AsLua {
    fn as_lua(&self) -> LuaContext {
        (**self).as_lua()
    }
}

unsafe impl<'a, L> AsLua for &'a mut L where L: AsLua {
    fn as_lua(&self) -> LuaContext {
        (**self).as_lua()
    }
}

unsafe impl<'a, L> AsMutLua for &'a mut L where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        (**self).as_mut_lua()
    }
}

/// Types that can be given to a Lua context, for example with `lua.set()` or as a return value
/// of a function.
pub trait Push<L> where L: AsMutLua {
    /// Pushes the value on the top of the stack.
    ///
    /// Must return a guard representing the elements that have been pushed.
    ///
    /// You can implement this for any type you want by redirecting to call to
    /// another implementation (for example `5.push_to_lua`) or by calling
    /// `userdata::push_userdata`.
    fn push_to_lua(self, lua: L) -> PushGuard<L>;
}

/// Types that can be obtained from a Lua context.
///
/// Most types that implement `Push` also implement `LuaRead`, but this is not always the case
/// (for example `&'static str` implements `Push` but not `LuaRead`).
pub trait LuaRead<L>: Sized where L: AsLua {
    /// Reads the data from Lua.
    fn lua_read(lua: L) -> Result<Self, L> {
        LuaRead::lua_read_at_position(lua, -1)
    }

    /// Reads the data from Lua at a given position.
    fn lua_read_at_position(lua: L, index: i32) -> Result<Self, L>;
}

/// Error that can happen when executing Lua code.
#[derive(Debug)]
pub enum LuaError {
    /// There was a syntax error when parsing the Lua code.
    SyntaxError(String),

    /// There was an error during execution of the Lua code
    /// (for example not enough parameters for a function call).
    ExecutionError(String),

    /// There was an IoError while reading the source code to execute.
    ReadError(IoError),

    /// The call to `execute` has requested the wrong type of data.
    WrongType,
}

impl<'lua> Lua<'lua> {
    /// Builds a new Lua context.
    ///
    /// # Panic
    ///
    /// The function panics if the underlying call to `lua_newstate` fails
    /// (which indicates lack of memory).
    pub fn new() -> Lua<'lua> {
        let lua = unsafe { ffi::lua_newstate(alloc, std::ptr::null_mut()) };
        if lua.is_null() {
            panic!("lua_newstate failed");
        }

        // this alloc function is required to create a lua state.
        extern "C" fn alloc(_ud: *mut libc::c_void, ptr: *mut libc::c_void, _osize: libc::size_t,
                            nsize: libc::size_t) -> *mut libc::c_void
        {
            unsafe {
                if nsize == 0 {
                    libc::free(ptr as *mut libc::c_void);
                    std::ptr::null_mut()
                } else {
                    libc::realloc(ptr, nsize)
                }
            }
        }

        // called whenever lua encounters an unexpected error.
        extern "C" fn panic(lua: *mut ffi::lua_State) -> libc::c_int {
            let err = unsafe { ffi::lua_tostring(lua, -1) };
            let err = unsafe { CStr::from_ptr(err) };
            let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
            panic!("PANIC: unprotected error in call to Lua API ({})\n", err);
        }

        unsafe { ffi::lua_atpanic(lua, panic) };

        Lua {
            lua: LuaContext(lua),
            must_be_closed: true,
            marker: PhantomData,
        }
    }

    /// Takes an existing `lua_State` and build a Lua object from it.
    ///
    /// # Arguments
    ///
    ///  * `close_at_the_end`: if true, lua_close will be called on the lua_State on the destructor
    pub unsafe fn from_existing_state<T>(lua: *mut T, close_at_the_end: bool) -> Lua<'lua> {
        Lua {
            lua: std::mem::transmute(lua),
            must_be_closed: close_at_the_end,
            marker: PhantomData,
        }
    }

    /// Opens all standard Lua libraries.
    /// This is done by calling `luaL_openlibs`.
    pub fn openlibs(&mut self) {
        unsafe { ffi::luaL_openlibs(self.lua.0) }
    }

    /// Executes some Lua code on the context.
    pub fn execute<'a, T>(&'a mut self, code: &str) -> Result<T, LuaError>
                          where T: for<'g> LuaRead<&'g mut PushGuard<&'a mut Lua<'lua>>> +
                                   for<'g> LuaRead<PushGuard<&'g mut PushGuard<&'a mut Lua<'lua>>>>
    {
        let mut f = try!(functions_read::LuaFunction::load(self, code));
        f.call()
    }

    /// Executes some Lua code on the context.
    pub fn execute_from_reader<'a, T, R>(&'a mut self, code: R) -> Result<T, LuaError>
            where T: for<'g> LuaRead<&'g mut PushGuard<&'a mut Lua<'lua>>> +
                     for<'g> LuaRead<PushGuard<&'g mut PushGuard<&'a mut Lua<'lua>>>>,
                  R: Read
    {
        let mut f = try!(functions_read::LuaFunction::load_from_reader(self, code));
        f.call()
    }

    /// Reads the value of a global variable.
    pub fn get<'l, V, I>(&'l mut self, index: I) -> Option<V>
                         where I: Borrow<str>, V: LuaRead<PushGuard<&'l mut Lua<'lua>>>
    {
        let index = CString::new(index.borrow()).unwrap();
        unsafe { ffi::lua_getglobal(self.lua.0, index.as_ptr()); }
        let guard = PushGuard { lua: self, size: 1 };
        LuaRead::lua_read(guard).ok()
    }

    /// Modifies the value of a global variable.
    pub fn set<'a, I, V>(&'a mut self, index: I, value: V)
                         where I: Borrow<str>, V: Push<&'a mut Lua<'lua>>
    {
        let index = CString::new(index.borrow()).unwrap();
        value.push_to_lua(self).forget();
        unsafe { ffi::lua_setglobal(self.lua.0, index.as_ptr()); }
    }

    /// Inserts an empty array, then loads it.
    pub fn empty_array<'a, I>(&'a mut self, index: I) -> LuaTable<PushGuard<&'a mut Lua<'lua>>>
                              where I: Borrow<str>
    {
        // TODO: cleaner implementation
        let mut me = self;
        let index2 = CString::new(index.borrow()).unwrap();
        Vec::<u8>::with_capacity(0).push_to_lua(&mut me).forget();
        unsafe { ffi::lua_setglobal(me.lua.0, index2.as_ptr()); }

        me.get(index).unwrap()
    }
}

impl<'lua> Drop for Lua<'lua> {
    fn drop(&mut self) {
        if self.must_be_closed {
            unsafe { ffi::lua_close(self.lua.0) }
        }
    }
}

impl<L> Drop for PushGuard<L> where L: AsMutLua {
    fn drop(&mut self) {
        if self.size != 0 {
            unsafe { ffi::lua_pop(self.lua.as_mut_lua().0, self.size); }
        }
    }
}
<commit_msg>Added #![feature(reflect_marker)]<commit_after>#![feature(core)]
#![feature(reflect_marker)]
#![feature(unboxed_closures)]

extern crate lua52_sys as ffi;
extern crate libc;

use std::ffi::{CStr, CString};
use std::io::Read;
use std::io::Error as IoError;
use std::borrow::Borrow;
use std::marker::PhantomData;

pub use functions_read::LuaFunction;
pub use functions_write::{function, InsideCallback};
pub use lua_tables::LuaTable;

pub mod any;
pub mod functions_read;
pub mod lua_tables;
pub mod userdata;

mod functions_write;
mod macros;
mod rust_tables;
mod values;
mod tuples;

/// Main object of the library.
///
/// The lifetime parameter corresponds to the lifetime of the content of the Lua context.
pub struct Lua<'lua> {
    lua: LuaContext,
    must_be_closed: bool,
    marker: PhantomData<&'lua ()>,
}

/// RAII guard for a value pushed on the stack.
pub struct PushGuard<L> where L: AsMutLua {
    lua: L,
    size: i32,
}

impl<L> PushGuard<L> where L: AsMutLua {
    /// Prevents the value from being poped when the `PushGuard` is destroyed, and returns the
    /// number of elements on the stack.
    fn forget(mut self) -> i32 {
        let size = self.size;
        self.size = 0;
        size
    }
}

/// Trait for objects that have access to a Lua context. When using a context returned by a
/// `AsLua`, you are not allowed to modify the stack.
pub unsafe trait AsLua {
    fn as_lua(&self) -> LuaContext;
}

/// Trait for objects that have access to a Lua context. You are allowed to modify the stack, but
/// it must be in the same state as it was when you started.
pub unsafe trait AsMutLua: AsLua {
    fn as_mut_lua(&mut self) -> LuaContext;
}

/// Opaque type that contains the raw Lua context.
#[derive(Copy, Clone)]
#[allow(raw_pointer_derive)]
pub struct LuaContext(*mut ffi::lua_State);
unsafe impl Send for LuaContext {}

unsafe impl<'a, 'lua> AsLua for &'a Lua<'lua> {
    fn as_lua(&self) -> LuaContext {
        self.lua
    }
}

unsafe impl<'a, 'lua> AsLua for &'a mut Lua<'lua> {
    fn as_lua(&self) -> LuaContext {
        self.lua
    }
}

unsafe impl<'a, 'lua> AsMutLua for &'a mut Lua<'lua> {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.lua
    }
}

unsafe impl<L> AsLua for PushGuard<L> where L: AsMutLua {
    fn as_lua(&self) -> LuaContext {
        self.lua.as_lua()
    }
}

unsafe impl<L> AsMutLua for PushGuard<L> where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        self.lua.as_mut_lua()
    }
}

unsafe impl<'a, L> AsLua for &'a L where L: AsLua {
    fn as_lua(&self) -> LuaContext {
        (**self).as_lua()
    }
}

unsafe impl<'a, L> AsLua for &'a mut L where L: AsLua {
    fn as_lua(&self) -> LuaContext {
        (**self).as_lua()
    }
}

unsafe impl<'a, L> AsMutLua for &'a mut L where L: AsMutLua {
    fn as_mut_lua(&mut self) -> LuaContext {
        (**self).as_mut_lua()
    }
}

/// Types that can be given to a Lua context, for example with `lua.set()` or as a return value
/// of a function.
pub trait Push<L> where L: AsMutLua {
    /// Pushes the value on the top of the stack.
    ///
    /// Must return a guard representing the elements that have been pushed.
    ///
    /// You can implement this for any type you want by redirecting to call to
    /// another implementation (for example `5.push_to_lua`) or by calling
    /// `userdata::push_userdata`.
    fn push_to_lua(self, lua: L) -> PushGuard<L>;
}

/// Types that can be obtained from a Lua context.
///
/// Most types that implement `Push` also implement `LuaRead`, but this is not always the case
/// (for example `&'static str` implements `Push` but not `LuaRead`).
pub trait LuaRead<L>: Sized where L: AsLua {
    /// Reads the data from Lua.
    fn lua_read(lua: L) -> Result<Self, L> {
        LuaRead::lua_read_at_position(lua, -1)
    }

    /// Reads the data from Lua at a given position.
    fn lua_read_at_position(lua: L, index: i32) -> Result<Self, L>;
}

/// Error that can happen when executing Lua code.
#[derive(Debug)]
pub enum LuaError {
    /// There was a syntax error when parsing the Lua code.
    SyntaxError(String),

    /// There was an error during execution of the Lua code
    /// (for example not enough parameters for a function call).
    ExecutionError(String),

    /// There was an IoError while reading the source code to execute.
    ReadError(IoError),

    /// The call to `execute` has requested the wrong type of data.
    WrongType,
}

impl<'lua> Lua<'lua> {
    /// Builds a new Lua context.
    ///
    /// # Panic
    ///
    /// The function panics if the underlying call to `lua_newstate` fails
    /// (which indicates lack of memory).
    pub fn new() -> Lua<'lua> {
        let lua = unsafe { ffi::lua_newstate(alloc, std::ptr::null_mut()) };
        if lua.is_null() {
            panic!("lua_newstate failed");
        }

        // this alloc function is required to create a lua state.
        extern "C" fn alloc(_ud: *mut libc::c_void, ptr: *mut libc::c_void, _osize: libc::size_t,
                            nsize: libc::size_t) -> *mut libc::c_void
        {
            unsafe {
                if nsize == 0 {
                    libc::free(ptr as *mut libc::c_void);
                    std::ptr::null_mut()
                } else {
                    libc::realloc(ptr, nsize)
                }
            }
        }

        // called whenever lua encounters an unexpected error.
        extern "C" fn panic(lua: *mut ffi::lua_State) -> libc::c_int {
            let err = unsafe { ffi::lua_tostring(lua, -1) };
            let err = unsafe { CStr::from_ptr(err) };
            let err = String::from_utf8(err.to_bytes().to_vec()).unwrap();
            panic!("PANIC: unprotected error in call to Lua API ({})\n", err);
        }

        unsafe { ffi::lua_atpanic(lua, panic) };

        Lua {
            lua: LuaContext(lua),
            must_be_closed: true,
            marker: PhantomData,
        }
    }

    /// Takes an existing `lua_State` and build a Lua object from it.
    ///
    /// # Arguments
    ///
    ///  * `close_at_the_end`: if true, lua_close will be called on the lua_State on the destructor
    pub unsafe fn from_existing_state<T>(lua: *mut T, close_at_the_end: bool) -> Lua<'lua> {
        Lua {
            lua: std::mem::transmute(lua),
            must_be_closed: close_at_the_end,
            marker: PhantomData,
        }
    }

    /// Opens all standard Lua libraries.
    /// This is done by calling `luaL_openlibs`.
    pub fn openlibs(&mut self) {
        unsafe { ffi::luaL_openlibs(self.lua.0) }
    }

    /// Executes some Lua code on the context.
    pub fn execute<'a, T>(&'a mut self, code: &str) -> Result<T, LuaError>
                          where T: for<'g> LuaRead<&'g mut PushGuard<&'a mut Lua<'lua>>> +
                                   for<'g> LuaRead<PushGuard<&'g mut PushGuard<&'a mut Lua<'lua>>>>
    {
        let mut f = try!(functions_read::LuaFunction::load(self, code));
        f.call()
    }

    /// Executes some Lua code on the context.
    pub fn execute_from_reader<'a, T, R>(&'a mut self, code: R) -> Result<T, LuaError>
            where T: for<'g> LuaRead<&'g mut PushGuard<&'a mut Lua<'lua>>> +
                     for<'g> LuaRead<PushGuard<&'g mut PushGuard<&'a mut Lua<'lua>>>>,
                  R: Read
    {
        let mut f = try!(functions_read::LuaFunction::load_from_reader(self, code));
        f.call()
    }

    /// Reads the value of a global variable.
    pub fn get<'l, V, I>(&'l mut self, index: I) -> Option<V>
                         where I: Borrow<str>, V: LuaRead<PushGuard<&'l mut Lua<'lua>>>
    {
        let index = CString::new(index.borrow()).unwrap();
        unsafe { ffi::lua_getglobal(self.lua.0, index.as_ptr()); }
        let guard = PushGuard { lua: self, size: 1 };
        LuaRead::lua_read(guard).ok()
    }

    /// Modifies the value of a global variable.
    pub fn set<'a, I, V>(&'a mut self, index: I, value: V)
                         where I: Borrow<str>, V: Push<&'a mut Lua<'lua>>
    {
        let index = CString::new(index.borrow()).unwrap();
        value.push_to_lua(self).forget();
        unsafe { ffi::lua_setglobal(self.lua.0, index.as_ptr()); }
    }

    /// Inserts an empty array, then loads it.
    pub fn empty_array<'a, I>(&'a mut self, index: I) -> LuaTable<PushGuard<&'a mut Lua<'lua>>>
                              where I: Borrow<str>
    {
        // TODO: cleaner implementation
        let mut me = self;
        let index2 = CString::new(index.borrow()).unwrap();
        Vec::<u8>::with_capacity(0).push_to_lua(&mut me).forget();
        unsafe { ffi::lua_setglobal(me.lua.0, index2.as_ptr()); }

        me.get(index).unwrap()
    }
}

impl<'lua> Drop for Lua<'lua> {
    fn drop(&mut self) {
        if self.must_be_closed {
            unsafe { ffi::lua_close(self.lua.0) }
        }
    }
}

impl<L> Drop for PushGuard<L> where L: AsMutLua {
    fn drop(&mut self) {
        if self.size != 0 {
            unsafe { ffi::lua_pop(self.lua.as_mut_lua().0, self.size); }
        }
    }
}
<|endoftext|>


<commit_before>#![recursion_limit = "128"]
extern crate proc_macro;
extern crate proc_macro2;

use proc_macro::TokenStream;

use quote::quote;
use syn;
use syn::Error;
use syn::ItemFn;

#[proc_macro_attribute]
pub fn libtock_main(_: TokenStream, input: TokenStream) -> TokenStream {
    generate_main_wrapped(input.into()).into()
}

fn generate_main_wrapped(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
    try_generate_main_wrapped(input).unwrap_or_else(|err| err.to_compile_error().into())
}

fn try_generate_main_wrapped(
    input: proc_macro2::TokenStream,
) -> Result<proc_macro2::TokenStream, Error> {
    let ast = syn::parse2::<ItemFn>(input)?;
    let block = ast.block;
    let output = &ast.sig.output;
    Ok(quote!(
        fn main() #output {
            let _block = async #block;
            unsafe {::core::executor::block_on(_block) }
        }
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn wraps_main_into_blocking_executor() {
        let method_def: proc_macro2::TokenStream = quote! {
            async fn main() -> ::libtock::result::TockResult<()>{
                method_call().await;
            }
        }
        .into();
        let actual: ItemFn = syn::parse2::<ItemFn>(generate_main_wrapped(method_def.into()))
            .unwrap()
            .into();
        let expected: ItemFn = syn::parse2::<ItemFn>(quote!(
            fn main() -> ::libtock::result::TockResult<()> {
                let _block = async {
                    method_call().await;
                };
                unsafe { ::core::executor::block_on(_block) }
            }
        ))
        .unwrap()
        .into();
        assert_eq!(actual, expected);
    }
}
<commit_msg>Add jrvanwhys description for the main attribute as doc<commit_after>#![recursion_limit = "128"]
extern crate proc_macro;
extern crate proc_macro2;

use proc_macro::TokenStream;

use quote::quote;
use syn;
use syn::Error;
use syn::ItemFn;

/// Procedural attribute macro. This is meant to be applied to a binary's async
/// `main()`, transforming into a function that returns a type acceptable for
/// `main()`. In other words, this will not compile with libtock-rs:
///     async fn main() {}
/// and this will:
///     #[async_main]
///     async fn main() {}
#[proc_macro_attribute]
pub fn libtock_main(_: TokenStream, input: TokenStream) -> TokenStream {
    generate_main_wrapped(input.into()).into()
}

fn generate_main_wrapped(input: proc_macro2::TokenStream) -> proc_macro2::TokenStream {
    try_generate_main_wrapped(input).unwrap_or_else(|err| err.to_compile_error().into())
}

fn try_generate_main_wrapped(
    input: proc_macro2::TokenStream,
) -> Result<proc_macro2::TokenStream, Error> {
    let ast = syn::parse2::<ItemFn>(input)?;
    let block = ast.block;
    let output = &ast.sig.output;
    Ok(quote!(
        fn main() #output {
            let _block = async #block;
            unsafe {::core::executor::block_on(_block) }
        }
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    #[test]
    fn wraps_main_into_blocking_executor() {
        let method_def: proc_macro2::TokenStream = quote! {
            async fn main() -> ::libtock::result::TockResult<()>{
                method_call().await;
            }
        }
        .into();
        let actual: ItemFn = syn::parse2::<ItemFn>(generate_main_wrapped(method_def.into()))
            .unwrap()
            .into();
        let expected: ItemFn = syn::parse2::<ItemFn>(quote!(
            fn main() -> ::libtock::result::TockResult<()> {
                let _block = async {
                    method_call().await;
                };
                unsafe { ::core::executor::block_on(_block) }
            }
        ))
        .unwrap()
        .into();
        assert_eq!(actual, expected);
    }
}
<|endoftext|>


<commit_before>use super::error::{ErrorKind, Result};
use super::header;
use super::util::tcpconnection;
use super::{ServicePair, ServiceResult};
use byteorder::WriteBytesExt;
use serde_rosmsg::{from_reader, to_writer};
use std;
use std::collections::HashMap;
use std::net::TcpListener;
use std::sync::Arc;
use std::thread;

pub struct Service {
    pub api: String,
    pub msg_type: String,
    pub service: String,
    _raii: tcpconnection::Raii,
}

impl Service {
    pub fn new<T, F>(
        hostname: &str,
        port: u16,
        service: &str,
        node_name: &str,
        handler: F,
    ) -> Result<Service>
    where
        T: ServicePair,
        F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
    {
        let listener = TcpListener::bind((hostname, port))?;
        let socket_address = listener.local_addr()?;
        let api = format!("rosrpc://{}:{}", hostname, socket_address.port());
        let (raii, listener) = tcpconnection::iterate(listener, format!("service '{}'", service));
        Ok(Service::wrap_stream::<T, _, _, _>(
            service,
            node_name,
            handler,
            raii,
            listener,
            &api,
        ))
    }

    fn wrap_stream<T, U, V, F>(
        service: &str,
        node_name: &str,
        handler: F,
        raii: tcpconnection::Raii,
        listener: V,
        api: &str,
    ) -> Service
    where
        T: ServicePair,
        U: std::io::Read + std::io::Write + Send + 'static,
        V: Iterator<Item = U> + Send + 'static,
        F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
    {
        let service_name = String::from(service);
        let node_name = String::from(node_name);
        thread::spawn(move || {
            listen_for_clients::<T, _, _, _>(&service_name, &node_name, handler, listener)
        });
        Service {
            api: String::from(api),
            msg_type: T::msg_type(),
            service: String::from(service),
            _raii: raii,
        }
    }
}

fn listen_for_clients<T, U, V, F>(service: &str, node_name: &str, handler: F, connections: V)
where
    T: ServicePair,
    U: std::io::Read + std::io::Write + Send + 'static,
    V: Iterator<Item = U>,
    F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
{
    let handler = Arc::new(handler);
    for mut stream in connections {
        // Service request starts by exchanging connection headers
        if let Err(err) = exchange_headers::<T, _>(&mut stream, service, node_name) {
            // Cconnection can be closed when a client checks for a service.
            if !err.is_closed_connection() {
                error!(
                    "Failed to exchange headers for service '{}': {}",
                    service, err
                );
            }
            continue;
        }

        // Spawn a thread for handling requests
        spawn_request_handler::<T, U, F>(stream, Arc::clone(&handler));
    }
}

fn exchange_headers<T, U>(stream: &mut U, service: &str, node_name: &str) -> Result<()>
where
    T: ServicePair,
    U: std::io::Write + std::io::Read,
{
    read_request::<T, U>(stream, service)?;
    write_response::<T, U>(stream, node_name)
}

fn read_request<T: ServicePair, U: std::io::Read>(stream: &mut U, service: &str) -> Result<()> {
    let fields = header::decode(stream)?;
    header::match_field(&fields, "service", service)?;
    if fields.get("callerid").is_none() {
        bail!(ErrorKind::HeaderMissingField("callerid".into()));
    }
    if header::match_field(&fields, "probe", "1").is_ok() {
        return Ok(());
    }
    header::match_field(&fields, "md5sum", &T::md5sum())
}

fn write_response<T, U>(stream: &mut U, node_name: &str) -> Result<()>
where
    T: ServicePair,
    U: std::io::Write,
{
    let mut fields = HashMap::<String, String>::new();
    fields.insert(String::from("callerid"), String::from(node_name));
    fields.insert(String::from("md5sum"), T::md5sum());
    fields.insert(String::from("type"), T::msg_type());
    header::encode(stream, &fields)?;
    Ok(())
}

fn spawn_request_handler<T, U, F>(stream: U, handler: Arc<F>)
where
    T: ServicePair,
    U: std::io::Read + std::io::Write + Send + 'static,
    F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
{
    thread::spawn(move || {
        if let Err(err) = handle_request_loop::<T, U, F>(stream, &handler) {
            if !err.is_closed_connection() {
                let info = err.iter()
                    .map(|v| format!("{}", v))
                    .collect::<Vec<_>>()
                    .join("\nCaused by:");
                error!("{}", info);
            }
        }
    });
}

fn handle_request_loop<T, U, F>(mut stream: U, handler: &F) -> Result<()>
where
    T: ServicePair,
    U: std::io::Read + std::io::Write,
    F: Fn(T::Request) -> ServiceResult<T::Response>,
{
    // Receive request from client
    // Break out of loop in case of failure to read request
    while let Ok(req) = from_reader(&mut stream) {
        // Call function that handles request and returns response
        match handler(req) {
            Ok(res) => {
                // Send True flag and response in case of success
                stream.write_u8(1)?;
                to_writer(&mut stream, &res)?;
            }
            Err(message) => {
                // Send False flag and error message string in case of failure
                stream.write_u8(0)?;
                to_writer(&mut stream, &message)?;
            }
        };
    }

    // Upon failure to read request, send client failure message
    // This can be caused by actual issues or by the client stopping the connection
    stream.write_u8(0)?;
    to_writer(&mut stream, &"Failed to parse passed arguments")?;
    Ok(())
}
<commit_msg>fix typo in comment<commit_after>use super::error::{ErrorKind, Result};
use super::header;
use super::util::tcpconnection;
use super::{ServicePair, ServiceResult};
use byteorder::WriteBytesExt;
use serde_rosmsg::{from_reader, to_writer};
use std;
use std::collections::HashMap;
use std::net::TcpListener;
use std::sync::Arc;
use std::thread;

pub struct Service {
    pub api: String,
    pub msg_type: String,
    pub service: String,
    _raii: tcpconnection::Raii,
}

impl Service {
    pub fn new<T, F>(
        hostname: &str,
        port: u16,
        service: &str,
        node_name: &str,
        handler: F,
    ) -> Result<Service>
    where
        T: ServicePair,
        F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
    {
        let listener = TcpListener::bind((hostname, port))?;
        let socket_address = listener.local_addr()?;
        let api = format!("rosrpc://{}:{}", hostname, socket_address.port());
        let (raii, listener) = tcpconnection::iterate(listener, format!("service '{}'", service));
        Ok(Service::wrap_stream::<T, _, _, _>(
            service,
            node_name,
            handler,
            raii,
            listener,
            &api,
        ))
    }

    fn wrap_stream<T, U, V, F>(
        service: &str,
        node_name: &str,
        handler: F,
        raii: tcpconnection::Raii,
        listener: V,
        api: &str,
    ) -> Service
    where
        T: ServicePair,
        U: std::io::Read + std::io::Write + Send + 'static,
        V: Iterator<Item = U> + Send + 'static,
        F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
    {
        let service_name = String::from(service);
        let node_name = String::from(node_name);
        thread::spawn(move || {
            listen_for_clients::<T, _, _, _>(&service_name, &node_name, handler, listener)
        });
        Service {
            api: String::from(api),
            msg_type: T::msg_type(),
            service: String::from(service),
            _raii: raii,
        }
    }
}

fn listen_for_clients<T, U, V, F>(service: &str, node_name: &str, handler: F, connections: V)
where
    T: ServicePair,
    U: std::io::Read + std::io::Write + Send + 'static,
    V: Iterator<Item = U>,
    F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
{
    let handler = Arc::new(handler);
    for mut stream in connections {
        // Service request starts by exchanging connection headers
        if let Err(err) = exchange_headers::<T, _>(&mut stream, service, node_name) {
            // Connection can be closed when a client checks for a service.
            if !err.is_closed_connection() {
                error!(
                    "Failed to exchange headers for service '{}': {}",
                    service, err
                );
            }
            continue;
        }

        // Spawn a thread for handling requests
        spawn_request_handler::<T, U, F>(stream, Arc::clone(&handler));
    }
}

fn exchange_headers<T, U>(stream: &mut U, service: &str, node_name: &str) -> Result<()>
where
    T: ServicePair,
    U: std::io::Write + std::io::Read,
{
    read_request::<T, U>(stream, service)?;
    write_response::<T, U>(stream, node_name)
}

fn read_request<T: ServicePair, U: std::io::Read>(stream: &mut U, service: &str) -> Result<()> {
    let fields = header::decode(stream)?;
    header::match_field(&fields, "service", service)?;
    if fields.get("callerid").is_none() {
        bail!(ErrorKind::HeaderMissingField("callerid".into()));
    }
    if header::match_field(&fields, "probe", "1").is_ok() {
        return Ok(());
    }
    header::match_field(&fields, "md5sum", &T::md5sum())
}

fn write_response<T, U>(stream: &mut U, node_name: &str) -> Result<()>
where
    T: ServicePair,
    U: std::io::Write,
{
    let mut fields = HashMap::<String, String>::new();
    fields.insert(String::from("callerid"), String::from(node_name));
    fields.insert(String::from("md5sum"), T::md5sum());
    fields.insert(String::from("type"), T::msg_type());
    header::encode(stream, &fields)?;
    Ok(())
}

fn spawn_request_handler<T, U, F>(stream: U, handler: Arc<F>)
where
    T: ServicePair,
    U: std::io::Read + std::io::Write + Send + 'static,
    F: Fn(T::Request) -> ServiceResult<T::Response> + Send + Sync + 'static,
{
    thread::spawn(move || {
        if let Err(err) = handle_request_loop::<T, U, F>(stream, &handler) {
            if !err.is_closed_connection() {
                let info = err.iter()
                    .map(|v| format!("{}", v))
                    .collect::<Vec<_>>()
                    .join("\nCaused by:");
                error!("{}", info);
            }
        }
    });
}

fn handle_request_loop<T, U, F>(mut stream: U, handler: &F) -> Result<()>
where
    T: ServicePair,
    U: std::io::Read + std::io::Write,
    F: Fn(T::Request) -> ServiceResult<T::Response>,
{
    // Receive request from client
    // Break out of loop in case of failure to read request
    while let Ok(req) = from_reader(&mut stream) {
        // Call function that handles request and returns response
        match handler(req) {
            Ok(res) => {
                // Send True flag and response in case of success
                stream.write_u8(1)?;
                to_writer(&mut stream, &res)?;
            }
            Err(message) => {
                // Send False flag and error message string in case of failure
                stream.write_u8(0)?;
                to_writer(&mut stream, &message)?;
            }
        };
    }

    // Upon failure to read request, send client failure message
    // This can be caused by actual issues or by the client stopping the connection
    stream.write_u8(0)?;
    to_writer(&mut stream, &"Failed to parse passed arguments")?;
    Ok(())
}
<|endoftext|>