<commit_before>#![feature(macro_rules)]

macro_rules! return_if_some(
    ($arg:expr) => (
        match $arg {
            Some(val) => return val,
            None => {},
        }
    );
)

pub fn pathmatch(pattern: &str, pathstring: &str) -> bool
{
    use std::str::Chars;
    let pattern_chars = pattern.chars();
    let mut path_chars = pathstring.chars();
    return pathmatch_impl(pattern_chars, &mut path_chars, false);

    fn pathmatch_impl(mut pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        if char_iter_equals(pattern_chars, "**/", true) {
            // How can I convert Skip<Chars> to Chars to make use of .skip(3) ?
            let mut pattern_chars_copy = pattern_chars;
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            if pathmatch_try(pattern_chars_copy, path_chars, alt_branch_mode) {
                return true;
            }
        }
        loop {
            if char_iter_equals(pattern_chars, "/**", false) && path_chars.peekable().is_empty() {
                return true;
            }
            match pattern_chars.next() {
                None => return path_chars.next().is_none(),
                Some('?') => match path_chars.next() {
                    None | Some('/') => return false,
                    _ => (),
                },
                Some('*') => match pattern_chars.peekable().peek() {
                    Some(&'*') => {
                        pattern_chars.next();
                        return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, true));
                    },
                    _ => return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, false)),
                },
                Some('/') => {
                    return_if_some!(match_exact('/', path_chars.next(), alt_branch_mode));
                    if char_iter_equals(pattern_chars, "**/", true) && pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                        return true;
                    }
                },
                Some('{') => {
                    let mut matched_something = false;
                    loop {
                        if !matched_something && pathmatch_try(pattern_chars, path_chars, true) {
                            matched_something = true;
                        }
                        match skip_alt_branch(&mut pattern_chars) {
                            Some(false) => break,
                            Some(true) => {},
                            None => /* error in pattern */ return false,
                        }
                    }
                    if !matched_something {
                        return false;
                    }
                },
                Some(',') | Some('}') if alt_branch_mode => return true,
                Some(pc) => return_if_some!(match_exact(pc, path_chars.next(), alt_branch_mode)),
            }
        }
    }

    fn pathmatch_try(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        let mut path_chars_copy = path_chars.clone();
        if pathmatch_impl(pattern_chars, &mut path_chars_copy, alt_branch_mode) {
            *path_chars = path_chars_copy;
            return true;
        }
        return false;
    }

    fn skip_alt_branch(pattern_chars: &mut Chars) -> Option<bool>
    {
        loop {
            match pattern_chars.next() {
                Some(',') => return Some(true),
                Some('}') => return Some(false),
                Some('{') => if skip_alt_branch(pattern_chars).is_none() { return None; },
                None => return None,
                _ => {},
            }
        }
    }

    fn match_any(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool, allow_pathsep: bool) -> Option<bool>
    {
        loop {
            if pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                return Some(true);
            }
            match path_chars.next() {
                None => return None,
                Some('/') if !allow_pathsep => return Some(false),
                _ => {},
            }
        }
    }

    fn match_exact(pattern_char: char, path_char: Option<char>, alt_branch_mode: bool) -> Option<bool>
    {
        match path_char {
            None => return Some(false),
            Some(',') | Some('}') if alt_branch_mode => return Some(false),
            Some(x) if x != pattern_char => return Some(false),
            _ => return None,
        }
    }

    fn char_iter_equals(mut char_iter: Chars, needle: &str, startswith: bool) -> bool
    {
        for needle_char in needle.chars() {
            match char_iter.next() {
                Some(x) if x == needle_char => (),
                _ => return false,
            }
        }
        return if startswith { true } else { char_iter.next().is_none() };
    }
}

#[test]
fn pathmatch_test_alt()
{
    assert!(pathmatch("{foo}", "foo"));
    assert!(pathmatch("{foo,bar,baz}", "foo"));
    assert!(pathmatch("{foo,bar,baz}", "bar"));
    assert!(pathmatch("{foo,bar,baz}", "baz"));
    assert!(!pathmatch("{foo,bar,baz}", "qux"));
    assert!(pathmatch("{foo,bar}", "foo"));
    assert!(pathmatch("{foo,bar}", "bar"));
    assert!(pathmatch("{}", ""));
}

#[test]
fn pathmatch_test_collapse()
{
    // `/**/` may match a single slash.
    assert!(pathmatch("foo/**/bar", "foo/bar"));
    assert!(pathmatch("foo/**/", "foo/"));
    assert!(!pathmatch("foo/**/", "foo"));
    assert!(pathmatch("/**/bar", "/bar"));
    assert!(!pathmatch("/**/bar", "bar"));
    // to prevent this behavior, you would use this
    assert!(!pathmatch("foo/*/**/bar", "foo/bar"));
    assert!(!pathmatch("foo/**/*/bar", "foo/bar"));
    assert!(pathmatch("foo/**/bar", "foo//bar"));
    assert!(pathmatch("foo/*/**/bar", "foo//bar"));
}

#[test]
fn pathmatch_test_anypath_leading()
{
    // `**/` at the beginning of pattern may match nothing.
    assert!(pathmatch("**/bar", "bar"));
    assert!(pathmatch("**/bar", "/bar"));
    assert!(!pathmatch("a**/bar", "abar"));
    assert!(!pathmatch("*/bar", "bar"));
}

#[test]
fn pathmatch_test_anypath_trailing()
{
    // `/**` at the end of pattern may match nothing.
    assert!(pathmatch("foo/**", "foo"));
    assert!(pathmatch("foo/**", "foo/"));
    assert!(!pathmatch("foo/**g", "foog"));
    assert!(!pathmatch("foo/*", "foo"));
}

#[test]
fn pathmatch_test_anypath()
{
    assert!(pathmatch("**", ""));
    assert!(pathmatch("a**", "a/b/c"));
    assert!(pathmatch("**c", "a/b/c"));
    assert!(pathmatch("a**c", "a/b/c"));
    assert!(pathmatch("a/**/c", "a/b/c"));
    assert!(pathmatch("**/c", "a/b/c"));
    assert!(!pathmatch("**/g", "e/fg"));
    assert!(pathmatch("a/**", "a/b/c"));
    assert!(!pathmatch("e/**", "ef/g"));
}

#[test]
fn pathmatch_test_pathsep()
{
    assert!(!pathmatch("a?b", "a/b"));
    assert!(!pathmatch("a*b", "a/b"));
    assert!(pathmatch("a*/b", "a/b"));
    assert!(pathmatch("a/*b", "a/b"));
}

#[test]
fn pathmatch_test_anyname()
{
    assert!(pathmatch("*", ""));
    assert!(pathmatch("*", "?"));
    assert!(pathmatch("*", "d?F"));
    assert!(pathmatch("a*", "a"));
    assert!(pathmatch("a*", "abcdef"));
    assert!(pathmatch("*f", "abcdef"));
    assert!(pathmatch("a*f", "abcdef"));
    assert!(pathmatch("*cd*", "abcdef"));
    assert!(pathmatch("a*cd*f", "abcdef"));
    assert!(pathmatch("acdf", "acdf"));
    assert!(!pathmatch("*a", "abc"));
    assert!(!pathmatch("c*", "abc"));
}

#[test]
fn pathmatch_test_anychar()
{
    assert!(pathmatch("?", "?"));
    assert!(pathmatch("?", "a"));
    assert!(pathmatch("??", "BC"));
    assert!(pathmatch("B?", "BC"));
    assert!(pathmatch("?C", "BC"));
    assert!(!pathmatch("?", ""));
    assert!(!pathmatch("D?", "BC"));
    assert!(!pathmatch("?E", "BC"));
}

#[test]
fn pathmatch_test_exact_equality()
{
    assert!(pathmatch("", ""));
    assert!(pathmatch("a", "a"));
    assert!(pathmatch("BC", "BC"));
    assert!(!pathmatch("a", ""));
    assert!(!pathmatch("", "BC"));
}

<commit_msg>test multiple matches with one call<commit_after>#![feature(macro_rules)]

macro_rules! return_if_some(
    ($arg:expr) => (
        match $arg {
            Some(val) => return val,
            None => {},
        }
    );
)

pub fn pathmatch(pattern: &str, pathstring: &str) -> bool
{
    use std::str::Chars;
    let pattern_chars = pattern.chars();
    let mut path_chars = pathstring.chars();
    return pathmatch_impl(pattern_chars, &mut path_chars, false);

    fn pathmatch_impl(mut pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        if char_iter_equals(pattern_chars, "**/", true) {
            // How can I convert Skip<Chars> to Chars to make use of .skip(3) ?
            let mut pattern_chars_copy = pattern_chars;
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            pattern_chars_copy.next();
            if pathmatch_try(pattern_chars_copy, path_chars, alt_branch_mode) {
                return true;
            }
        }
        loop {
            if char_iter_equals(pattern_chars, "/**", false) && path_chars.peekable().is_empty() {
                return true;
            }
            match pattern_chars.next() {
                None => return path_chars.next().is_none(),
                Some('?') => match path_chars.next() {
                    None | Some('/') => return false,
                    _ => (),
                },
                Some('*') => match pattern_chars.peekable().peek() {
                    Some(&'*') => {
                        pattern_chars.next();
                        return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, true));
                    },
                    _ => return_if_some!(match_any(pattern_chars, path_chars, alt_branch_mode, false)),
                },
                Some('/') => {
                    return_if_some!(match_exact('/', path_chars.next(), alt_branch_mode));
                    if char_iter_equals(pattern_chars, "**/", true) && pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                        return true;
                    }
                },
                Some('{') => {
                    let mut matched_something = false;
                    loop {
                        if !matched_something && pathmatch_try(pattern_chars, path_chars, true) {
                            matched_something = true;
                        }
                        match skip_alt_branch(&mut pattern_chars) {
                            Some(false) => break,
                            Some(true) => {},
                            None => /* error in pattern */ return false,
                        }
                    }
                    if !matched_something {
                        return false;
                    }
                },
                Some(',') | Some('}') if alt_branch_mode => return true,
                Some(pc) => return_if_some!(match_exact(pc, path_chars.next(), alt_branch_mode)),
            }
        }
    }

    fn pathmatch_try(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool) -> bool
    {
        let mut path_chars_copy = path_chars.clone();
        if pathmatch_impl(pattern_chars, &mut path_chars_copy, alt_branch_mode) {
            *path_chars = path_chars_copy;
            return true;
        }
        return false;
    }

    fn skip_alt_branch(pattern_chars: &mut Chars) -> Option<bool>
    {
        loop {
            match pattern_chars.next() {
                Some(',') => return Some(true),
                Some('}') => return Some(false),
                Some('{') => if skip_alt_branch(pattern_chars).is_none() { return None; },
                None => return None,
                _ => {},
            }
        }
    }

    fn match_any(pattern_chars: Chars, path_chars: &mut Chars, alt_branch_mode: bool, allow_pathsep: bool) -> Option<bool>
    {
        loop {
            if pathmatch_try(pattern_chars, path_chars, alt_branch_mode) {
                return Some(true);
            }
            match path_chars.next() {
                None => return None,
                Some('/') if !allow_pathsep => return Some(false),
                _ => {},
            }
        }
    }

    fn match_exact(pattern_char: char, path_char: Option<char>, alt_branch_mode: bool) -> Option<bool>
    {
        match path_char {
            None => return Some(false),
            Some(',') | Some('}') if alt_branch_mode => return Some(false),
            Some(x) if x != pattern_char => return Some(false),
            _ => return None,
        }
    }

    fn char_iter_equals(mut char_iter: Chars, needle: &str, startswith: bool) -> bool
    {
        for needle_char in needle.chars() {
            match char_iter.next() {
                Some(x) if x == needle_char => (),
                _ => return false,
            }
        }
        return if startswith { true } else { char_iter.next().is_none() };
    }
}

#[cfg(test)]
fn assert_pathmatch_many(pattern: &str, paths_yes: &[&str], paths_no: &[&str])
{
    for path in paths_yes.iter() {
        assert!(pathmatch(pattern, *path), "should match: pattern:'{}' path:'{}'", pattern, path);
    }
    for path in paths_no.iter() {
        assert!(!pathmatch(pattern, *path), "must not match: pattern:'{}' path:'{}'", pattern, path);
    }
}

#[test]
fn pathmatch_test_alt_combos()
{
//    assert_pathmatch_many("{foo/**,**/bar}", ["foo", "bar"], ["foobar"]);
    assert_pathmatch_many("a{?,/}c", ["abc", "a/c"], ["ac", "abbc"]);
//    assert_pathmatch_many("{foo/**,bar}baz", ["barbaz", "foo/baz"], ["foobaz"]);
//    assert_pathmatch_many("foo{bar,**/baz}", ["foobar", "foo/baz"], ["foobaz"]);
}

#[test]
fn pathmatch_test_alt()
{
    assert!(pathmatch("{foo}", "foo"));
    assert_pathmatch_many("{foo,bar,baz}", ["foo", "bar", "baz"], ["", "foobarbaz", "qux"]);
    assert_pathmatch_many("{foo,bar}", ["foo", "bar"], ["", "foobar"]);
    assert!(pathmatch("{}", ""));
    assert!(!pathmatch("{}", "{}"));
    assert!(pathmatch("{foo}{bar}", "foobar"));
    assert!(pathmatch("{foo}*{bar}", "foobar"));
    assert!(pathmatch("{foo}*{bar}", "fooXXbar"));
}

#[test]
fn pathmatch_test_collapse()
{
    // `/**/` may match a single slash.
    assert!(pathmatch("foo/**/bar", "foo/bar"));
    assert!(pathmatch("foo/**/", "foo/"));
    assert!(!pathmatch("foo/**/", "foo"));
    assert!(pathmatch("/**/bar", "/bar"));
    assert!(!pathmatch("/**/bar", "bar"));
    // to prevent this behavior, you would use this
    assert!(!pathmatch("foo/*/**/bar", "foo/bar"));
    assert!(!pathmatch("foo/**/*/bar", "foo/bar"));
    assert!(pathmatch("foo/**/bar", "foo//bar"));
    assert!(pathmatch("foo/*/**/bar", "foo//bar"));
}

#[test]
fn pathmatch_test_anypath_leading()
{
    // `**/` at the beginning of pattern may match nothing.
    assert!(pathmatch("**/bar", "bar"));
    assert!(pathmatch("**/bar", "/bar"));
    assert!(!pathmatch("a**/bar", "abar"));
    assert!(!pathmatch("*/bar", "bar"));
}

#[test]
fn pathmatch_test_anypath_trailing()
{
    // `/**` at the end of pattern may match nothing.
    assert!(pathmatch("foo/**", "foo"));
    assert!(pathmatch("foo/**", "foo/"));
    assert!(!pathmatch("foo/**g", "foog"));
    assert!(!pathmatch("foo/*", "foo"));
}

#[test]
fn pathmatch_test_anypath()
{
    assert!(pathmatch("**", ""));
    assert!(pathmatch("a**", "a/b/c"));
    assert!(pathmatch("**c", "a/b/c"));
    assert!(pathmatch("a**c", "a/b/c"));
    assert!(pathmatch("a/**/c", "a/b/c"));
    assert!(pathmatch("**/c", "a/b/c"));
    assert!(!pathmatch("**/g", "e/fg"));
    assert!(pathmatch("a/**", "a/b/c"));
    assert!(!pathmatch("e/**", "ef/g"));
}

#[test]
fn pathmatch_test_pathsep()
{
    assert!(!pathmatch("a?b", "a/b"));
    assert!(!pathmatch("a*b", "a/b"));
    assert!(pathmatch("a*/b", "a/b"));
    assert!(pathmatch("a/*b", "a/b"));
}

#[test]
fn pathmatch_test_anyname()
{
    assert!(pathmatch("*", ""));
    assert!(pathmatch("*", "?"));
    assert!(pathmatch("*", "d?F"));
    assert!(pathmatch("a*", "a"));
    assert!(pathmatch("a*", "abcdef"));
    assert!(pathmatch("*f", "abcdef"));
    assert!(pathmatch("a*f", "abcdef"));
    assert!(pathmatch("*cd*", "abcdef"));
    assert!(pathmatch("a*cd*f", "abcdef"));
    assert!(pathmatch("acdf", "acdf"));
    assert!(!pathmatch("*a", "abc"));
    assert!(!pathmatch("c*", "abc"));
}

#[test]
fn pathmatch_test_anychar()
{
    assert!(pathmatch("?", "?"));
    assert!(pathmatch("?", "a"));
    assert!(pathmatch("??", "BC"));
    assert!(pathmatch("B?", "BC"));
    assert!(pathmatch("?C", "BC"));
    assert!(!pathmatch("?", ""));
    assert!(!pathmatch("D?", "BC"));
    assert!(!pathmatch("?E", "BC"));
}

#[test]
fn pathmatch_test_exact_equality()
{
    assert!(pathmatch("", ""));
    assert!(pathmatch("a", "a"));
    assert!(pathmatch("BC", "BC"));
    assert!(!pathmatch("a", ""));
    assert!(!pathmatch("", "BC"));
}

<|endoftext|>


<commit_before>use std::collections::HashMap;
use super::{Chunk, ChunkIndex, HexPillar, PillarIndex};
use math::*;

/// Represents a whole game world consisting of multiple `Chunk`s.
///
/// Chunks are parallelograms (roughly) that are placed next to each other
/// in the world.
pub struct World {
    // TODO: make it private after we can access it immutable via a method! (see #7)
    pub chunks: HashMap<ChunkIndex, Chunk>,
}

impl World {
    /// Creates an empty world without any chunks.
    pub fn empty() -> Self {
        World { chunks: HashMap::new() }
    }

    /// Inserts the given chunk into the world and replaces the chunk that
    /// might have been at the given position before.
    pub fn replace_chunk(&mut self, index: ChunkIndex, chunk: Chunk) {
        // TODO: we might want to return the replaced chunk...
        self.chunks.insert(index, chunk);
        debug!("inserted chunk at position {:?}", index);
    }

    /// Inserts the given chunk at the given position, if there wasn't a chunk
    /// at that position before. In the latter case the given chunk is returned.
    pub fn add_chunk(&mut self, index: ChunkIndex, chunk: Chunk) -> Result<(), Chunk> {
        if self.chunks.contains_key(&index) {
            Err(chunk)
        } else {
            self.replace_chunk(index, chunk);
            Ok(())
        }
    }

    /// Returns the hex pillar at the given world position, iff the
    /// corresponding chunk is loaded.
    pub fn pillar_at(&self, pos: PillarIndex) -> Option<&HexPillar> {
        let chunk_size = super::CHUNK_SIZE as i32;

        // TODO: use `/` operator once it's implemented
        // let chunk_pos = pos / (super::CHUNK_SIZE as i32);
        let chunk_pos = pos.0 / chunk_size;
        let out = self.chunks.get(&ChunkIndex(chunk_pos)).map(|chunk| {

            let mut inner_pos = pos.0 % chunk_size;
            if inner_pos.q < 0 {
                inner_pos.q += chunk_size;
            }
            if inner_pos.r < 0 {
                inner_pos.r += chunk_size;
            }
            if chunk_pos.q != chunk_pos.r {
                ::std::mem::swap(&mut inner_pos.r, &mut inner_pos.q);
            }
            &chunk[inner_pos]
        });

        if out.is_none() {
            debug!("chunk {:?} is not loaded (position request {:?})",
                   chunk_pos,
                   pos);
        }
        out
    }

    /// Returns the chunk in which the given pillar exists.
    pub fn chunk_from_pillar(&self, pos: PillarIndex) -> Option<&Chunk> {
        let tmp = AxialPoint::new(pos.0.q / (super::CHUNK_SIZE as i32),
                                  pos.0.r / (super::CHUNK_SIZE as i32));
        let chunk_pos = ChunkIndex(tmp);
        self.chunk_at(chunk_pos)
    }

    /// Returns the requested chunk.
    pub fn chunk_at(&self, pos: ChunkIndex) -> Option<&Chunk> {
        let out = self.chunks.get(&pos);

        if out.is_none() {
            debug!("chunk {:?} is not loaded", pos);
        }

        out
    }
}
<commit_msg>fixed wrong indexing of inner_pos in world.rs<commit_after>use std::collections::HashMap;
use super::{Chunk, ChunkIndex, HexPillar, PillarIndex};
use math::*;

/// Represents a whole game world consisting of multiple `Chunk`s.
///
/// Chunks are parallelograms (roughly) that are placed next to each other
/// in the world.
pub struct World {
    // TODO: make it private after we can access it immutable via a method! (see #7)
    pub chunks: HashMap<ChunkIndex, Chunk>,
}

impl World {
    /// Creates an empty world without any chunks.
    pub fn empty() -> Self {
        World { chunks: HashMap::new() }
    }

    /// Inserts the given chunk into the world and replaces the chunk that
    /// might have been at the given position before.
    pub fn replace_chunk(&mut self, index: ChunkIndex, chunk: Chunk) {
        // TODO: we might want to return the replaced chunk...
        self.chunks.insert(index, chunk);
        debug!("inserted chunk at position {:?}", index);
    }

    /// Inserts the given chunk at the given position, if there wasn't a chunk
    /// at that position before. In the latter case the given chunk is returned.
    pub fn add_chunk(&mut self, index: ChunkIndex, chunk: Chunk) -> Result<(), Chunk> {
        if self.chunks.contains_key(&index) {
            Err(chunk)
        } else {
            self.replace_chunk(index, chunk);
            Ok(())
        }
    }

    /// Returns the hex pillar at the given world position, iff the
    /// corresponding chunk is loaded.
    pub fn pillar_at(&self, pos: PillarIndex) -> Option<&HexPillar> {
        let chunk_size = super::CHUNK_SIZE as i32;
        let mut new_pos = pos;
        // TODO: use `/` operator once it's implemented
        // let chunk_pos = pos / (super::CHUNK_SIZE as i32);
        if new_pos.0.q < 0 {
            new_pos.0.q -= 15;
        }
        if new_pos.0.r < 0 {
            new_pos.0.r -= 15;
        }
        let chunk_pos = new_pos.0 / chunk_size;
        let out = self.chunks.get(&ChunkIndex(chunk_pos)).map(|chunk| {

            let mut inner_pos = pos.0 % chunk_size;
            if inner_pos.q < 0 {
                inner_pos.q += chunk_size;
            }
            if inner_pos.r < 0 {
                inner_pos.r += chunk_size;
            }
            if chunk_pos.q != chunk_pos.r {
                ::std::mem::swap(&mut inner_pos.r, &mut inner_pos.q);
            }
            &chunk[inner_pos]
        });

        if out.is_none() {
            debug!("chunk {:?} is not loaded (position request {:?})",
                   chunk_pos,
                   pos);
        }
        out
    }

    /// Returns the chunk in which the given pillar exists.
    pub fn chunk_from_pillar(&self, pos: PillarIndex) -> Option<&Chunk> {
        let tmp = AxialPoint::new(pos.0.q / (super::CHUNK_SIZE as i32),
                                  pos.0.r / (super::CHUNK_SIZE as i32));
        let chunk_pos = ChunkIndex(tmp);
        self.chunk_at(chunk_pos)
    }

    /// Returns the requested chunk.
    pub fn chunk_at(&self, pos: ChunkIndex) -> Option<&Chunk> {
        let out = self.chunks.get(&pos);

        if out.is_none() {
            debug!("chunk {:?} is not loaded", pos);
        }

        out
    }
}
<|endoftext|>


<commit_before>#![cfg(test)]

extern crate esprit;
extern crate estree;
extern crate joker;
extern crate serde_json;
extern crate test;
extern crate unjson;

use esprit::script;
use estree::IntoScript;
use joker::track::Untrack;
use serde_json::value::Value;
use std::ffi::OsStr;
use std::fs::{File, read_dir};
use std::io::{Read, Write, stdout};
use std::path::Path;
use std::{thread, env};
use test::{TestDesc, TestDescAndFn, TestName, TestFn, Bencher, TDynBenchFn, test_main};
use test::ShouldPanic::No;
use unjson::Unjson;

struct DynBenchFn<F> {
    run: F
}

impl<F: Fn(&mut Bencher) + Send + 'static> TDynBenchFn for DynBenchFn<F> {
    fn run(&self, harness: &mut Bencher) {
        (self.run)(harness);
    }
}

fn add_bench<F: Fn(&mut Bencher) + Send + 'static>(tests: &mut Vec<TestDescAndFn>, name: String, ignore: bool, f: F) {
    tests.push(TestDescAndFn {
        desc: TestDesc {
            name: TestName::DynTestName(name),
            ignore: ignore,
            should_panic: No
        },
        testfn: TestFn::DynBenchFn(Box::new(DynBenchFn { run: f }))
    });
}

const DEFAULT_MB: usize = 4;

fn stack_size() -> usize {
    match env::var("ESTREE_STACK_SIZE_MB") {
        Ok(s) => match s.parse() {
            Ok(x) => Some(x),
            Err(_) => None
        },
        Err(env::VarError::NotPresent) => Some(DEFAULT_MB),
        Err(_) => None
    }.unwrap_or_else(|| {
        println!("warning: invalid ESTREE_STACK_SIZE_MB value; defaulting to {}MB", DEFAULT_MB);
        DEFAULT_MB
    }) * 1024 * 1024
}

fn integration_tests(target: &mut Vec<TestDescAndFn>, ignore: bool) {
    let root = Path::new(env!("CARGO_MANIFEST_DIR"));

    let fixtures = {
        let mut fixtures = root.to_path_buf();
        fixtures.push("tests");
        fixtures.push("esprima");
        fixtures.push("test");
        fixtures.push("3rdparty");
        fixtures
    };

    let syntax = fixtures.join("syntax");

    let files =
        read_dir(syntax).unwrap()
        .map(|dir| dir.unwrap().path())
        .filter(|path| path.extension() == Some(OsStr::new("json")))
        .map(|tree_path| {
            let mut source_path_buf = fixtures.join(tree_path.file_name().unwrap());
            source_path_buf.set_extension("js");
            (tree_path, source_path_buf)
        });

    for (tree_path, source_path) in files {
        let name = source_path.strip_prefix(&root).unwrap().to_str().unwrap().to_string();
        if !ignore {
            let mut source = String::new();
            File::open(source_path.clone()).unwrap().read_to_string(&mut source).unwrap();
            print!("Parsing JSON {}...", tree_path.strip_prefix(&root).unwrap().to_str().unwrap());
            stdout().flush().unwrap();
            let expected_ast = thread::Builder::new().stack_size(stack_size()).spawn(|| {
                let v: Value = serde_json::de::from_reader(File::open(tree_path).unwrap()).unwrap();
                v.into_object().unwrap().into_script().map_err(|err| {
                    format!("failed to deserialize script: {}", err)
                }).unwrap()
            }).unwrap().join().unwrap();
            println!(" done");
            add_bench(target, name, ignore, move |mut bench| {
                let mut result = None;
                bench.iter(|| {
                    result = Some(script(&source[..]))
                });
                match result.unwrap() {
                    Ok(mut actual_ast) => {
                        actual_ast.untrack();
                        assert!(actual_ast == expected_ast, "integration test got wrong result");
                    }
                    Err(actual_err) => {
                        panic!("integration test failed to parse:\n{:#?}", actual_err);
                    }
                }
            });
        } else {
            add_bench(target, name, ignore, |_| {});
        }
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let bench = args.contains(&"--bench".to_string());
    let mut tests = Vec::new();
    let ignore_integration_tests = !bench && env::var_os("ESTREE_INTEGRATION_TESTS") == None;
    if ignore_integration_tests {
        println!("note: Run with `ESTREE_INTEGRATION_TESTS=1` to run with integration tests (much slower).");
    }
    integration_tests(&mut tests, ignore_integration_tests);
    test_main(&args, tests);
}
<commit_msg>Run all benchmarks in dedicated thread<commit_after>#![cfg(test)]

extern crate esprit;
extern crate estree;
extern crate joker;
extern crate serde_json;
extern crate test;
extern crate unjson;

use esprit::script;
use estree::IntoScript;
use joker::track::Untrack;
use serde_json::value::Value;
use std::ffi::OsStr;
use std::fs::{File, read_dir};
use std::io::{Read, Write, stdout};
use std::path::Path;
use std::{thread, env};
use test::{TestDesc, TestDescAndFn, TestName, TestFn, Bencher, TDynBenchFn, test_main};
use test::ShouldPanic::No;
use unjson::Unjson;

struct DynBenchFn<F> {
    run: F
}

impl<F: Fn(&mut Bencher) + Send + 'static> TDynBenchFn for DynBenchFn<F> {
    fn run(&self, harness: &mut Bencher) {
        (self.run)(harness);
    }
}

fn add_bench<F: Fn(&mut Bencher) + Send + 'static>(tests: &mut Vec<TestDescAndFn>, name: String, ignore: bool, f: F) {
    tests.push(TestDescAndFn {
        desc: TestDesc {
            name: TestName::DynTestName(name),
            ignore: ignore,
            should_panic: No
        },
        testfn: TestFn::DynBenchFn(Box::new(DynBenchFn { run: f }))
    });
}

const DEFAULT_MB: usize = 4;

fn stack_size() -> usize {
    match env::var("ESTREE_STACK_SIZE_MB") {
        Ok(s) => match s.parse() {
            Ok(x) => Some(x),
            Err(_) => None
        },
        Err(env::VarError::NotPresent) => Some(DEFAULT_MB),
        Err(_) => None
    }.unwrap_or_else(|| {
        println!("warning: invalid ESTREE_STACK_SIZE_MB value; defaulting to {}MB", DEFAULT_MB);
        DEFAULT_MB
    }) * 1024 * 1024
}

fn integration_tests(target: &mut Vec<TestDescAndFn>, ignore: bool) {
    let root = Path::new(env!("CARGO_MANIFEST_DIR"));

    let fixtures = {
        let mut fixtures = root.to_path_buf();
        fixtures.push("tests");
        fixtures.push("esprima");
        fixtures.push("test");
        fixtures.push("3rdparty");
        fixtures
    };

    let syntax = fixtures.join("syntax");

    let files =
        read_dir(syntax).unwrap()
        .map(|dir| dir.unwrap().path())
        .filter(|path| path.extension() == Some(OsStr::new("json")))
        .map(|tree_path| {
            let mut source_path_buf = fixtures.join(tree_path.file_name().unwrap());
            source_path_buf.set_extension("js");
            (tree_path, source_path_buf)
        });

    for (tree_path, source_path) in files {
        let name = source_path.strip_prefix(&root).unwrap().to_str().unwrap().to_string();
        if !ignore {
            let mut source = String::new();
            File::open(source_path.clone()).unwrap().read_to_string(&mut source).unwrap();
            print!("Parsing JSON {}...", tree_path.strip_prefix(&root).unwrap().to_str().unwrap());
            stdout().flush().unwrap();
            let expected_ast = {
                let v: Value = serde_json::de::from_reader(File::open(tree_path).unwrap()).unwrap();
                v.into_object().unwrap().into_script().map_err(|err| {
                    format!("failed to deserialize script: {}", err)
                }).unwrap()
            };
            println!(" done");
            add_bench(target, name, ignore, move |mut bench| {
                let mut result = None;
                bench.iter(|| {
                    result = Some(script(&source[..]))
                });
                match result.unwrap() {
                    Ok(mut actual_ast) => {
                        actual_ast.untrack();
                        assert!(actual_ast == expected_ast, "integration test got wrong result");
                    }
                    Err(actual_err) => {
                        panic!("integration test failed to parse:\n{:#?}", actual_err);
                    }
                }
            });
        } else {
            add_bench(target, name, ignore, |_| {});
        }
    }
}

fn main() {
    let args: Vec<_> = env::args().collect();
    let bench = args.contains(&"--bench".to_string());
    let ignore_integration_tests = !bench && env::var_os("ESTREE_INTEGRATION_TESTS") == None;
    if ignore_integration_tests {
        println!("note: Run with `ESTREE_INTEGRATION_TESTS=1` to run with integration tests (much slower).");
    }
    thread::Builder::new().stack_size(stack_size()).spawn(move || {
        let mut tests = Vec::new();
        integration_tests(&mut tests, ignore_integration_tests);
        test_main(&args, tests);
    }).unwrap().join().unwrap();
}
<|endoftext|>


<commit_before>extern crate futures;

use actix_web::http::Method;
use actix_web::{App, AsyncResponder, Error, HttpRequest, HttpResponse, Query};
use chrono::naive::serde::ts_seconds;
use chrono::prelude::*;
use serde_json::Value;

use self::futures::Future;
use actix_state::State;
use build_info;
use data::TypeIdentifiable;
use data::*;
use repo_actor::*;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct DataResponse {
    #[serde(with = "ts_seconds")]
    revision: NaiveDateTime,
    deletions: Vec<Resource>,
    changes: Vec<DataChange>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct DataChange {
    resource: Resource,
    data: Value,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Resource {
    id: i32,

    #[serde(rename = "type")]
    _type: String,
}

#[derive(Deserialize)]
struct GetDataQuery {
    since_revision: DateTime<Utc>,
}

pub fn mount(app: App<State>) -> App<State> {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    app.route("/api/data", Method::GET, get_data)
       .route("/version", Method::GET, |_: HttpRequest<State>|
           build_info::build_version()
       )
}

fn get_data(
    (req, query): (HttpRequest<State>, Query<GetDataQuery>),
) -> Box<Future<Item = HttpResponse, Error = Error>> {
    let db = &req.state().db;

    let req_1 = db.send(GetNoteBooksMessage {
        since_revision: query.since_revision,
    });
    let req_2 = db.send(GetNotesMessage {
        since_revision: query.since_revision,
    });
    let req_3 = db.send(GetContentBlocksMessage {
        since_revision: query.since_revision,
    });
    let req_4 = db.send(GetDeletionsMessage {
        since_revision: query.since_revision,
    });

    req_1
        .join4(req_2, req_3, req_4)
        .from_err()
        .and_then(move |res| match res {
            (Ok(notebooks), Ok(notes), Ok(content_blocks), Ok(deleted_records)) => {
                let data_response =
                    build_response(notebooks, notes, content_blocks, deleted_records);
                Ok(HttpResponse::Ok().json(data_response))
            }
            _ => Ok(HttpResponse::InternalServerError().into()),
        })
        .responder()
}

fn build_response(
    notebooks: Vec<Notebook>,
    notes: Vec<Note>,
    content_blocks: Vec<ContentBlock>,
    deleted_records: Vec<Deletion>,
) -> DataResponse {
    let notebook_changes: Vec<DataChange> = notebooks
        .iter()
        .map(|n| DataChange {
            data: json!(n),
            resource: Resource {
                id: n.id,
                _type: n.type_name().into(),
            },
        })
        .collect();

    let mut note_changes: Vec<DataChange> = notes
        .iter()
        .map(|n| DataChange {
            data: json!(n),
            resource: Resource {
                id: n.id,
                _type: n.type_name().into(),
            },
        })
        .collect();

    let mut content_changes: Vec<DataChange> = content_blocks
        .iter()
        .map(|c| DataChange {
            data: json!(c),
            resource: Resource {
                id: c.id,
                _type: c.type_name().into(),
            },
        })
        .collect();

    let mut changes = notebook_changes;
    changes.append(&mut note_changes);
    changes.append(&mut content_changes);

    let deletions: Vec<Resource> = deleted_records
        .iter()
        .map(|d| Resource {
            id: d.id,
            _type: d._type.clone(),
        })
        .collect();

    // FIXME
    let revision = Utc::now().naive_utc();

    DataResponse {
        revision: revision,
        deletions: deletions,
        changes: changes,
    }
}
<commit_msg>Implement revision on API<commit_after>extern crate futures;

use actix_web::http::Method;
use actix_web::{App, AsyncResponder, Error, HttpRequest, HttpResponse, Query};
use chrono::prelude::*;
use serde_json::Value;

use self::futures::Future;
use actix_state::State;
use build_info;
use data::TypeIdentifiable;
use data::*;
use repo_actor::*;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct DataResponse {
    revision: DateTime<Utc>,
    deletions: Vec<Resource>,
    changes: Vec<DataChange>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct DataChange {
    resource: Resource,
    data: Value,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
struct Resource {
    id: i32,

    #[serde(rename = "type")]
    _type: String,
}

#[derive(Deserialize)]
struct GetDataQuery {
    since_revision: DateTime<Utc>,
}

pub fn mount(app: App<State>) -> App<State> {
    #[cfg_attr(rustfmt, rustfmt_skip)]
    app.route("/api/data", Method::GET, get_data)
       .route("/version", Method::GET, |_: HttpRequest<State>|
           build_info::build_version()
       )
}

fn get_data(
    (req, query): (HttpRequest<State>, Query<GetDataQuery>),
) -> Box<Future<Item = HttpResponse, Error = Error>> {
    let db = &req.state().db;

    let req_1 = db.send(GetNoteBooksMessage {
        since_revision: query.since_revision,
    });
    let req_2 = db.send(GetNotesMessage {
        since_revision: query.since_revision,
    });
    let req_3 = db.send(GetContentBlocksMessage {
        since_revision: query.since_revision,
    });
    let req_4 = db.send(GetDeletionsMessage {
        since_revision: query.since_revision,
    });

    req_1
        .join4(req_2, req_3, req_4)
        .from_err()
        .and_then(move |res| match res {
            (Ok(notebooks), Ok(notes), Ok(content_blocks), Ok(deleted_records)) => {
                let data_response =
                    build_response(notebooks, notes, content_blocks, deleted_records);
                Ok(HttpResponse::Ok().json(data_response))
            }
            _ => Ok(HttpResponse::InternalServerError().into()),
        })
        .responder()
}

fn build_response(
    notebooks: Vec<Notebook>,
    notes: Vec<Note>,
    content_blocks: Vec<ContentBlock>,
    deleted_records: Vec<Deletion>,
) -> DataResponse {
    let notebook_changes: Vec<DataChange> = notebooks
        .iter()
        .map(|n| DataChange {
            data: json!(n),
            resource: Resource {
                id: n.id,
                _type: n.type_name().into(),
            },
        })
        .collect();

    let mut note_changes: Vec<DataChange> = notes
        .iter()
        .map(|n| DataChange {
            data: json!(n),
            resource: Resource {
                id: n.id,
                _type: n.type_name().into(),
            },
        })
        .collect();

    let mut content_changes: Vec<DataChange> = content_blocks
        .iter()
        .map(|c| DataChange {
            data: json!(c),
            resource: Resource {
                id: c.id,
                _type: c.type_name().into(),
            },
        })
        .collect();

    let mut changes = notebook_changes;
    changes.append(&mut note_changes);
    changes.append(&mut content_changes);

    let deletions: Vec<Resource> = deleted_records
        .iter()
        .map(|d| Resource {
            id: d.id,
            _type: d._type.clone(),
        })
        .collect();

    let iter1 = notebooks.iter().map(|n| n.system_updated_at);
    let iter2 = notes.iter().map(|n| n.system_updated_at);
    let iter3 = content_blocks.iter().map(|c| c.system_updated_at);

    // FIXME include deletions
    let revision = iter1.chain(iter2).chain(iter3).max().unwrap_or(Utc::now());

    DataResponse {
        revision: revision,
        deletions: deletions,
        changes: changes,
    }
}
<|endoftext|>


<commit_before>#[cfg(test)]
mod bench {
	use test::Bencher;
	use basic_dsp::{
		ComplexVectorOperations,
		ComplexTimeVector32};
	use num::complex::Complex32;
    use tools::{VectorBox, Size};

    #[bench]
	fn complex_offset_32_benchmark(b: &mut Bencher)
	{
		let mut vector = VectorBox::<ComplexTimeVector32>::new(Size::Small);
		b.iter(|| {
			vector.execute(|v|  { v.complex_offset(Complex32::new(2.0, -5.0)) } )
		});
	}

	#[bench]
	fn complex_scale_32_benchmark(b: &mut Bencher)
	{
		let mut vector = VectorBox::<ComplexTimeVector32>::new(Size::Small);
		b.iter(|| {
			vector.execute(|v|  { v.complex_scale(Complex32::new(-2.0, 2.0)) } )
		});
	}
}<commit_msg>Added one more complex benchmark. Other complex operations would transform the vector into a real vector and I don't know yet how to benchmark this correctly.<commit_after>#[cfg(test)]
mod bench {
	use test::Bencher;
	use basic_dsp::{
		ComplexVectorOperations,
		ComplexTimeVector32};
	use num::complex::Complex32;
    use tools::{VectorBox, Size};

    #[bench]
	fn complex_offset_32s_benchmark(b: &mut Bencher)
	{
		let mut vector = VectorBox::<ComplexTimeVector32>::new(Size::Small);
		b.iter(|| {
			vector.execute(|v|  { v.complex_offset(Complex32::new(2.0, -5.0)) } )
		});
	}

	#[bench]
	fn complex_scale_32s_benchmark(b: &mut Bencher)
	{
		let mut vector = VectorBox::<ComplexTimeVector32>::new(Size::Small);
		b.iter(|| {
			vector.execute(|v|  { v.complex_scale(Complex32::new(-2.0, 2.0)) } )
		});
	}

    #[bench]
	fn complex_conj_32s_benchmark(b: &mut Bencher)
	{
		let mut vector = VectorBox::<ComplexTimeVector32>::new(Size::Small);
		b.iter(|| {
			vector.execute(|v|  { v.complex_conj() } )
		});
	}
}<|endoftext|>


<commit_before>extern crate tokio_core;
extern crate futures;
extern crate tk_http;
extern crate tk_listen;
extern crate wonky_tanks_server;

use std::cell::{RefCell, Cell};
use std::collections::hash_map::HashMap;
use std::fmt::Write;
use std::rc::Rc;

use tokio_core::reactor::Core;
use tokio_core::net::TcpListener;

use futures::{Stream, Future};
use futures::future::{FutureResult, ok};
use futures::sync::mpsc::{unbounded, UnboundedSender};

use tk_http::{Status};
use tk_http::server::buffered::{Request, BufferedDispatcher};
use tk_http::server::{Encoder, EncoderDone, Config as ServerConfig, Proto, Error as ServerError};
use tk_http::websocket::{Packet, Config as WebsocketConfig, Dispatcher, Frame, Loop, Error as WebsocketError};

use tk_listen::ListenExt;

use wonky_tanks_server::*;

const LOBBY: &'static str = "/";

type SharedData = Rc<RefCell<HashMap<CountedString, ChannelState>>>;
type TransientData = (SharedData, String);

#[derive(Clone, Default)]
pub struct ChannelState {
    capacity: Option<u8>,
    participants: Vec<Rc<UnboundedSender<Packet>>>,
}

impl std::fmt::Debug for ChannelState {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f
            .debug_struct("ChannelState")
            .field("capacity", &self.capacity)
            .field(
                "participants",
                &format_args!(
                    "{:x}",
                    IterContentsDebug(||
                        self
                            .participants
                            .iter()
                            .map(|i| (&i) as &UnboundedSender<_> as *const _ as usize)
                    )
                )
            )
            .finish()
    }
}

fn service<S>(directory: &Directory, data: &mut TransientData, req: Request, mut e: Encoder<S>) -> FutureResult<EncoderDone<S>, ServerError> {
    if let Some(ws) = req.websocket_handshake() {
        data.1.push_str(req.path());
        e.status(Status::SwitchingProtocol);
        e.add_header("Connection", "upgrade").unwrap();
        e.add_header("Upgrade", "websocket").unwrap();
        e.format_header("Sec-Websocket-Accept", &ws.accept).unwrap();
        e.done_headers().unwrap();
        return ok(e.done())
    }

    if let Some((ctype, data)) = directory.get(req.path()) {
        e.status(Status::Ok);
        e.add_length(data.len() as u64).unwrap();
        e.add_header("Content-Type", ctype).unwrap();
        if e.done_headers().unwrap() {
            e.write_body(&data);
        }
        return ok(e.done())
    }

    let not_found = "No data found".as_bytes();
    e.status(Status::NotFound);
    e.add_length(not_found.len() as u64).unwrap();
    e.add_header("Content-Type", "text/plain").unwrap();
    if e.done_headers().unwrap() {
        e.write_body(not_found);
    }
    ok(e.done())
}

struct WebsocketConnection {
    shared_data: SharedData,
    sender: Rc<UnboundedSender<Packet>>,
    token: CountedString,
}

impl WebsocketConnection {
    pub fn new(data: TransientData, sender: Rc<UnboundedSender<Packet>>) -> Self {
        let (shared_data, token) = data;
        let token: CountedString = token.into();
        let mut data: String = Default::default();
        match match {
            let shared_data = &mut *shared_data.borrow_mut();
            let shared_data = shared_data.entry(token.clone());
            #[cfg(debug_assertions)]
            println!("Previous data: {:?}", &shared_data);
            let &mut ChannelState { ref mut participants, capacity } = shared_data.or_insert_with(Default::default);
            participants.push(sender.clone());
            data.clear();
            write!(data, "count: {}", participants.len()).unwrap();
            let (last, participants) = participants.split_last().unwrap();
            for participant in participants {
                drop(participant.unbounded_send(Packet::Text(data.clone())));
            }
            drop(last.unbounded_send(Packet::Text(data.clone())));
            (capacity, participants.len())
        } {
            (Some(capacity), 1) if 1 < capacity => Some(true),
            (None, 1) => Some(true),
            (Some(capacity), count) if count == capacity as usize && count != 1 => Some(false),
            _ => None,
        } {
            Some(toggle) => {
                if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                    data.clear();
                    write!(data, "{}: {}", if toggle { "+" } else { "-" }, token).unwrap();
                    let (last, participants) = lobby.participants.split_last().unwrap();
                    for participant in participants {
                        drop(participant.unbounded_send(Packet::Text(data.clone())));
                    }
                    drop(last.unbounded_send(Packet::Text(data)));
                }
            },
            None => {},
        }
        #[cfg(debug_assertions)]
        println!("Created connection: {:x}/{}", (&sender) as &UnboundedSender<_> as *const _ as usize, token);
        WebsocketConnection { shared_data, sender, token }
    }

    fn is_lobby(&self) -> bool {
        LOBBY == &*self.token.0
    }
}

impl Drop for WebsocketConnection {
    fn drop(&mut self) {
        let &mut WebsocketConnection {
            ref mut shared_data,
            ref sender,
            ref token
        } = self;
        use std::collections::hash_map::Entry::Occupied;
        if Some(1) != {
            let mut map = shared_data.borrow_mut();
            let mut entry =
                if let Occupied(entry) = map.entry(token.clone()) {
                    entry
                } else {
                    panic!("Bad state; expected {} to be occupied: {:?}", token, shared_data)
                };
            if let (capacity, 0) = {
                let &mut ChannelState { ref mut participants, capacity } = entry.get_mut();
                participants.retain(|target| !Rc::ptr_eq(target, sender));
                (capacity, participants.len())
            } {
                entry.remove_entry();
                capacity
            } else {
                Some(1)
            }
        } {
            if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                let data = format!("-: {}", token);
                let (last, participants) = lobby.participants.split_last().unwrap();
                for participant in participants {
                    drop(participant.unbounded_send(Packet::Text(data.clone())));
                }
                drop(last.unbounded_send(Packet::Text(data)));
            }
        }
        #[cfg(debug_assertions)]
        println!("Dropped connection: {:x}/{}", (&self.sender) as &UnboundedSender<_> as *const _ as usize, token);
    }
}

impl Dispatcher for WebsocketConnection {
    type Future = FutureResult<(), WebsocketError>;

    fn frame(&mut self, frame: &Frame) -> FutureResult<(), WebsocketError> {
        if self.is_lobby() { return ok(()) }
        let &mut WebsocketConnection { ref shared_data, ref sender, ref token } = self;

        match frame {
            &Frame::Text(t) => {
                // FIXME - parse state changes
            },
            &Frame::Binary(b) => {
                let &ChannelState { ref participants, .. } = &(&*shared_data.borrow())[token];
                if participants.len() == 1 { return ok(()) }
                let mut participants = participants.iter().filter(|&participant| !Rc::ptr_eq(participant, sender));
                let last = participants.next_back().unwrap();
                let to_send = Packet::Binary(Vec::from(b));
                for participant in participants {
                    drop(participant.unbounded_send(to_send.clone()));
                }
                drop(last.unbounded_send(to_send));
            },
            _ => {},
        }

        ok(())
    }
}

fn main() {
    let directory = &Directory::new(std::env::current_dir().unwrap().as_path());
    let shared_data: SharedData = Default::default();
    let mut lp = Core::new().unwrap();
    let h1 = lp.handle();

    let addr = "0.0.0.0:5000".parse().unwrap();
    let listener = TcpListener::bind(&addr, &lp.handle()).unwrap();
    let cfg = ServerConfig::new().done();
    let wcfg = WebsocketConfig::new().done();

    let pipeline = listener.incoming();
    let pipeline =
        pipeline.map(
        move |(socket, addr)| {
            let wcfg = wcfg.clone();
            let h2 = h1.clone();

            let data: TransientData = (shared_data.clone(), Default::default());
            let data = Rc::new(RefCell::new(data));
            let http_data = data.clone();
            let websocket_data = Cell::new(Some(data));

            Proto::new(
                socket,
                &cfg,
                BufferedDispatcher::new_with_websockets(
                    addr,
                    &h1,
                    move |p1, p2|
                        service(directory, &mut *http_data.borrow_mut(),p1, p2),
                    move |outp, inp| {
                        let (sender, receiver) = unbounded();
                        let sender = Rc::new(sender);
                        Loop::server(
                            outp,
                            inp,
                            receiver.map_err(move |e| format!("{:?} : stream closed", e)),
                            WebsocketConnection::new(
                                Rc::try_unwrap(websocket_data.replace(None).unwrap()).unwrap().into_inner(),
                                sender
                            ),
                            &wcfg,
                            &h2
                        )
                            .map_err(|e| println!("Websocket err: {:?}", e))
                    }
                ),
                &h1
            )
                .map_err(|e| {println!("Connection error: {:?}", e);})
                .then(|_| Ok(())) // don't fail, please
        }
    );
    let pipeline = pipeline.listen(1000);

    lp.run(pipeline).unwrap();
}<commit_msg>Initialzie token more directly<commit_after>extern crate tokio_core;
extern crate futures;
extern crate tk_http;
extern crate tk_listen;
extern crate wonky_tanks_server;

use std::cell::RefCell;
use std::collections::hash_map::HashMap;
use std::fmt::Write;
use std::rc::Rc;

use tokio_core::reactor::Core;
use tokio_core::net::TcpListener;

use futures::{Stream, Future};
use futures::future::{FutureResult, ok};
use futures::sync::mpsc::{unbounded, UnboundedSender};

use tk_http::{Status};
use tk_http::server::buffered::{Request, BufferedDispatcher};
use tk_http::server::{Encoder, EncoderDone, Config as ServerConfig, Proto, Error as ServerError};
use tk_http::websocket::{Packet, Config as WebsocketConfig, Dispatcher, Frame, Loop, Error as WebsocketError};

use tk_listen::ListenExt;

use wonky_tanks_server::*;

const LOBBY: &'static str = "/";

type SharedData = Rc<RefCell<HashMap<CountedString, ChannelState>>>;

#[derive(Clone, Default)]
pub struct ChannelState {
    capacity: Option<u8>,
    participants: Vec<Rc<UnboundedSender<Packet>>>,
}

impl std::fmt::Debug for ChannelState {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        f
            .debug_struct("ChannelState")
            .field("capacity", &self.capacity)
            .field(
                "participants",
                &format_args!(
                    "{:x}",
                    IterContentsDebug(||
                        self
                            .participants
                            .iter()
                            .map(|i| (&i) as &UnboundedSender<_> as *const _ as usize)
                    )
                )
            )
            .finish()
    }
}

fn service<S>(directory: &Directory, token: &mut Option<String>, req: Request, mut e: Encoder<S>) -> FutureResult<EncoderDone<S>, ServerError> {
    if let Some(ws) = req.websocket_handshake() {
        *token = Some(req.path().to_string());
        e.status(Status::SwitchingProtocol);
        e.add_header("Connection", "upgrade").unwrap();
        e.add_header("Upgrade", "websocket").unwrap();
        e.format_header("Sec-Websocket-Accept", &ws.accept).unwrap();
        e.done_headers().unwrap();
        return ok(e.done())
    }

    if let Some((ctype, data)) = directory.get(req.path()) {
        e.status(Status::Ok);
        e.add_length(data.len() as u64).unwrap();
        e.add_header("Content-Type", ctype).unwrap();
        if e.done_headers().unwrap() {
            e.write_body(&data);
        }
        return ok(e.done())
    }

    let not_found = "No data found".as_bytes();
    e.status(Status::NotFound);
    e.add_length(not_found.len() as u64).unwrap();
    e.add_header("Content-Type", "text/plain").unwrap();
    if e.done_headers().unwrap() {
        e.write_body(not_found);
    }
    ok(e.done())
}

struct WebsocketConnection {
    shared_data: SharedData,
    sender: Rc<UnboundedSender<Packet>>,
    token: CountedString,
}

impl WebsocketConnection {
    pub fn new(shared_data: SharedData, token: String, sender: Rc<UnboundedSender<Packet>>) -> Self {
        let token: CountedString  = token.into();
        let mut data: String = Default::default();
        match match {
            let shared_data = &mut *shared_data.borrow_mut();
            let shared_data = shared_data.entry(token.clone());
            #[cfg(debug_assertions)]
            println!("Previous data: {:?}", &shared_data);
            let &mut ChannelState { ref mut participants, capacity } = shared_data.or_insert_with(Default::default);
            participants.push(sender.clone());
            data.clear();
            write!(data, "count: {}", participants.len()).unwrap();
            let (last, participants) = participants.split_last().unwrap();
            for participant in participants {
                drop(participant.unbounded_send(Packet::Text(data.clone())));
            }
            drop(last.unbounded_send(Packet::Text(data.clone())));
            (capacity, participants.len())
        } {
            (Some(capacity), 1) if 1 < capacity => Some(true),
            (None, 1) => Some(true),
            (Some(capacity), count) if count == capacity as usize && count != 1 => Some(false),
            _ => None,
        } {
            Some(toggle) => {
                if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                    data.clear();
                    write!(data, "{}: {}", if toggle { "+" } else { "-" }, token).unwrap();
                    let (last, participants) = lobby.participants.split_last().unwrap();
                    for participant in participants {
                        drop(participant.unbounded_send(Packet::Text(data.clone())));
                    }
                    drop(last.unbounded_send(Packet::Text(data)));
                }
            },
            None => {},
        }
        #[cfg(debug_assertions)]
        println!("Created connection: {:x}/{}", (&sender) as &UnboundedSender<_> as *const _ as usize, token);
        WebsocketConnection { shared_data, sender, token }
    }

    fn is_lobby(&self) -> bool {
        LOBBY == &*self.token.0
    }
}

impl Drop for WebsocketConnection {
    fn drop(&mut self) {
        let &mut WebsocketConnection {
            ref mut shared_data,
            ref sender,
            ref token
        } = self;
        use std::collections::hash_map::Entry::Occupied;
        if Some(1) != {
            let mut map = shared_data.borrow_mut();
            let mut entry =
                if let Occupied(entry) = map.entry(token.clone()) {
                    entry
                } else {
                    panic!("Bad state; expected {} to be occupied: {:?}", token, shared_data)
                };
            if let (capacity, 0) = {
                let &mut ChannelState { ref mut participants, capacity } = entry.get_mut();
                participants.retain(|target| !Rc::ptr_eq(target, sender));
                (capacity, participants.len())
            } {
                entry.remove_entry();
                capacity
            } else {
                Some(1)
            }
        } {
            if let Some(lobby) = shared_data.try_borrow().unwrap().get(LOBBY) {
                let data = format!("-: {}", token);
                let (last, participants) = lobby.participants.split_last().unwrap();
                for participant in participants {
                    drop(participant.unbounded_send(Packet::Text(data.clone())));
                }
                drop(last.unbounded_send(Packet::Text(data)));
            }
        }
        #[cfg(debug_assertions)]
        println!("Dropped connection: {:x}/{}", (&self.sender) as &UnboundedSender<_> as *const _ as usize, token);
    }
}

impl Dispatcher for WebsocketConnection {
    type Future = FutureResult<(), WebsocketError>;

    fn frame(&mut self, frame: &Frame) -> FutureResult<(), WebsocketError> {
        if self.is_lobby() { return ok(()) }
        let &mut WebsocketConnection { ref shared_data, ref sender, ref token } = self;

        match frame {
            &Frame::Text(t) => {
                // FIXME - parse state changes
            },
            &Frame::Binary(b) => {
                let &ChannelState { ref participants, .. } = &(&*shared_data.borrow())[token];
                if participants.len() == 1 { return ok(()) }
                let mut participants = participants.iter().filter(|&participant| !Rc::ptr_eq(participant, sender));
                let last = participants.next_back().unwrap();
                let to_send = Packet::Binary(Vec::from(b));
                for participant in participants {
                    drop(participant.unbounded_send(to_send.clone()));
                }
                drop(last.unbounded_send(to_send));
            },
            _ => {},
        }

        ok(())
    }
}

fn main() {
    let directory = &Directory::new(std::env::current_dir().unwrap().as_path());
    let shared_data: SharedData = Default::default();
    let mut lp = Core::new().unwrap();
    let h1 = lp.handle();

    let addr = "0.0.0.0:5000".parse().unwrap();
    let listener = TcpListener::bind(&addr, &lp.handle()).unwrap();
    let cfg = ServerConfig::new().done();
    let wcfg = WebsocketConfig::new().done();

    let pipeline = listener.incoming();
    let pipeline =
        pipeline.map(
        move |(socket, addr)| {
            let wcfg = wcfg.clone();
            let h2 = h1.clone();

            let shared_data = shared_data.clone();
            let data = Rc::new(RefCell::new(None));
            let http_data = data.clone();
            let websocket_data = data;

            Proto::new(
                socket,
                &cfg,
                BufferedDispatcher::new_with_websockets(
                    addr,
                    &h1,
                    move |p1, p2| {
                        let mut http_data = http_data.borrow_mut();
                        let http_data: &mut Option<String> = &mut http_data;
                        if let Some(token) = http_data.take() {
                            panic!("http initializer called twice: {}", token);
                        }
                        let ret = service(
                            directory,
                            http_data,
                            p1,
                            p2
                        );
                        ret
                    },
                    move |outp, inp| {
                        let (sender, receiver) = unbounded();
                        let sender = Rc::new(sender);
                        Loop::server(
                            outp,
                            inp,
                            receiver.map_err(move |e| format!("{:?} : stream closed", e)),
                            WebsocketConnection::new(
                                shared_data.clone(),
                                websocket_data
                                    .borrow_mut()
                                    .take()
                                    .expect("http initializer not called yet"),
                                sender
                            ),
                            &wcfg,
                            &h2
                        )
                            .map_err(|e| println!("Websocket err: {:?}", e))
                    }
                ),
                &h1
            )
                .map_err(|e| {println!("Connection error: {:?}", e);})
                .then(|_| Ok(())) // don't fail, please
        }
    );
    let pipeline = pipeline.listen(1000);

    lp.run(pipeline).unwrap();
}<|endoftext|>


<commit_before>use std::{
    str,
    str::FromStr,
};

use thiserror::Error;

use crate::urs::Urs;

#[derive(Error, Debug)]
pub enum Error {
    #[error("Could not parse urs: {0}")]
    CannotParseUrs(String),

    #[error("Could not parse taxid: {0}")]
    CannotParseTaxid(String),
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct UrsTaxid(u64, u64);

impl UrsTaxid {
    pub fn new(urs: u64, taxid: u64) -> Self {
        UrsTaxid(urs, taxid)
    }

    pub fn to_string(&self) -> String {
        format!("URS{:010X}_{}", self.0, self.1)
    }

    pub fn urs(&self) -> u64 {
        self.0
    }

    pub fn taxid(&self) -> u64 {
        self.1
    }

    pub fn short(&self) -> String {
        let urs: Urs = self.urs().into();
        format!("{}_{}", urs.short_urs(), self.taxid())
    }
}

impl FromStr for UrsTaxid {
    type Err = Error;

    fn from_str(raw: &str) -> Result<Self, Self::Err> {
        let (raw_urs, raw_taxid) = raw.split_at(14);
        let urs = u64::from_str_radix(&raw_urs[3..13], 16)
            .map_err(|_| Error::CannotParseUrs(raw.to_string()))?;

        let taxid =
            raw_taxid.parse::<u64>().map_err(|_| Error::CannotParseTaxid(raw_taxid.to_string()))?;
        Ok(Self(urs, taxid))
    }
}

impl From<&UrsTaxid> for String {
    fn from(urs: &UrsTaxid) -> String {
        format!("URS{:010X}_{}", urs.0, urs.1)
    }
}

impl From<&UrsTaxid> for Urs {
    fn from(urs: &UrsTaxid) -> Urs {
        Urs::from(urs.0)
    }
}

impl From<UrsTaxid> for Urs {
    fn from(urs: UrsTaxid) -> Urs {
        Urs::from(urs.0)
    }
}

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

    #[test]
    fn can_convert_string_to_urs_taxid() -> Result<(), Box<dyn Error>> {
        assert_eq!("URS0000000009_1".parse::<UrsTaxid>()?, UrsTaxid(9, 1));
        assert_eq!("URS0000C0472E_12445".parse::<UrsTaxid>()?, UrsTaxid(12601134, 12445));
        assert_eq!("URS0000000001_562".parse::<UrsTaxid>()?, UrsTaxid(1, 562));
        assert_eq!("URS00008B8A75_9606".parse::<UrsTaxid>()?, UrsTaxid(9144949, 9606));
        assert_eq!("URS00001EE391_1250050".parse::<UrsTaxid>()?, UrsTaxid(2024337, 1250050));
        assert_eq!("URS00008C3642_9606".parse::<UrsTaxid>()?, UrsTaxid(9188930, 9606));
        Ok(())
    }
}
<commit_msg>Properly create a URS when parsing UrsTaxid<commit_after>use std::{
    str,
    str::FromStr,
};

use thiserror::Error;

use crate::urs::Urs;

#[derive(Error, Debug)]
pub enum Error {
    #[error("Could not parse urs: {0}")]
    CannotParseUrs(String),

    #[error("Could not parse taxid: {0}")]
    CannotParseTaxid(String),
}

#[derive(Debug, PartialEq, Eq, Hash, Clone)]
pub struct UrsTaxid(u64, u64);

impl UrsTaxid {
    pub fn new(urs: u64, taxid: u64) -> Self {
        UrsTaxid(urs, taxid)
    }

    pub fn to_string(&self) -> String {
        format!("URS{:010X}_{}", self.0, self.1)
    }

    pub fn urs(&self) -> Urs {
        Urs::from(self.0)
    }

    pub fn taxid(&self) -> u64 {
        self.1
    }

    pub fn short(&self) -> String {
        let urs: Urs = self.urs().into();
        format!("{}_{}", urs.short_urs(), self.taxid())
    }
}

impl FromStr for UrsTaxid {
    type Err = Error;

    fn from_str(raw: &str) -> Result<Self, Self::Err> {
        let (raw_urs, raw_taxid) = raw.split_at(14);
        let urs = u64::from_str_radix(&raw_urs[3..13], 16)
            .map_err(|_| Error::CannotParseUrs(raw.to_string()))?;

        let taxid =
            raw_taxid.parse::<u64>().map_err(|_| Error::CannotParseTaxid(raw_taxid.to_string()))?;
        Ok(Self(urs, taxid))
    }
}

impl From<&UrsTaxid> for String {
    fn from(urs: &UrsTaxid) -> String {
        format!("URS{:010X}_{}", urs.0, urs.1)
    }
}

impl From<&UrsTaxid> for Urs {
    fn from(urs: &UrsTaxid) -> Urs {
        Urs::from(urs.0)
    }
}

impl From<UrsTaxid> for Urs {
    fn from(urs: UrsTaxid) -> Urs {
        Urs::from(urs.0)
    }
}

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

    #[test]
    fn can_convert_string_to_urs_taxid() -> Result<(), Box<dyn Error>> {
        assert_eq!("URS0000000009_1".parse::<UrsTaxid>()?, UrsTaxid(9, 1));
        assert_eq!("URS0000C0472E_12445".parse::<UrsTaxid>()?, UrsTaxid(12601134, 12445));
        assert_eq!("URS0000000001_562".parse::<UrsTaxid>()?, UrsTaxid(1, 562));
        assert_eq!("URS00008B8A75_9606".parse::<UrsTaxid>()?, UrsTaxid(9144949, 9606));
        assert_eq!("URS00001EE391_1250050".parse::<UrsTaxid>()?, UrsTaxid(2024337, 1250050));
        assert_eq!("URS00008C3642_9606".parse::<UrsTaxid>()?, UrsTaxid(9188930, 9606));
        Ok(())
    }
}
<|endoftext|>


<commit_before>use matches::matches;
use rustc::hir::*;
use rustc::lint::*;
use rustc::{declare_lint, lint_array};
use if_chain::if_chain;
use rustc::ty;
use crate::utils::{differing_macro_contexts, match_type, paths, snippet, span_lint_and_then, walk_ptrs_ty, SpanlessEq};
use crate::utils::sugg::Sugg;

/// **What it does:** Checks for manual swapping.
///
/// **Why is this bad?** The `std::mem::swap` function exposes the intent better
/// without deinitializing or copying either variable.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let t = b;
/// b = a;
/// a = t;
/// ```
declare_clippy_lint! {
    pub MANUAL_SWAP,
    complexity,
    "manual swap of two variables"
}

/// **What it does:** Checks for `foo = bar; bar = foo` sequences.
///
/// **Why is this bad?** This looks like a failed attempt to swap.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// a = b;
/// b = a;
/// ```
declare_clippy_lint! {
    pub ALMOST_SWAPPED,
    correctness,
    "`foo = bar; bar = foo` sequence"
}

#[derive(Copy, Clone)]
pub struct Swap;

impl LintPass for Swap {
    fn get_lints(&self) -> LintArray {
        lint_array![MANUAL_SWAP, ALMOST_SWAPPED]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap {
    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) {
        check_manual_swap(cx, block);
        check_suspicious_swap(cx, block);
    }
}

/// Implementation of the `MANUAL_SWAP` lint.
fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block) {
    for w in block.stmts.windows(3) {
        if_chain! {
            // let t = foo();
            if let StmtKind::Decl(ref tmp, _) = w[0].node;
            if let DeclKind::Local(ref tmp) = tmp.node;
            if let Some(ref tmp_init) = tmp.init;
            if let PatKind::Binding(_, _, ident, None) = tmp.pat.node;

            // foo() = bar();
            if let StmtKind::Semi(ref first, _) = w[1].node;
            if let ExprKind::Assign(ref lhs1, ref rhs1) = first.node;

            // bar() = t;
            if let StmtKind::Semi(ref second, _) = w[2].node;
            if let ExprKind::Assign(ref lhs2, ref rhs2) = second.node;
            if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.node;
            if rhs2.segments.len() == 1;

            if ident.as_str() == rhs2.segments[0].ident.as_str();
            if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1);
            if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2);
            then {
                fn check_for_slice<'a>(
                    cx: &LateContext<'_, '_>,
                    lhs1: &'a Expr,
                    lhs2: &'a Expr,
                ) -> Option<(&'a Expr, &'a Expr, &'a Expr)> {
                    if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.node {
                        if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.node {
                            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
                                let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1));

                                if matches!(ty.sty, ty::Slice(_)) ||
                                    matches!(ty.sty, ty::Array(_, _)) ||
                                    match_type(cx, ty, &paths::VEC) ||
                                    match_type(cx, ty, &paths::VEC_DEQUE) {
                                        return Some((lhs1, idx1, idx2));
                                }
                            }
                        }
                    }

                    None
                }

                let (replace, what, sugg) = if let Some((slice, idx1, idx2)) = check_for_slice(cx, lhs1, lhs2) {
                    if let Some(slice) = Sugg::hir_opt(cx, slice) {
                        (false,
                         format!(" elements of `{}`", slice),
                         format!("{}.swap({}, {})",
                                 slice.maybe_par(),
                                 snippet(cx, idx1.span, ".."),
                                 snippet(cx, idx2.span, "..")))
                    } else {
                        (false, "".to_owned(), "".to_owned())
                    }
                } else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
                    (true, format!(" `{}` and `{}`", first, second),
                        format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()))
                } else {
                    (true, "".to_owned(), "".to_owned())
                };

                let span = w[0].span.to(second.span);

                span_lint_and_then(cx,
                                   MANUAL_SWAP,
                                   span,
                                   &format!("this looks like you are swapping{} manually", what),
                                   |db| {
                                       if !sugg.is_empty() {
                                           db.span_suggestion(span, "try", sugg);

                                           if replace {
                                               db.note("or maybe you should use `std::mem::replace`?");
                                           }
                                       }
                                   });
            }
        }
    }
}

/// Implementation of the `ALMOST_SWAPPED` lint.
fn check_suspicious_swap(cx: &LateContext<'_, '_>, block: &Block) {
    for w in block.stmts.windows(2) {
        if_chain! {
            if let StmtKind::Semi(ref first, _) = w[0].node;
            if let StmtKind::Semi(ref second, _) = w[1].node;
            if !differing_macro_contexts(first.span, second.span);
            if let ExprKind::Assign(ref lhs0, ref rhs0) = first.node;
            if let ExprKind::Assign(ref lhs1, ref rhs1) = second.node;
            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1);
            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0);
            then {
                let lhs0 = Sugg::hir_opt(cx, lhs0);
                let rhs0 = Sugg::hir_opt(cx, rhs0);
                let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
                    (
                        format!(" `{}` and `{}`", first, second),
                        first.mut_addr().to_string(),
                        second.mut_addr().to_string(),
                    )
                } else {
                    ("".to_owned(), "".to_owned(), "".to_owned())
                };

                let span = first.span.to(second.span);

                span_lint_and_then(cx,
                                   ALMOST_SWAPPED,
                                   span,
                                   &format!("this looks like you are trying to swap{}", what),
                                   |db| {
                                       if !what.is_empty() {
                                           db.span_suggestion(span, "try",
                                                              format!("std::mem::swap({}, {})", lhs, rhs));
                                           db.note("or maybe you should use `std::mem::replace`?");
                                       }
                                   });
            }
        }
    }
}
<commit_msg>add how-to example for std::mem::transmute() usage instead of manual swap<commit_after>use matches::matches;
use rustc::hir::*;
use rustc::lint::*;
use rustc::{declare_lint, lint_array};
use if_chain::if_chain;
use rustc::ty;
use crate::utils::{differing_macro_contexts, match_type, paths, snippet, span_lint_and_then, walk_ptrs_ty, SpanlessEq};
use crate::utils::sugg::Sugg;

/// **What it does:** Checks for manual swapping.
///
/// **Why is this bad?** The `std::mem::swap` function exposes the intent better
/// without deinitializing or copying either variable.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// let t = b;
/// b = a;
/// a = t;
/// ```
/// Use std::mem::swap():
/// ```rust
/// std::mem::swap(&mut a, &mut b);
/// ```
declare_clippy_lint! {
    pub MANUAL_SWAP,
    complexity,
    "manual swap of two variables"
}

/// **What it does:** Checks for `foo = bar; bar = foo` sequences.
///
/// **Why is this bad?** This looks like a failed attempt to swap.
///
/// **Known problems:** None.
///
/// **Example:**
/// ```rust,ignore
/// a = b;
/// b = a;
/// ```
declare_clippy_lint! {
    pub ALMOST_SWAPPED,
    correctness,
    "`foo = bar; bar = foo` sequence"
}

#[derive(Copy, Clone)]
pub struct Swap;

impl LintPass for Swap {
    fn get_lints(&self) -> LintArray {
        lint_array![MANUAL_SWAP, ALMOST_SWAPPED]
    }
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for Swap {
    fn check_block(&mut self, cx: &LateContext<'a, 'tcx>, block: &'tcx Block) {
        check_manual_swap(cx, block);
        check_suspicious_swap(cx, block);
    }
}

/// Implementation of the `MANUAL_SWAP` lint.
fn check_manual_swap(cx: &LateContext<'_, '_>, block: &Block) {
    for w in block.stmts.windows(3) {
        if_chain! {
            // let t = foo();
            if let StmtKind::Decl(ref tmp, _) = w[0].node;
            if let DeclKind::Local(ref tmp) = tmp.node;
            if let Some(ref tmp_init) = tmp.init;
            if let PatKind::Binding(_, _, ident, None) = tmp.pat.node;

            // foo() = bar();
            if let StmtKind::Semi(ref first, _) = w[1].node;
            if let ExprKind::Assign(ref lhs1, ref rhs1) = first.node;

            // bar() = t;
            if let StmtKind::Semi(ref second, _) = w[2].node;
            if let ExprKind::Assign(ref lhs2, ref rhs2) = second.node;
            if let ExprKind::Path(QPath::Resolved(None, ref rhs2)) = rhs2.node;
            if rhs2.segments.len() == 1;

            if ident.as_str() == rhs2.segments[0].ident.as_str();
            if SpanlessEq::new(cx).ignore_fn().eq_expr(tmp_init, lhs1);
            if SpanlessEq::new(cx).ignore_fn().eq_expr(rhs1, lhs2);
            then {
                fn check_for_slice<'a>(
                    cx: &LateContext<'_, '_>,
                    lhs1: &'a Expr,
                    lhs2: &'a Expr,
                ) -> Option<(&'a Expr, &'a Expr, &'a Expr)> {
                    if let ExprKind::Index(ref lhs1, ref idx1) = lhs1.node {
                        if let ExprKind::Index(ref lhs2, ref idx2) = lhs2.node {
                            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, lhs2) {
                                let ty = walk_ptrs_ty(cx.tables.expr_ty(lhs1));

                                if matches!(ty.sty, ty::Slice(_)) ||
                                    matches!(ty.sty, ty::Array(_, _)) ||
                                    match_type(cx, ty, &paths::VEC) ||
                                    match_type(cx, ty, &paths::VEC_DEQUE) {
                                        return Some((lhs1, idx1, idx2));
                                }
                            }
                        }
                    }

                    None
                }

                let (replace, what, sugg) = if let Some((slice, idx1, idx2)) = check_for_slice(cx, lhs1, lhs2) {
                    if let Some(slice) = Sugg::hir_opt(cx, slice) {
                        (false,
                         format!(" elements of `{}`", slice),
                         format!("{}.swap({}, {})",
                                 slice.maybe_par(),
                                 snippet(cx, idx1.span, ".."),
                                 snippet(cx, idx2.span, "..")))
                    } else {
                        (false, "".to_owned(), "".to_owned())
                    }
                } else if let (Some(first), Some(second)) = (Sugg::hir_opt(cx, lhs1), Sugg::hir_opt(cx, rhs1)) {
                    (true, format!(" `{}` and `{}`", first, second),
                        format!("std::mem::swap({}, {})", first.mut_addr(), second.mut_addr()))
                } else {
                    (true, "".to_owned(), "".to_owned())
                };

                let span = w[0].span.to(second.span);

                span_lint_and_then(cx,
                                   MANUAL_SWAP,
                                   span,
                                   &format!("this looks like you are swapping{} manually", what),
                                   |db| {
                                       if !sugg.is_empty() {
                                           db.span_suggestion(span, "try", sugg);

                                           if replace {
                                               db.note("or maybe you should use `std::mem::replace`?");
                                           }
                                       }
                                   });
            }
        }
    }
}

/// Implementation of the `ALMOST_SWAPPED` lint.
fn check_suspicious_swap(cx: &LateContext<'_, '_>, block: &Block) {
    for w in block.stmts.windows(2) {
        if_chain! {
            if let StmtKind::Semi(ref first, _) = w[0].node;
            if let StmtKind::Semi(ref second, _) = w[1].node;
            if !differing_macro_contexts(first.span, second.span);
            if let ExprKind::Assign(ref lhs0, ref rhs0) = first.node;
            if let ExprKind::Assign(ref lhs1, ref rhs1) = second.node;
            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs0, rhs1);
            if SpanlessEq::new(cx).ignore_fn().eq_expr(lhs1, rhs0);
            then {
                let lhs0 = Sugg::hir_opt(cx, lhs0);
                let rhs0 = Sugg::hir_opt(cx, rhs0);
                let (what, lhs, rhs) = if let (Some(first), Some(second)) = (lhs0, rhs0) {
                    (
                        format!(" `{}` and `{}`", first, second),
                        first.mut_addr().to_string(),
                        second.mut_addr().to_string(),
                    )
                } else {
                    ("".to_owned(), "".to_owned(), "".to_owned())
                };

                let span = first.span.to(second.span);

                span_lint_and_then(cx,
                                   ALMOST_SWAPPED,
                                   span,
                                   &format!("this looks like you are trying to swap{}", what),
                                   |db| {
                                       if !what.is_empty() {
                                           db.span_suggestion(span, "try",
                                                              format!("std::mem::swap({}, {})", lhs, rhs));
                                           db.note("or maybe you should use `std::mem::replace`?");
                                       }
                                   });
            }
        }
    }
}
<|endoftext|>