<commit_before>use std::path::{Path, PathBuf};

use xshell::{cmd, pushd, pushenv, read_file};

use crate::{cargo_files, codegen, project_root, rust_files};

#[test]
fn generate_grammar() {
    codegen::generate_syntax().unwrap()
}

#[test]
fn generate_parser_tests() {
    codegen::generate_parser_tests().unwrap()
}

#[test]
fn generate_assists_tests() {
    codegen::generate_assists_tests().unwrap();
}

/// This clones rustc repo, and so is not worth to keep up-to-date. We update
/// manually by un-ignoring the test from time to time.
#[test]
#[ignore]
fn generate_lint_completions() {
    codegen::generate_lint_completions().unwrap()
}

#[test]
fn check_code_formatting() {
    let _dir = pushd(project_root()).unwrap();
    let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
    crate::ensure_rustfmt().unwrap();
    let res = cmd!("cargo fmt -- --check").run();
    if res.is_err() {
        let _ = cmd!("cargo fmt").run();
    }
    res.unwrap()
}

#[test]
fn smoke_test_generate_documentation() {
    codegen::docs().unwrap()
}

#[test]
fn check_lsp_extensions_docs() {
    let expected_hash = {
        let lsp_ext_rs =
            read_file(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")).unwrap();
        stable_hash(lsp_ext_rs.as_str())
    };

    let actual_hash = {
        let lsp_extensions_md =
            read_file(project_root().join("docs/dev/lsp-extensions.md")).unwrap();
        let text = lsp_extensions_md
            .lines()
            .find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
            .unwrap()
            .trim();
        u64::from_str_radix(text, 16).unwrap()
    };

    if actual_hash != expected_hash {
        panic!(
            "
lsp_ext.rs was changed without touching lsp-extensions.md.

Expected hash: {:x}
Actual hash:   {:x}

Please adjust docs/dev/lsp-extensions.md.
",
            expected_hash, actual_hash
        )
    }
}

#[test]
fn rust_files_are_tidy() {
    let mut tidy_docs = TidyDocs::default();
    for path in rust_files() {
        let text = read_file(&path).unwrap();
        check_todo(&path, &text);
        check_dbg(&path, &text);
        check_trailing_ws(&path, &text);
        deny_clippy(&path, &text);
        tidy_docs.visit(&path, &text);
    }
    tidy_docs.finish();
}

#[test]
fn cargo_files_are_tidy() {
    for cargo in cargo_files() {
        let mut section = None;
        for (line_no, text) in read_file(&cargo).unwrap().lines().enumerate() {
            let text = text.trim();
            if text.starts_with('[') {
                if !text.ends_with(']') {
                    panic!(
                        "\nplease don't add comments or trailing whitespace in section lines.\n\
                            {}:{}\n",
                        cargo.display(),
                        line_no + 1
                    )
                }
                section = Some(text);
                continue;
            }
            let text: String = text.split_whitespace().collect();
            if !text.contains("path=") {
                continue;
            }
            match section {
                Some(s) if s.contains("dev-dependencies") => {
                    if text.contains("version") {
                        panic!(
                            "\ncargo internal dev-dependencies should not have a version.\n\
                            {}:{}\n",
                            cargo.display(),
                            line_no + 1
                        );
                    }
                }
                Some(s) if s.contains("dependencies") => {
                    if !text.contains("version") {
                        panic!(
                            "\ncargo internal dependencies should have a version.\n\
                            {}:{}\n",
                            cargo.display(),
                            line_no + 1
                        );
                    }
                }
                _ => {}
            }
        }
    }
}

#[test]
fn check_merge_commits() {
    let stdout = cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19..")
        .read()
        .unwrap();
    if !stdout.is_empty() {
        panic!(
            "
Merge commits are not allowed in the history.

When updating a pull-request, please rebase your feature branch
on top of master by running `git rebase master`. If rebase fails,
you can re-apply your changes like this:

  # Just look around to see the current state.
  $ git status
  $ git log

  # Abort in-progress rebase and merges, if any.
  $ git rebase --abort
  $ git merge --abort

  # Make the branch point to the latest commit from master,
  # while maintaining your local changes uncommited.
  $ git reset --soft origin/master

  # Commit all changes in a single batch.
  $ git commit -am'My changes'

  # Verify that everything looks alright.
  $ git status
  $ git log

  # Push the changes. We did a rebase, so we need `--force` option.
  # `--force-with-lease` is a more safe (Rusty) version of `--force`.
  $ git push --force-with-lease

  # Verify that both local and remote branch point to the same commit.
  $ git log

And don't fear to mess something up during a rebase -- you can
always restore the previous state using `git ref-log`:

https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after-undo-local
"
        );
    }
}

fn deny_clippy(path: &Path, text: &str) {
    let ignore = &[
        // The documentation in string literals may contain anything for its own purposes
        "ide_completion/src/generated_lint_completions.rs",
    ];
    if ignore.iter().any(|p| path.ends_with(p)) {
        return;
    }

    if text.contains("\u{61}llow(clippy") {
        panic!(
            "\n\nallowing lints is forbidden: {}.
rust-analyzer intentionally doesn't check clippy on CI.
You can allow lint globally via `xtask clippy`.
See https://github.com/rust-lang/rust-clippy/issues/5537 for discussion.

",
            path.display()
        )
    }
}

#[test]
fn check_licenses() {
    let expected = "
0BSD OR MIT OR Apache-2.0
Apache-2.0
Apache-2.0 OR BSL-1.0
Apache-2.0 OR MIT
Apache-2.0/MIT
BSD-3-Clause
CC0-1.0 OR Artistic-2.0
ISC
MIT
MIT / Apache-2.0
MIT OR Apache-2.0
MIT OR Apache-2.0 OR Zlib
MIT OR Zlib OR Apache-2.0
MIT/Apache-2.0
Unlicense OR MIT
Unlicense/MIT
Zlib OR Apache-2.0 OR MIT
"
    .lines()
    .filter(|it| !it.is_empty())
    .collect::<Vec<_>>();

    let meta = cmd!("cargo metadata --format-version 1").read().unwrap();
    let mut licenses = meta
        .split(|c| c == ',' || c == '{' || c == '}')
        .filter(|it| it.contains(r#""license""#))
        .map(|it| it.trim())
        .map(|it| it[r#""license":"#.len()..].trim_matches('"'))
        .collect::<Vec<_>>();
    licenses.sort_unstable();
    licenses.dedup();
    if licenses != expected {
        let mut diff = String::new();

        diff += &"New Licenses:\n".to_string();
        for &l in licenses.iter() {
            if !expected.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        diff += &"\nMissing Licenses:\n".to_string();
        for &l in expected.iter() {
            if !licenses.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        panic!("different set of licenses!\n{}", diff);
    }
    assert_eq!(licenses, expected);
}

fn check_todo(path: &Path, text: &str) {
    let need_todo = &[
        // This file itself obviously needs to use todo (<- like this!).
        "tests/tidy.rs",
        // Some of our assists generate `todo!()`.
        "handlers/add_turbo_fish.rs",
        "handlers/generate_function.rs",
        "handlers/fill_match_arms.rs",
        // To support generating `todo!()` in assists, we have `expr_todo()` in
        // `ast::make`.
        "ast/make.rs",
        // The documentation in string literals may contain anything for its own purposes
        "ide_completion/src/generated_lint_completions.rs",
    ];
    if need_todo.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
        // Generated by an assist
        if text.contains("${0:todo!()}") {
            return;
        }

        panic!(
            "\nTODO markers or todo! macros should not be committed to the master branch,\n\
             use FIXME instead\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_dbg(path: &Path, text: &str) {
    let need_dbg = &[
        // This file itself obviously needs to use dbg.
        "tests/tidy.rs",
        // Assists to remove `dbg!()`
        "handlers/remove_dbg.rs",
        // We have .dbg postfix
        "ide_completion/src/completions/postfix.rs",
        // The documentation in string literals may contain anything for its own purposes
        "ide_completion/src/lib.rs",
        "ide_completion/src/generated_lint_completions.rs",
        // test for doc test for remove_dbg
        "src/tests/generated.rs",
    ];
    if need_dbg.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("dbg!") {
        panic!(
            "\ndbg! macros should not be committed to the master branch,\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_trailing_ws(path: &Path, text: &str) {
    if is_exclude_dir(path, &["test_data"]) {
        return;
    }
    for (line_number, line) in text.lines().enumerate() {
        if line.chars().last().map(char::is_whitespace) == Some(true) {
            panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
        }
    }
}

#[derive(Default)]
struct TidyDocs {
    missing_docs: Vec<String>,
    contains_fixme: Vec<PathBuf>,
}

impl TidyDocs {
    fn visit(&mut self, path: &Path, text: &str) {
        // Tests and diagnostic fixes don't need module level comments.
        if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar"]) {
            return;
        }

        if is_exclude_file(path) {
            return;
        }

        let first_line = match text.lines().next() {
            Some(it) => it,
            None => return,
        };

        if first_line.starts_with("//!") {
            if first_line.contains("FIXME") {
                self.contains_fixme.push(path.to_path_buf());
            }
        } else {
            if text.contains("// Feature:") || text.contains("// Assist:") {
                return;
            }
            self.missing_docs.push(path.display().to_string());
        }

        fn is_exclude_file(d: &Path) -> bool {
            let file_names = ["tests.rs", "famous_defs_fixture.rs"];

            d.file_name()
                .unwrap_or_default()
                .to_str()
                .map(|f_n| file_names.iter().any(|name| *name == f_n))
                .unwrap_or(false)
        }
    }

    fn finish(self) {
        if !self.missing_docs.is_empty() {
            panic!(
                "\nMissing docs strings\n\n\
                 modules:\n{}\n\n",
                self.missing_docs.join("\n")
            )
        }

        for path in self.contains_fixme {
            panic!("FIXME doc in a fully-documented crate: {}", path.display())
        }
    }
}

fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
    p.strip_prefix(project_root())
        .unwrap()
        .components()
        .rev()
        .skip(1)
        .filter_map(|it| it.as_os_str().to_str())
        .any(|it| dirs_to_exclude.contains(&it))
}

#[allow(deprecated)]
fn stable_hash(text: &str) -> u64 {
    use std::hash::{Hash, Hasher, SipHasher};

    let text = text.replace('\r', "");
    let mut hasher = SipHasher::default();
    text.hash(&mut hasher);
    hasher.finish()
}
<commit_msg>Use push_str<commit_after>use std::path::{Path, PathBuf};

use xshell::{cmd, pushd, pushenv, read_file};

use crate::{cargo_files, codegen, project_root, rust_files};

#[test]
fn generate_grammar() {
    codegen::generate_syntax().unwrap()
}

#[test]
fn generate_parser_tests() {
    codegen::generate_parser_tests().unwrap()
}

#[test]
fn generate_assists_tests() {
    codegen::generate_assists_tests().unwrap();
}

/// This clones rustc repo, and so is not worth to keep up-to-date. We update
/// manually by un-ignoring the test from time to time.
#[test]
#[ignore]
fn generate_lint_completions() {
    codegen::generate_lint_completions().unwrap()
}

#[test]
fn check_code_formatting() {
    let _dir = pushd(project_root()).unwrap();
    let _e = pushenv("RUSTUP_TOOLCHAIN", "stable");
    crate::ensure_rustfmt().unwrap();
    let res = cmd!("cargo fmt -- --check").run();
    if res.is_err() {
        let _ = cmd!("cargo fmt").run();
    }
    res.unwrap()
}

#[test]
fn smoke_test_generate_documentation() {
    codegen::docs().unwrap()
}

#[test]
fn check_lsp_extensions_docs() {
    let expected_hash = {
        let lsp_ext_rs =
            read_file(project_root().join("crates/rust-analyzer/src/lsp_ext.rs")).unwrap();
        stable_hash(lsp_ext_rs.as_str())
    };

    let actual_hash = {
        let lsp_extensions_md =
            read_file(project_root().join("docs/dev/lsp-extensions.md")).unwrap();
        let text = lsp_extensions_md
            .lines()
            .find_map(|line| line.strip_prefix("lsp_ext.rs hash:"))
            .unwrap()
            .trim();
        u64::from_str_radix(text, 16).unwrap()
    };

    if actual_hash != expected_hash {
        panic!(
            "
lsp_ext.rs was changed without touching lsp-extensions.md.

Expected hash: {:x}
Actual hash:   {:x}

Please adjust docs/dev/lsp-extensions.md.
",
            expected_hash, actual_hash
        )
    }
}

#[test]
fn rust_files_are_tidy() {
    let mut tidy_docs = TidyDocs::default();
    for path in rust_files() {
        let text = read_file(&path).unwrap();
        check_todo(&path, &text);
        check_dbg(&path, &text);
        check_trailing_ws(&path, &text);
        deny_clippy(&path, &text);
        tidy_docs.visit(&path, &text);
    }
    tidy_docs.finish();
}

#[test]
fn cargo_files_are_tidy() {
    for cargo in cargo_files() {
        let mut section = None;
        for (line_no, text) in read_file(&cargo).unwrap().lines().enumerate() {
            let text = text.trim();
            if text.starts_with('[') {
                if !text.ends_with(']') {
                    panic!(
                        "\nplease don't add comments or trailing whitespace in section lines.\n\
                            {}:{}\n",
                        cargo.display(),
                        line_no + 1
                    )
                }
                section = Some(text);
                continue;
            }
            let text: String = text.split_whitespace().collect();
            if !text.contains("path=") {
                continue;
            }
            match section {
                Some(s) if s.contains("dev-dependencies") => {
                    if text.contains("version") {
                        panic!(
                            "\ncargo internal dev-dependencies should not have a version.\n\
                            {}:{}\n",
                            cargo.display(),
                            line_no + 1
                        );
                    }
                }
                Some(s) if s.contains("dependencies") => {
                    if !text.contains("version") {
                        panic!(
                            "\ncargo internal dependencies should have a version.\n\
                            {}:{}\n",
                            cargo.display(),
                            line_no + 1
                        );
                    }
                }
                _ => {}
            }
        }
    }
}

#[test]
fn check_merge_commits() {
    let stdout = cmd!("git rev-list --merges --invert-grep --author 'bors\\[bot\\]' HEAD~19..")
        .read()
        .unwrap();
    if !stdout.is_empty() {
        panic!(
            "
Merge commits are not allowed in the history.

When updating a pull-request, please rebase your feature branch
on top of master by running `git rebase master`. If rebase fails,
you can re-apply your changes like this:

  # Just look around to see the current state.
  $ git status
  $ git log

  # Abort in-progress rebase and merges, if any.
  $ git rebase --abort
  $ git merge --abort

  # Make the branch point to the latest commit from master,
  # while maintaining your local changes uncommited.
  $ git reset --soft origin/master

  # Commit all changes in a single batch.
  $ git commit -am'My changes'

  # Verify that everything looks alright.
  $ git status
  $ git log

  # Push the changes. We did a rebase, so we need `--force` option.
  # `--force-with-lease` is a more safe (Rusty) version of `--force`.
  $ git push --force-with-lease

  # Verify that both local and remote branch point to the same commit.
  $ git log

And don't fear to mess something up during a rebase -- you can
always restore the previous state using `git ref-log`:

https://github.blog/2015-06-08-how-to-undo-almost-anything-with-git/#redo-after-undo-local
"
        );
    }
}

fn deny_clippy(path: &Path, text: &str) {
    let ignore = &[
        // The documentation in string literals may contain anything for its own purposes
        "ide_completion/src/generated_lint_completions.rs",
    ];
    if ignore.iter().any(|p| path.ends_with(p)) {
        return;
    }

    if text.contains("\u{61}llow(clippy") {
        panic!(
            "\n\nallowing lints is forbidden: {}.
rust-analyzer intentionally doesn't check clippy on CI.
You can allow lint globally via `xtask clippy`.
See https://github.com/rust-lang/rust-clippy/issues/5537 for discussion.

",
            path.display()
        )
    }
}

#[test]
fn check_licenses() {
    let expected = "
0BSD OR MIT OR Apache-2.0
Apache-2.0
Apache-2.0 OR BSL-1.0
Apache-2.0 OR MIT
Apache-2.0/MIT
BSD-3-Clause
CC0-1.0 OR Artistic-2.0
ISC
MIT
MIT / Apache-2.0
MIT OR Apache-2.0
MIT OR Apache-2.0 OR Zlib
MIT OR Zlib OR Apache-2.0
MIT/Apache-2.0
Unlicense OR MIT
Unlicense/MIT
Zlib OR Apache-2.0 OR MIT
"
    .lines()
    .filter(|it| !it.is_empty())
    .collect::<Vec<_>>();

    let meta = cmd!("cargo metadata --format-version 1").read().unwrap();
    let mut licenses = meta
        .split(|c| c == ',' || c == '{' || c == '}')
        .filter(|it| it.contains(r#""license""#))
        .map(|it| it.trim())
        .map(|it| it[r#""license":"#.len()..].trim_matches('"'))
        .collect::<Vec<_>>();
    licenses.sort_unstable();
    licenses.dedup();
    if licenses != expected {
        let mut diff = String::new();

        diff.push_str("New Licenses:\n");
        for &l in licenses.iter() {
            if !expected.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        diff.push_str("\nMissing Licenses:\n");
        for &l in expected.iter() {
            if !licenses.contains(&l) {
                diff += &format!("  {}\n", l)
            }
        }

        panic!("different set of licenses!\n{}", diff);
    }
    assert_eq!(licenses, expected);
}

fn check_todo(path: &Path, text: &str) {
    let need_todo = &[
        // This file itself obviously needs to use todo (<- like this!).
        "tests/tidy.rs",
        // Some of our assists generate `todo!()`.
        "handlers/add_turbo_fish.rs",
        "handlers/generate_function.rs",
        "handlers/fill_match_arms.rs",
        // To support generating `todo!()` in assists, we have `expr_todo()` in
        // `ast::make`.
        "ast/make.rs",
        // The documentation in string literals may contain anything for its own purposes
        "ide_completion/src/generated_lint_completions.rs",
    ];
    if need_todo.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("TODO") || text.contains("TOOD") || text.contains("todo!") {
        // Generated by an assist
        if text.contains("${0:todo!()}") {
            return;
        }

        panic!(
            "\nTODO markers or todo! macros should not be committed to the master branch,\n\
             use FIXME instead\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_dbg(path: &Path, text: &str) {
    let need_dbg = &[
        // This file itself obviously needs to use dbg.
        "tests/tidy.rs",
        // Assists to remove `dbg!()`
        "handlers/remove_dbg.rs",
        // We have .dbg postfix
        "ide_completion/src/completions/postfix.rs",
        // The documentation in string literals may contain anything for its own purposes
        "ide_completion/src/lib.rs",
        "ide_completion/src/generated_lint_completions.rs",
        // test for doc test for remove_dbg
        "src/tests/generated.rs",
    ];
    if need_dbg.iter().any(|p| path.ends_with(p)) {
        return;
    }
    if text.contains("dbg!") {
        panic!(
            "\ndbg! macros should not be committed to the master branch,\n\
             {}\n",
            path.display(),
        )
    }
}

fn check_trailing_ws(path: &Path, text: &str) {
    if is_exclude_dir(path, &["test_data"]) {
        return;
    }
    for (line_number, line) in text.lines().enumerate() {
        if line.chars().last().map(char::is_whitespace) == Some(true) {
            panic!("Trailing whitespace in {} at line {}", path.display(), line_number)
        }
    }
}

#[derive(Default)]
struct TidyDocs {
    missing_docs: Vec<String>,
    contains_fixme: Vec<PathBuf>,
}

impl TidyDocs {
    fn visit(&mut self, path: &Path, text: &str) {
        // Tests and diagnostic fixes don't need module level comments.
        if is_exclude_dir(path, &["tests", "test_data", "fixes", "grammar"]) {
            return;
        }

        if is_exclude_file(path) {
            return;
        }

        let first_line = match text.lines().next() {
            Some(it) => it,
            None => return,
        };

        if first_line.starts_with("//!") {
            if first_line.contains("FIXME") {
                self.contains_fixme.push(path.to_path_buf());
            }
        } else {
            if text.contains("// Feature:") || text.contains("// Assist:") {
                return;
            }
            self.missing_docs.push(path.display().to_string());
        }

        fn is_exclude_file(d: &Path) -> bool {
            let file_names = ["tests.rs", "famous_defs_fixture.rs"];

            d.file_name()
                .unwrap_or_default()
                .to_str()
                .map(|f_n| file_names.iter().any(|name| *name == f_n))
                .unwrap_or(false)
        }
    }

    fn finish(self) {
        if !self.missing_docs.is_empty() {
            panic!(
                "\nMissing docs strings\n\n\
                 modules:\n{}\n\n",
                self.missing_docs.join("\n")
            )
        }

        for path in self.contains_fixme {
            panic!("FIXME doc in a fully-documented crate: {}", path.display())
        }
    }
}

fn is_exclude_dir(p: &Path, dirs_to_exclude: &[&str]) -> bool {
    p.strip_prefix(project_root())
        .unwrap()
        .components()
        .rev()
        .skip(1)
        .filter_map(|it| it.as_os_str().to_str())
        .any(|it| dirs_to_exclude.contains(&it))
}

#[allow(deprecated)]
fn stable_hash(text: &str) -> u64 {
    use std::hash::{Hash, Hasher, SipHasher};

    let text = text.replace('\r', "");
    let mut hasher = SipHasher::default();
    text.hash(&mut hasher);
    hasher.finish()
}
<|endoftext|>


<commit_before>use sha1::Sha1;

use aio::SharedConnection;
use cmd::{cmd, Cmd};
use connection::ConnectionLike;
use futures::{Async, Future, Poll};
use types::{ErrorKind, FromRedisValue, RedisError, RedisFuture, RedisResult, ToRedisArgs};

/// Represents a lua script.
#[derive(Clone)]
pub struct Script {
    code: String,
    hash: String,
}

/// The script object represents a lua script that can be executed on the
/// redis server.  The object itself takes care of automatic uploading and
/// execution.  The script object itself can be shared and is immutable.
///
/// Example:
///
/// ```rust,no_run
/// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// # let mut con = client.get_connection().unwrap();
/// let script = redis::Script::new(r"
///     return tonumber(ARGV[1]) + tonumber(ARGV[2]);
/// ");
/// let result = script.arg(1).arg(2).invoke(&mut con);
/// assert_eq!(result, Ok(3));
/// ```
impl Script {
    /// Creates a new script object.
    pub fn new(code: &str) -> Script {
        let mut hash = Sha1::new();
        hash.update(code.as_bytes());
        Script {
            code: code.to_string(),
            hash: hash.digest().to_string(),
        }
    }

    /// Returns the script's SHA1 hash in hexadecimal format.
    pub fn get_hash(&self) -> &str {
        &self.hash
    }

    /// Creates a script invocation object with a key filled in.
    #[inline]
    pub fn key<T: ToRedisArgs>(&self, key: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: key.to_redis_args(),
        }
    }

    /// Creates a script invocation object with an argument filled in.
    #[inline]
    pub fn arg<T: ToRedisArgs>(&self, arg: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: arg.to_redis_args(),
            keys: vec![],
        }
    }

    /// Returns an empty script invocation object.  This is primarily useful
    /// for programmatically adding arguments and keys because the type will
    /// not change.  Normally you can use `arg` and `key` directly.
    #[inline]
    pub fn prepare_invoke(&self) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: vec![],
        }
    }

    /// Invokes the script directly without arguments.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &mut ConnectionLike) -> RedisResult<T> {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: vec![],
        }
        .invoke(con)
    }
}

/// Represents a prepared script call.
pub struct ScriptInvocation<'a> {
    script: &'a Script,
    args: Vec<Vec<u8>>,
    keys: Vec<Vec<u8>>,
}

/// This type collects keys and other arguments for the script so that it
/// can be then invoked.  While the `Script` type itself holds the script,
/// the `ScriptInvocation` holds the arguments that should be invoked until
/// it's sent to the server.
impl<'a> ScriptInvocation<'a> {
    /// Adds a regular argument to the invocation.  This ends up as `ARGV[i]`
    /// in the script.
    #[inline]
    pub fn arg<'b, T: ToRedisArgs>(&'b mut self, arg: T) -> &'b mut ScriptInvocation<'a>
    where
        'a: 'b,
    {
        arg.write_redis_args(&mut self.args);
        self
    }

    /// Adds a key argument to the invocation.  This ends up as `KEYS[i]`
    /// in the script.
    #[inline]
    pub fn key<'b, T: ToRedisArgs>(&'b mut self, key: T) -> &'b mut ScriptInvocation<'a>
    where
        'a: 'b,
    {
        key.write_redis_args(&mut self.keys);
        self
    }

    /// Invokes the script and returns the result.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &mut ConnectionLike) -> RedisResult<T> {
        loop {
            match cmd("EVALSHA")
                .arg(self.script.hash.as_bytes())
                .arg(self.keys.len())
                .arg(&*self.keys)
                .arg(&*self.args)
                .query(con)
            {
                Ok(val) => {
                    return Ok(val);
                }
                Err(err) => {
                    if err.kind() == ErrorKind::NoScriptError {
                        let _: () = cmd("SCRIPT")
                            .arg("LOAD")
                            .arg(self.script.code.as_bytes())
                            .query(con)?;
                    } else {
                        fail!(err);
                    }
                }
            }
        }
    }

    /// Asynchronously invokes the script and returns the result.
    #[inline]
    pub fn invoke_async<T: FromRedisValue + Send + 'static>(
        &self,
        con: SharedConnection,
    ) -> impl Future<Item = (SharedConnection, T), Error = RedisError> {
        let mut eval_cmd = cmd("EVALSHA");
        eval_cmd
            .arg(self.script.hash.as_bytes())
            .arg(self.keys.len())
            .arg(&*self.keys)
            .arg(&*self.args);

        let mut load_cmd = cmd("SCRIPT");
        load_cmd.arg("LOAD").arg(self.script.code.as_bytes());

        let future = eval_cmd.query_async(con.clone());
        InvokeAsyncFuture {
            con: con.clone(),
            eval_cmd,
            load_cmd,
            status: ScriptStatus::NotLoaded,
            future,
        }
    }
}

#[derive(PartialEq)]
enum ScriptStatus {
    NotLoaded,
    Loading,
    Loaded,
}

/// A future that runs the given script and loads it into Redis if
/// it has not already been loaded
struct InvokeAsyncFuture<T> {
    con: SharedConnection,
    eval_cmd: Cmd,
    load_cmd: Cmd,
    status: ScriptStatus,
    future: RedisFuture<(SharedConnection, T)>,
}

impl<T> Future for InvokeAsyncFuture<T>
where
    T: FromRedisValue + Send + 'static,
{
    type Item = (SharedConnection, T);
    type Error = RedisError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            match self.future.poll() {
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Ok(Async::Ready((con, val))) => {
                    if self.status == ScriptStatus::Loading {
                        self.status = ScriptStatus::Loaded;
                        self.future = self.eval_cmd.query_async(con);
                    } else {
                        return Ok(Async::Ready((con, val)));
                    }
                }
                Err(err) => {
                    if err.kind() == ErrorKind::NoScriptError
                        && self.status == ScriptStatus::NotLoaded
                    {
                        self.status = ScriptStatus::Loading;
                        self.future = self.load_cmd.query_async(self.con.clone())
                    } else {
                        return Err(err);
                    }
                }
            }
        }
    }
}
<commit_msg>refactor: Script doesn't need to impl Clone<commit_after>use sha1::Sha1;

use aio::SharedConnection;
use cmd::{cmd, Cmd};
use connection::ConnectionLike;
use futures::{Async, Future, Poll};
use types::{ErrorKind, FromRedisValue, RedisError, RedisFuture, RedisResult, ToRedisArgs};

/// Represents a lua script.
pub struct Script {
    code: String,
    hash: String,
}

/// The script object represents a lua script that can be executed on the
/// redis server.  The object itself takes care of automatic uploading and
/// execution.  The script object itself can be shared and is immutable.
///
/// Example:
///
/// ```rust,no_run
/// # let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// # let mut con = client.get_connection().unwrap();
/// let script = redis::Script::new(r"
///     return tonumber(ARGV[1]) + tonumber(ARGV[2]);
/// ");
/// let result = script.arg(1).arg(2).invoke(&mut con);
/// assert_eq!(result, Ok(3));
/// ```
impl Script {
    /// Creates a new script object.
    pub fn new(code: &str) -> Script {
        let mut hash = Sha1::new();
        hash.update(code.as_bytes());
        Script {
            code: code.to_string(),
            hash: hash.digest().to_string(),
        }
    }

    /// Returns the script's SHA1 hash in hexadecimal format.
    pub fn get_hash(&self) -> &str {
        &self.hash
    }

    /// Creates a script invocation object with a key filled in.
    #[inline]
    pub fn key<T: ToRedisArgs>(&self, key: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: key.to_redis_args(),
        }
    }

    /// Creates a script invocation object with an argument filled in.
    #[inline]
    pub fn arg<T: ToRedisArgs>(&self, arg: T) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: arg.to_redis_args(),
            keys: vec![],
        }
    }

    /// Returns an empty script invocation object.  This is primarily useful
    /// for programmatically adding arguments and keys because the type will
    /// not change.  Normally you can use `arg` and `key` directly.
    #[inline]
    pub fn prepare_invoke(&self) -> ScriptInvocation {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: vec![],
        }
    }

    /// Invokes the script directly without arguments.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &mut ConnectionLike) -> RedisResult<T> {
        ScriptInvocation {
            script: self,
            args: vec![],
            keys: vec![],
        }
        .invoke(con)
    }
}

/// Represents a prepared script call.
pub struct ScriptInvocation<'a> {
    script: &'a Script,
    args: Vec<Vec<u8>>,
    keys: Vec<Vec<u8>>,
}

/// This type collects keys and other arguments for the script so that it
/// can be then invoked.  While the `Script` type itself holds the script,
/// the `ScriptInvocation` holds the arguments that should be invoked until
/// it's sent to the server.
impl<'a> ScriptInvocation<'a> {
    /// Adds a regular argument to the invocation.  This ends up as `ARGV[i]`
    /// in the script.
    #[inline]
    pub fn arg<'b, T: ToRedisArgs>(&'b mut self, arg: T) -> &'b mut ScriptInvocation<'a>
    where
        'a: 'b,
    {
        arg.write_redis_args(&mut self.args);
        self
    }

    /// Adds a key argument to the invocation.  This ends up as `KEYS[i]`
    /// in the script.
    #[inline]
    pub fn key<'b, T: ToRedisArgs>(&'b mut self, key: T) -> &'b mut ScriptInvocation<'a>
    where
        'a: 'b,
    {
        key.write_redis_args(&mut self.keys);
        self
    }

    /// Invokes the script and returns the result.
    #[inline]
    pub fn invoke<T: FromRedisValue>(&self, con: &mut ConnectionLike) -> RedisResult<T> {
        loop {
            match cmd("EVALSHA")
                .arg(self.script.hash.as_bytes())
                .arg(self.keys.len())
                .arg(&*self.keys)
                .arg(&*self.args)
                .query(con)
            {
                Ok(val) => {
                    return Ok(val);
                }
                Err(err) => {
                    if err.kind() == ErrorKind::NoScriptError {
                        let _: () = cmd("SCRIPT")
                            .arg("LOAD")
                            .arg(self.script.code.as_bytes())
                            .query(con)?;
                    } else {
                        fail!(err);
                    }
                }
            }
        }
    }

    /// Asynchronously invokes the script and returns the result.
    #[inline]
    pub fn invoke_async<T: FromRedisValue + Send + 'static>(
        &self,
        con: SharedConnection,
    ) -> impl Future<Item = (SharedConnection, T), Error = RedisError> {
        let mut eval_cmd = cmd("EVALSHA");
        eval_cmd
            .arg(self.script.hash.as_bytes())
            .arg(self.keys.len())
            .arg(&*self.keys)
            .arg(&*self.args);

        let mut load_cmd = cmd("SCRIPT");
        load_cmd.arg("LOAD").arg(self.script.code.as_bytes());

        let future = eval_cmd.query_async(con.clone());
        InvokeAsyncFuture {
            con: con.clone(),
            eval_cmd,
            load_cmd,
            status: ScriptStatus::NotLoaded,
            future,
        }
    }
}

#[derive(PartialEq)]
enum ScriptStatus {
    NotLoaded,
    Loading,
    Loaded,
}

/// A future that runs the given script and loads it into Redis if
/// it has not already been loaded
struct InvokeAsyncFuture<T> {
    con: SharedConnection,
    eval_cmd: Cmd,
    load_cmd: Cmd,
    status: ScriptStatus,
    future: RedisFuture<(SharedConnection, T)>,
}

impl<T> Future for InvokeAsyncFuture<T>
where
    T: FromRedisValue + Send + 'static,
{
    type Item = (SharedConnection, T);
    type Error = RedisError;

    fn poll(&mut self) -> Poll<Self::Item, Self::Error> {
        loop {
            match self.future.poll() {
                Ok(Async::NotReady) => return Ok(Async::NotReady),
                Ok(Async::Ready((con, val))) => {
                    if self.status == ScriptStatus::Loading {
                        self.status = ScriptStatus::Loaded;
                        self.future = self.eval_cmd.query_async(con);
                    } else {
                        return Ok(Async::Ready((con, val)));
                    }
                }
                Err(err) => {
                    if err.kind() == ErrorKind::NoScriptError
                        && self.status == ScriptStatus::NotLoaded
                    {
                        self.status = ScriptStatus::Loading;
                        self.future = self.load_cmd.query_async(self.con.clone())
                    } else {
                        return Err(err);
                    }
                }
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Provides the server and handles the incomming requests
//! All ports are handled by the same function

// System modules:
use std::net::{TcpListener, TcpStream, SocketAddr};
use std::thread::spawn;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::fs::OpenOptions;
use std::io::BufWriter;
use std::io::Result;
use time::{now, Tm};

// Internal modules:
use configuration::{Configuration, HEADER_LENGTH};
use data_parser::{parse_text_data, StationDataType};

fn write_xml_data(tm: Tm, local_port: u16, data: &StationDataType, file_name: &str) -> Result<()> {
    let mut file_handle = BufWriter::new(try!(OpenOptions::new()
        .write(true).create(true).append(true).open(format!("{}.xml", file_name))));

    let current_date_time = tm.strftime("%Y.%m.%d - %H:%M:%S").unwrap();

    try!(write!(file_handle, "<measure>\n"));
    try!(write!(file_handle, "<port>{}</port>\n", local_port));
    try!(write!(file_handle, "<date_time>{}</date_time>\n", &current_date_time));
    try!(write!(file_handle, "<data>\n"));

    // try!(write!(file_handle, "{:?}\n", data));

    match data {
        &StationDataType::SingleData(time_stamp_tm, voltage) => {
            if let Ok(time_stamp) = time_stamp_tm.strftime("%Y.%m.%d - %H:%M:%S") {
                try!(write!(file_handle, "    <time_stamp>{}</time_stamp>\n", time_stamp));
                try!(write!(file_handle, "    <voltage>{}</voltage>\n", voltage));
            }
        },
        &StationDataType::MultipleData(ref full_data_set) => {
            if let Ok(time_stamp) = full_data_set.time_stamp.strftime("%Y.%m.%d - %H:%M:%S") {
                try!(write!(file_handle, "    <time_stamp>{}</time_stamp>\n", time_stamp));
                try!(write!(file_handle, "    <air_temperature>{}</air_temperature>\n", full_data_set.air_temperature));
                try!(write!(file_handle, "    <air_relative_humidity>{}</air_relative_humidity>\n", full_data_set.air_relative_humidity));
                try!(write!(file_handle, "    <solar_radiation>{}</solar_radiation>\n", full_data_set.solar_radiation));
                try!(write!(file_handle, "    <soil_water_content>{}</soil_water_content>\n", full_data_set.soil_water_content));
                try!(write!(file_handle, "    <soil_temperature>{}</soil_temperature>\n", full_data_set.soil_temperature));
                try!(write!(file_handle, "    <wind_speed>{}</wind_speed>\n", full_data_set.wind_speed));
                try!(write!(file_handle, "    <wind_max>{}</wind_max>\n", full_data_set.wind_max));
                try!(write!(file_handle, "    <wind_direction>{}</wind_direction>\n", full_data_set.wind_direction));
                try!(write!(file_handle, "    <percipitation>{}</percipitation>\n", full_data_set.percipitation));
                try!(write!(file_handle, "    <air_pressure>{}</air_pressure>\n", full_data_set.air_pressure));
            }
        }
    }

    try!(write!(file_handle, "</data>\n"));
    try!(write!(file_handle, "</measure>\n\n"));

    Ok(())
}

fn write_csv_data(data: &StationDataType, file_name: &str) -> Result<()> {
    let mut file_handle = BufWriter::new(try!(OpenOptions::new()
        .write(true).create(true).append(true).open(format!("{}.csv", file_name))));

        match data {
            &StationDataType::SingleData(time_stamp_tm, voltage) => {
                if let Ok(time_stamp) = time_stamp_tm.strftime("%Y.%m.%d - %H:%M:%S") {
                    try!(write!(file_handle, "\"{}\", {}\n", time_stamp, voltage));
                }
            },
            &StationDataType::MultipleData(ref full_data_set) => {
                if let Ok(time_stamp) = full_data_set.time_stamp.strftime("%Y.%m.%d - %H:%M:%S") {
                    try!(write!(file_handle, "\"{}\", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}\n",
                        time_stamp,
                        full_data_set.air_temperature,
                        full_data_set.air_relative_humidity,
                        full_data_set.solar_radiation,
                        full_data_set.soil_water_content,
                        full_data_set.soil_temperature,
                        full_data_set.wind_speed,
                        full_data_set.wind_max,
                        full_data_set.wind_direction,
                        full_data_set.percipitation,
                        full_data_set.air_pressure
                    ));
                }
            }
        }

    Ok(())
}

fn port_to_station(port: u16) -> String{
    match port {
        2100 => "2100_Na".to_string(),
        2101 => "2101_SG".to_string(),
        2102 => "2102_PdA".to_string(),
        2103 => "2103_LC".to_string(),
        2104 => "2104_Tue".to_string(),
        _ => "unknown".to_string()
    }
}

fn handle_client(stream: &mut TcpStream, remote_addr: &SocketAddr,
    all_data_file: &Arc<Mutex<String>>, monthly_data_folder: &Arc<Mutex<String>>) -> Result<()> {
    info!("Client socket address: {}", remote_addr);

    let local_addr = try!(stream.local_addr());

    let local_port = match local_addr {
        SocketAddr::V4(local_addr) => local_addr.port(),
        SocketAddr::V6(local_addr) => local_addr.port()
    };

    info!("Port: {}", local_port);

    let mut buffer = Vec::new();

    let len = try!(stream.read_to_end(&mut buffer));
    info!("Number of bytes received: {}", len);

    if buffer.len() > HEADER_LENGTH {
        let (buffer_left, buffer_right) = buffer.split_at(HEADER_LENGTH);

        let str_header = String::from_utf8_lossy(buffer_left);
        let str_data = String::from_utf8_lossy(buffer_right);

        info!("Header: {:?}", buffer_left);
        info!("Data: {:?}", buffer_right);

        info!("Header (ASCII): '{}'", str_header);
        info!("Data (ASCII): '{}'", str_data);

        let station_folder = port_to_station(local_port);

        match parse_text_data(&buffer_right) {
            Ok(parsed_data) => {
                match all_data_file.lock() {
                    Ok(all_data_file) => {
                        let tm = now();
                        let file_name = format!("{}/{}", station_folder, *all_data_file);
                        try!(write_xml_data(tm, local_port, &parsed_data, &file_name));
                        try!(write_csv_data(&parsed_data, &file_name));
                    },
                    Err(e) => info!("Mutex (poison) error (all_data_file): {}", e)
                }

                match monthly_data_folder.lock() {
                    Ok(monthly_data_folder) => {
                        let tm = now();
                        let current_year = tm.strftime("%Y").unwrap();
                        let current_month = tm.strftime("%m").unwrap();
                        // TODO: create separate folder for year and month in Rust
                        let file_name = format!("{}/{}/{}_{}", *monthly_data_folder, station_folder, current_year, current_month);
                        try!(write_xml_data(tm, local_port, &parsed_data, &file_name));
                        try!(write_csv_data(&parsed_data, &file_name));
                    },
                    Err(e) => info!("Mutex (poison) error (monthly_data_folder): {}", e)
                }

            },
            Err(e) => {
                info!("Could not parse data: {}", e);
            }

        }
    } else if buffer.len() < HEADER_LENGTH {
        info!("Invalid header (less than {} bytes received)!", HEADER_LENGTH);
        info!("Bytes: {:?}", buffer);
        info!("Bytes (ASCII): '{}'", String::from_utf8_lossy(&buffer));
    } else { // buffer.len() == HEADER_LENGTH -> no data, only header
        info!("No data received, just header.");
        info!("Bytes: {:?}", buffer);
        info!("Bytes (ASCII): '{}'", String::from_utf8_lossy(&buffer));
    }

    Ok(())
}

pub fn start_service(config: Configuration) {
    let mut listeners = Vec::new();

    for port in config.ports {
        match TcpListener::bind(("0.0.0.0", port)) {
            Ok(listener) => {
                info!("Create listener for port {}", port);
                listeners.push(listener);
            },
            Err(e) => info!("Network error: {}", e)
        }
    }

    let all_data_file = Arc::new(Mutex::new(config.all_data_file.clone()));
    let monthly_data_folder = Arc::new(Mutex::new(config.monthly_data_folder.clone()));

    for listener in listeners {
        let all_data_file = all_data_file.clone();
        let monthly_data_folder = monthly_data_folder.clone();
        spawn(move|| {
            loop {
                let result = listener.accept();
                if let Ok(result) = result {
                    let (mut stream, addr) = result;
                    if let Err(io_error) = handle_client(&mut stream, &addr,
                            &all_data_file, &monthly_data_folder) {
                        info!("IOError: {}", io_error);
                    }
                }
            }
        });
    }
}
<commit_msg>fix date output format for xml and csv<commit_after>//! Provides the server and handles the incomming requests
//! All ports are handled by the same function

// System modules:
use std::net::{TcpListener, TcpStream, SocketAddr};
use std::thread::spawn;
use std::io::prelude::*;
use std::sync::{Arc, Mutex};
use std::fs::OpenOptions;
use std::io::BufWriter;
use std::io::Result;
use time::{now, Tm};

// Internal modules:
use configuration::{Configuration, HEADER_LENGTH};
use data_parser::{parse_text_data, StationDataType};

fn write_xml_data(tm: Tm, local_port: u16, data: &StationDataType, file_name: &str) -> Result<()> {
    let mut file_handle = BufWriter::new(try!(OpenOptions::new()
        .write(true).create(true).append(true).open(format!("{}.xml", file_name))));

    let current_date_time = tm.strftime("%Y.%m.%d - %H:%M:%S").unwrap();

    try!(write!(file_handle, "<measure>\n"));
    try!(write!(file_handle, "<port>{}</port>\n", local_port));
    try!(write!(file_handle, "<date_time>{}</date_time>\n", &current_date_time));
    try!(write!(file_handle, "<data>\n"));

    // try!(write!(file_handle, "{:?}\n", data));

    match data {
        &StationDataType::SingleData(time_stamp_tm, voltage) => {
            if let Ok(time_stamp) = time_stamp_tm.strftime("%Y-%m-%d %H:%M:%S") {
                try!(write!(file_handle, "    <time_stamp>{}</time_stamp>\n", time_stamp));
                try!(write!(file_handle, "    <voltage>{}</voltage>\n", voltage));
            }
        },
        &StationDataType::MultipleData(ref full_data_set) => {
            if let Ok(time_stamp) = full_data_set.time_stamp.strftime("%Y-%m-%d %H:%M:%S") {
                try!(write!(file_handle, "    <time_stamp>{}</time_stamp>\n", time_stamp));
                try!(write!(file_handle, "    <air_temperature>{}</air_temperature>\n", full_data_set.air_temperature));
                try!(write!(file_handle, "    <air_relative_humidity>{}</air_relative_humidity>\n", full_data_set.air_relative_humidity));
                try!(write!(file_handle, "    <solar_radiation>{}</solar_radiation>\n", full_data_set.solar_radiation));
                try!(write!(file_handle, "    <soil_water_content>{}</soil_water_content>\n", full_data_set.soil_water_content));
                try!(write!(file_handle, "    <soil_temperature>{}</soil_temperature>\n", full_data_set.soil_temperature));
                try!(write!(file_handle, "    <wind_speed>{}</wind_speed>\n", full_data_set.wind_speed));
                try!(write!(file_handle, "    <wind_max>{}</wind_max>\n", full_data_set.wind_max));
                try!(write!(file_handle, "    <wind_direction>{}</wind_direction>\n", full_data_set.wind_direction));
                try!(write!(file_handle, "    <percipitation>{}</percipitation>\n", full_data_set.percipitation));
                try!(write!(file_handle, "    <air_pressure>{}</air_pressure>\n", full_data_set.air_pressure));
            }
        }
    }

    try!(write!(file_handle, "</data>\n"));
    try!(write!(file_handle, "</measure>\n\n"));

    Ok(())
}

fn write_csv_data(data: &StationDataType, file_name: &str) -> Result<()> {
    let mut file_handle = BufWriter::new(try!(OpenOptions::new()
        .write(true).create(true).append(true).open(format!("{}.csv", file_name))));

        match data {
            &StationDataType::SingleData(time_stamp_tm, voltage) => {
                if let Ok(time_stamp) = time_stamp_tm.strftime("%Y-%m-%d %H:%M:%S") {
                    try!(write!(file_handle, "\"{}\", {}\n", time_stamp, voltage));
                }
            },
            &StationDataType::MultipleData(ref full_data_set) => {
                if let Ok(time_stamp) = full_data_set.time_stamp.strftime("%Y-%m-%d %H:%M:%S") {
                    try!(write!(file_handle, "\"{}\", {}, {}, {}, {}, {}, {}, {}, {}, {}, {}\n",
                        time_stamp,
                        full_data_set.air_temperature,
                        full_data_set.air_relative_humidity,
                        full_data_set.solar_radiation,
                        full_data_set.soil_water_content,
                        full_data_set.soil_temperature,
                        full_data_set.wind_speed,
                        full_data_set.wind_max,
                        full_data_set.wind_direction,
                        full_data_set.percipitation,
                        full_data_set.air_pressure
                    ));
                }
            }
        }

    Ok(())
}

fn port_to_station(port: u16) -> String{
    match port {
        2100 => "2100_Na".to_string(),
        2101 => "2101_SG".to_string(),
        2102 => "2102_PdA".to_string(),
        2103 => "2103_LC".to_string(),
        2104 => "2104_Tue".to_string(),
        _ => "unknown".to_string()
    }
}

fn handle_client(stream: &mut TcpStream, remote_addr: &SocketAddr,
    all_data_file: &Arc<Mutex<String>>, monthly_data_folder: &Arc<Mutex<String>>) -> Result<()> {
    info!("Client socket address: {}", remote_addr);

    let local_addr = try!(stream.local_addr());

    let local_port = match local_addr {
        SocketAddr::V4(local_addr) => local_addr.port(),
        SocketAddr::V6(local_addr) => local_addr.port()
    };

    info!("Port: {}", local_port);

    let mut buffer = Vec::new();

    let len = try!(stream.read_to_end(&mut buffer));
    info!("Number of bytes received: {}", len);

    if buffer.len() > HEADER_LENGTH {
        let (buffer_left, buffer_right) = buffer.split_at(HEADER_LENGTH);

        let str_header = String::from_utf8_lossy(buffer_left);
        let str_data = String::from_utf8_lossy(buffer_right);

        info!("Header: {:?}", buffer_left);
        info!("Data: {:?}", buffer_right);

        info!("Header (ASCII): '{}'", str_header);
        info!("Data (ASCII): '{}'", str_data);

        let station_folder = port_to_station(local_port);

        match parse_text_data(&buffer_right) {
            Ok(parsed_data) => {
                info!("Data parsed correctly");
                match all_data_file.lock() {
                    Ok(all_data_file) => {
                        let tm = now();
                        let file_name = format!("{}/{}", station_folder, *all_data_file);
                        try!(write_xml_data(tm, local_port, &parsed_data, &file_name));
                        try!(write_csv_data(&parsed_data, &file_name));
                    },
                    Err(e) => info!("Mutex (poison) error (all_data_file): {}", e)
                }

                match monthly_data_folder.lock() {
                    Ok(monthly_data_folder) => {
                        let tm = now();
                        let current_year = tm.strftime("%Y").unwrap();
                        let current_month = tm.strftime("%m").unwrap();
                        // TODO: create separate folder for year and month in Rust
                        let file_name = format!("{}/{}/{}_{}", *monthly_data_folder, station_folder, current_year, current_month);
                        try!(write_xml_data(tm, local_port, &parsed_data, &file_name));
                        try!(write_csv_data(&parsed_data, &file_name));
                    },
                    Err(e) => info!("Mutex (poison) error (monthly_data_folder): {}", e)
                }

            },
            Err(e) => {
                info!("Could not parse data: {}", e);
            }
        }
    } else if buffer.len() < HEADER_LENGTH {
        info!("Invalid header (less than {} bytes received)!", HEADER_LENGTH);
        info!("Bytes: {:?}", buffer);
        info!("Bytes (ASCII): '{}'", String::from_utf8_lossy(&buffer));
    } else { // buffer.len() == HEADER_LENGTH -> no data, only header
        info!("No data received, just header.");
        info!("Bytes: {:?}", buffer);
        info!("Bytes (ASCII): '{}'", String::from_utf8_lossy(&buffer));
    }

    info!("handle_client finished")

    Ok(())
}

pub fn start_service(config: Configuration) {
    let mut listeners = Vec::new();

    for port in config.ports {
        match TcpListener::bind(("0.0.0.0", port)) {
            Ok(listener) => {
                info!("Create listener for port {}", port);
                listeners.push(listener);
            },
            Err(e) => info!("Network error: {}", e)
        }
    }

    let all_data_file = Arc::new(Mutex::new(config.all_data_file.clone()));
    let monthly_data_folder = Arc::new(Mutex::new(config.monthly_data_folder.clone()));

    for listener in listeners {
        let all_data_file = all_data_file.clone();
        let monthly_data_folder = monthly_data_folder.clone();
        spawn(move|| {
            loop {
                let result = listener.accept();
                if let Ok(result) = result {
                    let (mut stream, addr) = result;
                    if let Err(io_error) = handle_client(&mut stream, &addr,
                            &all_data_file, &monthly_data_folder) {
                        info!("IOError: {}", io_error);
                    }
                }
            }
        });
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::io;
use std::mem;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
#[cfg(unix)]
use libc::c_int;
#[cfg(windows)]
use winapi::c_int;

use sys;
use sys::c;

pub struct Socket {
    inner: sys::Socket,
}

impl Socket {
    pub fn new(family: c_int, ty: c_int) -> io::Result<Socket> {
        Ok(Socket { inner: try!(sys::Socket::new(family, ty)) })
    }

    pub fn bind(&self, addr: &SocketAddr) -> io::Result<()> {
        #[cfg(not(all(target_arch = "aarch64",target_os = "android")))]
        use libc::socklen_t as len_t;
        #[cfg(all(target_arch = "aarch64",target_os = "android"))]
        use libc::c_int as len_t;

        let (addr, len) = addr2raw(addr);
        unsafe {
            ::cvt(c::bind(self.inner.raw(), addr, len as len_t)).map(|_| ())
        }
    }

    pub fn listen(&self, backlog: i32) -> io::Result<()> {
        unsafe {
            ::cvt(c::listen(self.inner.raw(), backlog)).map(|_| ())
        }
    }

    pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
        let (addr, len) = addr2raw(addr);
        unsafe {
            ::cvt(c::connect(self.inner.raw(), addr, len)).map(|_| ())
        }
    }

    pub fn getsockname(&self) -> io::Result<SocketAddr> {
        unsafe {
            let mut storage: c::sockaddr_storage = mem::zeroed();
            let mut len = mem::size_of_val(&storage) as c::socklen_t;
            try!(::cvt(c::getsockname(self.inner.raw(),
                                      &mut storage as *mut _ as *mut _,
                                      &mut len)));
            raw2addr(&storage, len)
        }
    }
}

impl fmt::Debug for Socket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.inner.raw().fmt(f)
    }
}

impl ::AsInner for Socket {
    type Inner = sys::Socket;
    fn as_inner(&self) -> &sys::Socket { &self.inner }
}

impl ::FromInner for Socket {
    type Inner = sys::Socket;
    fn from_inner(sock: sys::Socket) -> Socket {
        Socket { inner: sock }
    }
}

impl ::IntoInner for Socket {
    type Inner = sys::Socket;
    fn into_inner(self) -> sys::Socket { self.inner }
}

fn addr2raw(addr: &SocketAddr) -> (*const c::sockaddr, c::socklen_t) {
    match *addr {
        SocketAddr::V4(ref a) => {
            (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t)
        }
        SocketAddr::V6(ref a) => {
            (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t)
        }
    }
}

fn raw2addr(storage: &c::sockaddr_storage, len: c::socklen_t) -> io::Result<SocketAddr> {
    match storage.ss_family as c_int {
        c::AF_INET => {
            unsafe {
                assert!(len as usize >= mem::size_of::<c::sockaddr_in>());
                let sa = storage as *const _ as *const c::sockaddr_in;
                let bits = c::sockaddr_in_u32(&(*sa));
                let ip = Ipv4Addr::new((bits >> 24) as u8,
                                       (bits >> 16) as u8,
                                       (bits >> 8) as u8,
                                       bits as u8);
                Ok(SocketAddr::V4(SocketAddrV4::new(ip, ::ntoh((*sa).sin_port))))
            }
        }
        c::AF_INET6 => {
            unsafe {
                assert!(len as usize >= mem::size_of::<c::sockaddr_in6>());

                let sa = storage as *const _ as *const c::sockaddr_in6;
                let arr = (*sa).sin6_addr.s6_addr;

                let ip = Ipv6Addr::new(
                    (arr[0] as u16) << 8 | (arr[1] as u16),
                    (arr[2] as u16) << 8 | (arr[3] as u16),
                    (arr[4] as u16) << 8 | (arr[5] as u16),
                    (arr[6] as u16) << 8 | (arr[7] as u16),
                    (arr[8] as u16) << 8 | (arr[9] as u16),
                    (arr[10] as u16) << 8 | (arr[11] as u16),
                    (arr[12] as u16) << 8 | (arr[13] as u16),
                    (arr[14] as u16) << 8 | (arr[15] as u16),
                );

                Ok(SocketAddr::V6(SocketAddrV6::new(ip,
                                                    ::ntoh((*sa).sin6_port),
                                                    (*sa).sin6_flowinfo,
                                                    (*sa).sin6_scope_id)))
            }
        }
        _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid argument")),
    }
}
<commit_msg>Fix compile on Windows<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use std::fmt;
use std::io;
use std::mem;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6};
#[cfg(unix)]
use libc::c_int;
#[cfg(windows)]
use winapi::c_int;

use sys;
use sys::c;

pub struct Socket {
    inner: sys::Socket,
}

impl Socket {
    pub fn new(family: c_int, ty: c_int) -> io::Result<Socket> {
        Ok(Socket { inner: try!(sys::Socket::new(family, ty)) })
    }

    pub fn bind(&self, addr: &SocketAddr) -> io::Result<()> {
        #[cfg(not(all(target_arch = "aarch64",target_os = "android")))]
        use sys::c::socklen_t as len_t;
        #[cfg(all(target_arch = "aarch64",target_os = "android"))]
        use libc::c_int as len_t;

        let (addr, len) = addr2raw(addr);
        unsafe {
            ::cvt(c::bind(self.inner.raw(), addr, len as len_t)).map(|_| ())
        }
    }

    pub fn listen(&self, backlog: i32) -> io::Result<()> {
        unsafe {
            ::cvt(c::listen(self.inner.raw(), backlog)).map(|_| ())
        }
    }

    pub fn connect(&self, addr: &SocketAddr) -> io::Result<()> {
        let (addr, len) = addr2raw(addr);
        unsafe {
            ::cvt(c::connect(self.inner.raw(), addr, len)).map(|_| ())
        }
    }

    pub fn getsockname(&self) -> io::Result<SocketAddr> {
        unsafe {
            let mut storage: c::sockaddr_storage = mem::zeroed();
            let mut len = mem::size_of_val(&storage) as c::socklen_t;
            try!(::cvt(c::getsockname(self.inner.raw(),
                                      &mut storage as *mut _ as *mut _,
                                      &mut len)));
            raw2addr(&storage, len)
        }
    }
}

impl fmt::Debug for Socket {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.inner.raw().fmt(f)
    }
}

impl ::AsInner for Socket {
    type Inner = sys::Socket;
    fn as_inner(&self) -> &sys::Socket { &self.inner }
}

impl ::FromInner for Socket {
    type Inner = sys::Socket;
    fn from_inner(sock: sys::Socket) -> Socket {
        Socket { inner: sock }
    }
}

impl ::IntoInner for Socket {
    type Inner = sys::Socket;
    fn into_inner(self) -> sys::Socket { self.inner }
}

fn addr2raw(addr: &SocketAddr) -> (*const c::sockaddr, c::socklen_t) {
    match *addr {
        SocketAddr::V4(ref a) => {
            (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t)
        }
        SocketAddr::V6(ref a) => {
            (a as *const _ as *const _, mem::size_of_val(a) as c::socklen_t)
        }
    }
}

fn raw2addr(storage: &c::sockaddr_storage, len: c::socklen_t) -> io::Result<SocketAddr> {
    match storage.ss_family as c_int {
        c::AF_INET => {
            unsafe {
                assert!(len as usize >= mem::size_of::<c::sockaddr_in>());
                let sa = storage as *const _ as *const c::sockaddr_in;
                let bits = c::sockaddr_in_u32(&(*sa));
                let ip = Ipv4Addr::new((bits >> 24) as u8,
                                       (bits >> 16) as u8,
                                       (bits >> 8) as u8,
                                       bits as u8);
                Ok(SocketAddr::V4(SocketAddrV4::new(ip, ::ntoh((*sa).sin_port))))
            }
        }
        c::AF_INET6 => {
            unsafe {
                assert!(len as usize >= mem::size_of::<c::sockaddr_in6>());

                let sa = storage as *const _ as *const c::sockaddr_in6;
                let arr = (*sa).sin6_addr.s6_addr;

                let ip = Ipv6Addr::new(
                    (arr[0] as u16) << 8 | (arr[1] as u16),
                    (arr[2] as u16) << 8 | (arr[3] as u16),
                    (arr[4] as u16) << 8 | (arr[5] as u16),
                    (arr[6] as u16) << 8 | (arr[7] as u16),
                    (arr[8] as u16) << 8 | (arr[9] as u16),
                    (arr[10] as u16) << 8 | (arr[11] as u16),
                    (arr[12] as u16) << 8 | (arr[13] as u16),
                    (arr[14] as u16) << 8 | (arr[15] as u16),
                );

                Ok(SocketAddr::V6(SocketAddrV6::new(ip,
                                                    ::ntoh((*sa).sin6_port),
                                                    (*sa).sin6_flowinfo,
                                                    (*sa).sin6_scope_id)))
            }
        }
        _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "invalid argument")),
    }
}
<|endoftext|>


<commit_before>//! A module that represents a logical clause and works with terms.

use terms::*;
use clause::OperationType::DERIVE;
// use clause::OperationType::JOIN;
// use clause::OperationType::DELETE;
// use clause::OperationType::BRANCH;

/// Represents a logical equation in conjunctive normal form
/// Consists of a finite collection of Terms of symbols chained via OR
/// that are chained in the clause with AND operations.
#[derive(Debug)]
pub struct Clause {
    pub terms: Vec<Term>,
}

impl PartialEq for Clause {
    fn eq(&self, other: &Clause) -> bool {
        for t in &self.terms {
            if !other.contains(t) {
                return false
            }
        }

        for t in &other.terms {
            if !self.contains(t) {
                return false
            }
        }

        return true;
    }
}

/// Describes a reduce operation type
pub enum OperationType {
    /// Eliminates single symbol terms
    DERIVE,
    /// Joins two terms, eliminating at most one symbol
    JOIN,
    /// Run a branching scenario
    BRANCH,
}

/// Represents a reduce operation and every piece of data involved
#[allow(dead_code)]
pub struct Operation<'a> {
    _type: OperationType,
    term: Vec<&'a Term>,
    symbol: Vec<&'a Symbol>,
}

impl Clause {

    /// Insert a term naively into an clause
    pub fn insert(&mut self, term: Term) {
        self.terms.push(term);
    }

    /// Removes a term from the clause based on it's values, not
    ///   the order the symbols appear in the term in.
    ///
    /// This is a relatively slow function (it runs on linear time) so
    ///   should be avoided whenever possible.
    pub fn remove(&mut self, term: Term) {
        if !self.contains(&term) {
            return;
        }

        /* Only push to new vector if value is right */
        let mut newvec: Vec<Term> = Vec::new();
        for t in &self.terms {
            if term != *t {
                newvec.push(t.clone());
            }
        }

        self.terms = newvec;
    }

    /// Analyse the clause and return a series of possible operations to the
    ///   user. If then calling `reduce` with the data returned from this
    ///   function it will let some options take prescedence over others
    ///   according to the resolution scheme algorithm
    pub fn consider_reduce(&self) -> Vec<Operation> {
        let ops = Vec::new();
        return ops;
    }

    /// Run a single reduce step and return the exact reduce operation
    pub fn reduce(&mut self) -> Operation {

        /* First we search for single defined terms */
        let mut single: Term = self.search_single_terms().unwrap();
        self.reduce_single_term(&mut single);

        return Operation {
            _type: DERIVE,
            term: Vec::new(),
            symbol: Vec::new(),
        };
    }

    /// Check if a term is contained in this clause. The order of symbols
    ///   in the terms don't matter, as long as the values are the same
    ///
    /// This function is relatively slow (O(n²)) because it compares
    ///   all symbols in a term with all symbols in the term provided
    pub fn contains(&self, term: &Term) -> bool {
        for t in &self.terms {
            if t == term {
                return true;
            }
        }

        return false;
    }

    //////////////////////////////////////////////////////////////

    /// Reduces a single term from the clause (rule 1)
    fn reduce_single_term(&mut self, term: &mut Term) {

        let mut op = Operation {
            _type: DERIVE,
            term: Vec::new(),
            symbol: Vec::new(),
        };

        op.term.push(term);
        for symbol in &term.symbols {
            op.symbol.push(symbol);
        }
        self.remove(term.clone());
    }

    /// Will return the first single-value term it encounters
    fn search_single_terms(&mut self) -> Option<Term> {
        for term in self.terms.iter() {
            if term.symbols.len() == 1 {
                return Some(term.clone());
            }
        }
        return None;
    }
}

/// A helper macro that can be used to easily create a clause from multiple
///   terms. The clause will use conjuctive-normal-form to connect terms.
///
/// clause![term![symbol!["A"]], b, c]]
#[macro_export]
macro_rules! clause {
    ( $( $x:expr ),* ) => {{
        let mut tmp: Vec<Term> = Vec::new();
        $( tmp.push($x); )*
        Clause { terms: tmp }
    }};
}<commit_msg>ACTUALLY implementing function for #8 😅<commit_after>//! A module that represents a logical clause and works with terms.

use terms::*;
use clause::OperationType::DERIVE;
// use clause::OperationType::JOIN;
// use clause::OperationType::DELETE;
// use clause::OperationType::BRANCH;

use std::collections::HashMap;

/// Represents a logical equation in conjunctive normal form
/// Consists of a finite collection of Terms of symbols chained via OR
/// that are chained in the clause with AND operations.
#[derive(Debug)]
pub struct Clause {
    pub terms: Vec<Term>,
}

impl PartialEq for Clause {
    fn eq(&self, other: &Clause) -> bool {
        for t in &self.terms {
            if !other.contains(t) {
                return false
            }
        }

        for t in &other.terms {
            if !self.contains(t) {
                return false
            }
        }

        return true;
    }
}

/// Describes a reduce operation type
pub enum OperationType {
    /// Eliminates single symbol terms
    DERIVE,
    /// Joins two terms, eliminating at most one symbol
    JOIN,
    /// Run a branching scenario
    BRANCH,
}

/// Represents a reduce operation and every piece of data involved
#[allow(dead_code)]
pub struct Operation<'a> {
    _type: OperationType,
    term: Vec<&'a Term>,
    symbol: Vec<&'a Symbol>,
}

impl Clause {

    /// Insert a term naively into an clause
    pub fn insert(&mut self, term: Term) {
        self.terms.push(term);
    }

    /// Removes a term from the clause based on it's values, not
    ///   the order the symbols appear in the term in.
    ///
    /// This is a relatively slow function (it runs on linear time) so
    ///   should be avoided whenever possible.
    pub fn remove(&mut self, term: Term) {
        if !self.contains(&term) {
            return;
        }

        /* Only push to new vector if value is right */
        let mut newvec: Vec<Term> = Vec::new();
        for t in &self.terms {
            if term != *t {
                newvec.push(t.clone());
            }
        }

        self.terms = newvec;
    }

    /// Analyse the clause and return a series of possible operations to the
    ///   user. If then calling `reduce` with the data returned from this
    ///   function it will let some options take prescedence over others
    ///   according to the resolution scheme algorithm
    pub fn consider_reduce(&self) -> Vec<Operation> {
        let ops = Vec::new();
        return ops;
    }

    /// Run a single reduce step and return the exact reduce operation
    pub fn reduce(&mut self) -> Operation {

        /* First we search for single defined terms */
        let mut single: Term = self.search_single_terms().unwrap();
        self.reduce_single_term(&mut single);

        return Operation {
            _type: DERIVE,
            term: Vec::new(),
            symbol: Vec::new(),
        };
    }

    /// Check if a term is contained in this clause. The order of symbols
    ///   in the terms don't matter, as long as the values are the same
    ///
    /// This function is relatively slow (O(n²)) because it compares
    ///   all symbols in a term with all symbols in the term provided
    pub fn contains(&self, term: &Term) -> bool {
        for t in &self.terms {
            if t == term {
                return true;
            }
        }

        return false;
    }

    /// A function that counts all occurances of symbols in the clause. Can be used
    ///   to estimate a best-reduction-strategy for a given clause to avoid potential
    ///   locks further steps down the line.
    pub fn count_symbols(&self) -> HashMap<Symbol, i32> {
        let mut map = HashMap::new();

        for t in &self.terms {

            for sym in &t.symbols {

                if map.contains_key(sym) {
                    let num: i32 = *map.get(sym).unwrap();
                    map.insert(sym.clone(), num + 1);
                } else {
                    map.insert(sym.clone(), 1);
                }
            }
        }
        return map;
    }

    //////////////////////////////////////////////////////////////

    /// Reduces a single term from the clause (rule 1)
    fn reduce_single_term(&mut self, term: &mut Term) {

        let mut op = Operation {
            _type: DERIVE,
            term: Vec::new(),
            symbol: Vec::new(),
        };

        op.term.push(term);
        for symbol in &term.symbols {
            op.symbol.push(symbol);
        }
        self.remove(term.clone());
    }

    /// Will return the first single-value term it encounters
    fn search_single_terms(&mut self) -> Option<Term> {
        for term in self.terms.iter() {
            if term.symbols.len() == 1 {
                return Some(term.clone());
            }
        }
        return None;
    }
}

/// A helper macro that can be used to easily create a clause from multiple
///   terms. The clause will use conjuctive-normal-form to connect terms.
///
/// clause![term![symbol!["A"]], b, c]]
#[macro_export]
macro_rules! clause {
    ( $( $x:expr ),* ) => {{
        let mut tmp: Vec<Term> = Vec::new();
        $( tmp.push($x); )*
        Clause { terms: tmp }
    }};
}<|endoftext|>


<commit_before>use std::convert::{From, Into};
use std::fmt::{Debug, Display, Formatter};
use std::fmt;
use std::result::Result;
use std::str::FromStr;

use regex::Regex;

use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;

#[derive(Clone)]
#[derive(Hash)]
#[derive(Eq)]
#[derive(PartialEq)]
/**
 * FileID
 *
 * A FileID contains of two parts: The ID type and the Hash. For example the FileID
 *
 *  UUID-235-1215-1212
 *
 * has a type ("UUID") and a Hash ("235-1215-1212").
 */
pub struct FileID {
    id:         FileHash,
    id_type:    FileIDType,
}

impl FileID {

    pub fn new(id_type: FileIDType, id: FileHash) -> FileID {
        FileID {
            id: id,
            id_type: id_type,
        }
    }

    /**
     * Get the type of the FileID
     */
    pub fn get_type(&self) -> FileIDType {
        self.id_type.clone()
    }

    /**
     * Get the Hash of the FileID
     */
    pub fn get_id(&self) -> FileHash {
        self.id.clone()
    }

    /**
     * Parse a String into a FileID, if possible
     */
    pub fn parse(string: &String) -> Option<FileID> {
        // we assume that it is an path
        let regex = Regex::new(r"([:alnum:]*)-([:upper:]*)-([A-Za-z0-9-_]*)\.(.*)").unwrap();
        let s = string.split("/").last().unwrap_or("");

        debug!("Regex build: {:?}", regex);
        debug!("Matching string: '{}'", s);
        regex.captures(s).and_then(|capts| {
            // first one is the whole string, index 1-N are the matches.
            if capts.len() != 5 {
                debug!("Matches, but not expected number of groups");
                return None;
            }
            debug!("Matches: {}", capts.len());

            let modname     = capts.at(1).unwrap();
            let hashname    = capts.at(2).unwrap();
            let mut hash    = capts.at(3).unwrap();

            debug!("Destructure FilePath to ID:");
            debug!("                  FilePath: {:?}", s);
            debug!("               Module Name: {:?}", modname);
            debug!("                 Hash Name: {:?}", hashname);
            debug!("                      Hash: {:?}", hash);

            FileIDType::from_str(hashname).map(|idtype| {
                debug!("ID type = {:?}", idtype);
                Some(FileID {
                    id: FileHash::from(hash),
                    id_type: idtype,
                })
            }).ok()
        }).unwrap_or({
            debug!("Did not match");
            debug!("It is no path, actually. So we assume it is an ID already");
            None
        })
    }

}

impl Debug for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "FileID[{:?}]: {:?}",
               self.id_type,
               self.id));
        Ok(())
    }

}

impl Display for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "{}-{}", self.id_type, self.id));
        Ok(())
    }

}

impl Into<String> for FileID {

    fn into(self) -> String {
        let typestr : String = self.id_type.into();
        let idstr   : String = self.id.into();
        typestr + "-" + &idstr[..]
    }
}

#[cfg(test)]
mod test {

    use storage::file::id::FileID;
    use storage::file::id_type::FileIDType;

    #[test]
    fn file_id_from_string() {
        setup_logger();

        let s1 = String::from("/home/user/testmodule-UUID-some-id.imag");
        let s2 = String::from("/home/user/testmodule-UUID-some-id.extension.imag");
        let s3 = String::from("/home/user/testmodule-NOHASH-some-id.imag");

        let id1 = FileID::parse(&s1).unwrap();
        let id2 = FileID::parse(&s2).unwrap();
        assert!(FileID::parse(&s3).is_none());

        println!("Id 1 : {:?}", id1);
        println!("Id 2 : {:?}", id2);

        assert_eq!(FileIDType::UUID, id1.get_type());
        assert_eq!(FileIDType::UUID, id2.get_type());

        let h1 : String = id1.get_id().into();
        let h2 : String = id2.get_id().into();

        assert_eq!(String::from("some-id"), h1);
        assert_eq!(String::from("some-id"), h2);

        let f1 : String = id1.into();
        let f2 : String = id2.into();

        assert_eq!(String::from("UUID-some-id"), f1);
        assert_eq!(String::from("UUID-some-id"), f2);
    }

    fn setup_logger() {
        extern crate log;
        use log::{LogLevelFilter, set_logger};
        use runtime::ImagLogger;

        log::set_logger(|max_log_lvl| {
            let lvl = LogLevelFilter::Debug;
            max_log_lvl.set(lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        });
        debug!("Init logger for test");
    }

}

<commit_msg>Remove unneeded Keyword "mut"<commit_after>use std::convert::{From, Into};
use std::fmt::{Debug, Display, Formatter};
use std::fmt;
use std::result::Result;
use std::str::FromStr;

use regex::Regex;

use storage::file::id_type::FileIDType;
use storage::file::hash::FileHash;

#[derive(Clone)]
#[derive(Hash)]
#[derive(Eq)]
#[derive(PartialEq)]
/**
 * FileID
 *
 * A FileID contains of two parts: The ID type and the Hash. For example the FileID
 *
 *  UUID-235-1215-1212
 *
 * has a type ("UUID") and a Hash ("235-1215-1212").
 */
pub struct FileID {
    id:         FileHash,
    id_type:    FileIDType,
}

impl FileID {

    pub fn new(id_type: FileIDType, id: FileHash) -> FileID {
        FileID {
            id: id,
            id_type: id_type,
        }
    }

    /**
     * Get the type of the FileID
     */
    pub fn get_type(&self) -> FileIDType {
        self.id_type.clone()
    }

    /**
     * Get the Hash of the FileID
     */
    pub fn get_id(&self) -> FileHash {
        self.id.clone()
    }

    /**
     * Parse a String into a FileID, if possible
     */
    pub fn parse(string: &String) -> Option<FileID> {
        // we assume that it is an path
        let regex = Regex::new(r"([:alnum:]*)-([:upper:]*)-([A-Za-z0-9-_]*)\.(.*)").unwrap();
        let s = string.split("/").last().unwrap_or("");

        debug!("Regex build: {:?}", regex);
        debug!("Matching string: '{}'", s);
        regex.captures(s).and_then(|capts| {
            // first one is the whole string, index 1-N are the matches.
            if capts.len() != 5 {
                debug!("Matches, but not expected number of groups");
                return None;
            }
            debug!("Matches: {}", capts.len());

            let modname  = capts.at(1).unwrap();
            let hashname = capts.at(2).unwrap();
            let hash     = capts.at(3).unwrap();

            debug!("Destructure FilePath to ID:");
            debug!("                  FilePath: {:?}", s);
            debug!("               Module Name: {:?}", modname);
            debug!("                 Hash Name: {:?}", hashname);
            debug!("                      Hash: {:?}", hash);

            FileIDType::from_str(hashname).map(|idtype| {
                debug!("ID type = {:?}", idtype);
                Some(FileID {
                    id: FileHash::from(hash),
                    id_type: idtype,
                })
            }).ok()
        }).unwrap_or({
            debug!("Did not match");
            debug!("It is no path, actually. So we assume it is an ID already");
            None
        })
    }

}

impl Debug for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "FileID[{:?}]: {:?}",
               self.id_type,
               self.id));
        Ok(())
    }

}

impl Display for FileID {

    fn fmt(&self, fmt: &mut Formatter) -> fmt::Result {
        try!(write!(fmt, "{}-{}", self.id_type, self.id));
        Ok(())
    }

}

impl Into<String> for FileID {

    fn into(self) -> String {
        let typestr : String = self.id_type.into();
        let idstr   : String = self.id.into();
        typestr + "-" + &idstr[..]
    }
}

#[cfg(test)]
mod test {

    use storage::file::id::FileID;
    use storage::file::id_type::FileIDType;

    #[test]
    fn file_id_from_string() {
        setup_logger();

        let s1 = String::from("/home/user/testmodule-UUID-some-id.imag");
        let s2 = String::from("/home/user/testmodule-UUID-some-id.extension.imag");
        let s3 = String::from("/home/user/testmodule-NOHASH-some-id.imag");

        let id1 = FileID::parse(&s1).unwrap();
        let id2 = FileID::parse(&s2).unwrap();
        assert!(FileID::parse(&s3).is_none());

        println!("Id 1 : {:?}", id1);
        println!("Id 2 : {:?}", id2);

        assert_eq!(FileIDType::UUID, id1.get_type());
        assert_eq!(FileIDType::UUID, id2.get_type());

        let h1 : String = id1.get_id().into();
        let h2 : String = id2.get_id().into();

        assert_eq!(String::from("some-id"), h1);
        assert_eq!(String::from("some-id"), h2);

        let f1 : String = id1.into();
        let f2 : String = id2.into();

        assert_eq!(String::from("UUID-some-id"), f1);
        assert_eq!(String::from("UUID-some-id"), f2);
    }

    fn setup_logger() {
        extern crate log;
        use log::{LogLevelFilter, set_logger};
        use runtime::ImagLogger;

        log::set_logger(|max_log_lvl| {
            let lvl = LogLevelFilter::Debug;
            max_log_lvl.set(lvl);
            Box::new(ImagLogger::new(lvl.to_log_level().unwrap()))
        });
        debug!("Init logger for test");
    }

}

<|endoftext|>


<commit_before>
//! The class factory is an infrastructure type used by the COM clients to
//! create instances of the `#[com_class(..)]`es provided by the intercom
//! library.
//!
//! Intercom implements the class factory infrastructure automatically when the
//! user specifies the `#[com_library(..)]` -attribute.

use super::*;

#[allow(non_camel_case_types)]
#[doc(hidden)]
#[cfg(windows)]
pub struct ClassFactoryVtbl {
    pub __base: IUnknownVtbl,
    pub create_instance: unsafe extern "stdcall" fn( RawComPtr, RawComPtr, REFIID, *mut RawComPtr ) -> HRESULT,
    pub lock_server: unsafe extern "stdcall" fn( RawComPtr, bool ) -> HRESULT
}

#[allow(non_camel_case_types)]
#[doc(hidden)]
#[cfg(not(windows))]
pub struct ClassFactoryVtbl {
    pub __base: IUnknownVtbl,
    pub create_instance: unsafe extern "C" fn( RawComPtr, RawComPtr, REFIID, *mut RawComPtr ) -> HRESULT,
    pub lock_server: unsafe extern "C" fn( RawComPtr, bool ) -> HRESULT
}

#[doc(hidden)]
pub struct ClassFactory<T> {
    pub clsid : REFCLSID,
    pub create_instance : T,
}

impl< T: Fn( REFCLSID ) -> ComResult< RawComPtr > > CoClass for ClassFactory<T> {
    type VTableList = &'static ClassFactoryVtbl;
    fn create_vtable_list() -> Self::VTableList {
        ClassFactory::<T>::create_vtable()
    }
    fn query_interface(
        vtables : &Self::VTableList,
        riid : REFIID,
    ) -> ComResult< RawComPtr >
    {
        if riid.is_null() { return Err( E_NOINTERFACE ) }
        unsafe { match *riid {
            super::IID_IUnknown | super::IID_IClassFactory =>
                    Ok( vtables as *const _ as RawComPtr ),
            _ => Err( E_NOINTERFACE ),
        } }
    }

    fn interface_supports_error_info( _riid : REFIID ) -> bool { false }
}

impl AsRef<IUnknownVtbl> for ClassFactoryVtbl {
    fn as_ref( &self ) -> &IUnknownVtbl {
        &self.__base
    }
}

impl< T: Fn( REFCLSID ) -> ComResult< RawComPtr > > ClassFactory<T> {

    pub fn new( clsid : REFCLSID, create_instance : T ) -> Self {
        Self { clsid, create_instance }
    }

    #[cfg(windows)]
    pub unsafe extern "stdcall" fn create_instance(
        self_vtbl : RawComPtr,
        _outer : RawComPtr,
        riid : REFIID,
        out : *mut RawComPtr,
    ) -> HRESULT
    {
        Self::create_instance_agnostic(self_vtbl, _outer, riid, out)
    }

    #[cfg(not(windows))]
    pub unsafe extern "C" fn create_instance(
        self_vtbl : RawComPtr,
        _outer : RawComPtr,
        riid : REFIID,
        out : *mut RawComPtr,
    ) -> HRESULT
    {
        Self::create_instance_agnostic(self_vtbl, _outer, riid, out)
    }

    #[cfg(windows)]
    pub unsafe extern "stdcall" fn lock_server(
        self_vtbl : RawComPtr,
        lock : bool
    ) -> HRESULT
    {
        Self::lock_server_agnostic(self_vtbl, lock)
    }

    #[cfg(not(windows))]
    pub unsafe extern "C" fn lock_server(
        self_vtbl : RawComPtr,
        lock : bool
    ) -> HRESULT
    {
        Self::lock_server_agnostic(self_vtbl, lock)
    }

    pub fn create_vtable() -> &'static ClassFactoryVtbl {
        &ClassFactoryVtbl {
            __base : IUnknownVtbl {
                query_interface : ComBox::< Self >::query_interface_ptr,
                add_ref : ComBox::< Self >::add_ref_ptr,
                release : ComBox::< Self >::release_ptr,
            },
            create_instance : Self::create_instance,
            lock_server : Self::lock_server
        }
    }

    unsafe fn create_instance_agnostic(
        self_vtbl : RawComPtr,
        _outer : RawComPtr,
        riid : REFIID,
        out : *mut RawComPtr,
    ) -> HRESULT
    {
        if out.is_null() {
            return E_POINTER
        }
        *out = std::ptr::null_mut();

        let cb = ComBox::< Self >::from_ptr( self_vtbl );

        let iunk_ptr = match (cb.create_instance)( cb.clsid ) {
            Ok( m ) => m,
            Err( hr ) => return hr,
        } as *const *const IUnknownVtbl;

        ((**iunk_ptr).query_interface)(
            iunk_ptr as RawComPtr,
            riid,
            out );

        S_OK
    }

    unsafe fn lock_server_agnostic(
        self_vtbl : RawComPtr,
        lock : bool
    ) -> HRESULT
    {
        if lock {
            ComBox::<Self>::add_ref_ptr( self_vtbl );
        } else {
            ComBox::<Self>::release_ptr( self_vtbl );
        }
        S_OK
    }
}

<commit_msg>Fixed error code propagation<commit_after>
//! The class factory is an infrastructure type used by the COM clients to
//! create instances of the `#[com_class(..)]`es provided by the intercom
//! library.
//!
//! Intercom implements the class factory infrastructure automatically when the
//! user specifies the `#[com_library(..)]` -attribute.

use super::*;

#[allow(non_camel_case_types)]
#[doc(hidden)]
#[cfg(windows)]
pub struct ClassFactoryVtbl {
    pub __base: IUnknownVtbl,
    pub create_instance: unsafe extern "stdcall" fn( RawComPtr, RawComPtr, REFIID, *mut RawComPtr ) -> HRESULT,
    pub lock_server: unsafe extern "stdcall" fn( RawComPtr, bool ) -> HRESULT
}

#[allow(non_camel_case_types)]
#[doc(hidden)]
#[cfg(not(windows))]
pub struct ClassFactoryVtbl {
    pub __base: IUnknownVtbl,
    pub create_instance: unsafe extern "C" fn( RawComPtr, RawComPtr, REFIID, *mut RawComPtr ) -> HRESULT,
    pub lock_server: unsafe extern "C" fn( RawComPtr, bool ) -> HRESULT
}

#[doc(hidden)]
pub struct ClassFactory<T> {
    pub clsid : REFCLSID,
    pub create_instance : T,
}

impl< T: Fn( REFCLSID ) -> ComResult< RawComPtr > > CoClass for ClassFactory<T> {
    type VTableList = &'static ClassFactoryVtbl;
    fn create_vtable_list() -> Self::VTableList {
        ClassFactory::<T>::create_vtable()
    }
    fn query_interface(
        vtables : &Self::VTableList,
        riid : REFIID,
    ) -> ComResult< RawComPtr >
    {
        if riid.is_null() { return Err( E_NOINTERFACE ) }
        unsafe { match *riid {
            super::IID_IUnknown | super::IID_IClassFactory =>
                    Ok( vtables as *const _ as RawComPtr ),
            _ => Err( E_NOINTERFACE ),
        } }
    }

    fn interface_supports_error_info( _riid : REFIID ) -> bool { false }
}

impl AsRef<IUnknownVtbl> for ClassFactoryVtbl {
    fn as_ref( &self ) -> &IUnknownVtbl {
        &self.__base
    }
}

impl< T: Fn( REFCLSID ) -> ComResult< RawComPtr > > ClassFactory<T> {

    pub fn new( clsid : REFCLSID, create_instance : T ) -> Self {
        Self { clsid, create_instance }
    }

    #[cfg(windows)]
    pub unsafe extern "stdcall" fn create_instance(
        self_vtbl : RawComPtr,
        _outer : RawComPtr,
        riid : REFIID,
        out : *mut RawComPtr,
    ) -> HRESULT
    {
        Self::create_instance_agnostic(self_vtbl, _outer, riid, out)
    }

    #[cfg(not(windows))]
    pub unsafe extern "C" fn create_instance(
        self_vtbl : RawComPtr,
        _outer : RawComPtr,
        riid : REFIID,
        out : *mut RawComPtr,
    ) -> HRESULT
    {
        Self::create_instance_agnostic(self_vtbl, _outer, riid, out)
    }

    #[cfg(windows)]
    pub unsafe extern "stdcall" fn lock_server(
        self_vtbl : RawComPtr,
        lock : bool
    ) -> HRESULT
    {
        Self::lock_server_agnostic(self_vtbl, lock)
    }

    #[cfg(not(windows))]
    pub unsafe extern "C" fn lock_server(
        self_vtbl : RawComPtr,
        lock : bool
    ) -> HRESULT
    {
        Self::lock_server_agnostic(self_vtbl, lock)
    }

    pub fn create_vtable() -> &'static ClassFactoryVtbl {
        &ClassFactoryVtbl {
            __base : IUnknownVtbl {
                query_interface : ComBox::< Self >::query_interface_ptr,
                add_ref : ComBox::< Self >::add_ref_ptr,
                release : ComBox::< Self >::release_ptr,
            },
            create_instance : Self::create_instance,
            lock_server : Self::lock_server
        }
    }

    unsafe fn create_instance_agnostic(
        self_vtbl : RawComPtr,
        _outer : RawComPtr,
        riid : REFIID,
        out : *mut RawComPtr,
    ) -> HRESULT
    {
        if out.is_null() {
            return E_POINTER
        }
        *out = std::ptr::null_mut();

        let cb = ComBox::< Self >::from_ptr( self_vtbl );

        let iunk_ptr = match (cb.create_instance)( cb.clsid ) {
            Ok( m ) => m,
            Err( hr ) => return hr,
        } as *const *const IUnknownVtbl;

        ((**iunk_ptr).query_interface)(
            iunk_ptr as RawComPtr,
            riid,
            out )
    }

    unsafe fn lock_server_agnostic(
        self_vtbl : RawComPtr,
        lock : bool
    ) -> HRESULT
    {
        if lock {
            ComBox::<Self>::add_ref_ptr( self_vtbl );
        } else {
            ComBox::<Self>::release_ptr( self_vtbl );
        }
        S_OK
    }
}

<|endoftext|>