<commit_before>//! Test runner.
//!
//! This module implements the `TestRunner` struct which manages executing tests as well as
//! scanning directories for tests.

use std::error::Error;
use std::fmt::{self, Display};
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use filetest::{TestResult, runone};
use filetest::concurrent::{ConcurrentRunner, Reply};
use CommandResult;

// Timeout in seconds when we're not making progress.
const TIMEOUT_PANIC: usize = 10;

// Timeout for reporting slow tests without panicking.
const TIMEOUT_SLOW: usize = 3;

struct QueueEntry {
    path: PathBuf,
    state: State,
}

#[derive(PartialEq, Eq, Debug)]
enum State {
    New,
    Queued,
    Running,
    Done(TestResult),
}

impl QueueEntry {
    pub fn path(&self) -> &Path {
        self.path.as_path()
    }
}

impl Display for QueueEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let p = self.path.to_string_lossy();
        match self.state {
            State::Done(Ok(dur)) => {
                write!(
                    f,
                    "{}.{:03} {}",
                    dur.as_secs(),
                    dur.subsec_nanos() / 1000000,
                    p
                )
            }
            State::Done(Err(ref e)) => write!(f, "FAIL {}: {}", p, e),
            _ => write!(f, "{}", p),
        }
    }
}

pub struct TestRunner {
    verbose: bool,

    // Directories that have not yet been scanned.
    dir_stack: Vec<PathBuf>,

    // Filenames of tests to run.
    tests: Vec<QueueEntry>,

    // Pointer into `tests` where the `New` entries begin.
    new_tests: usize,

    // Number of contiguous reported tests at the front of `tests`.
    reported_tests: usize,

    // Number of errors seen so far.
    errors: usize,

    // Number of ticks received since we saw any progress.
    ticks_since_progress: usize,

    threads: Option<ConcurrentRunner>,
}

impl TestRunner {
    /// Create a new blank TrstRunner.
    pub fn new(verbose: bool) -> Self {
        Self {
            verbose,
            dir_stack: Vec::new(),
            tests: Vec::new(),
            new_tests: 0,
            reported_tests: 0,
            errors: 0,
            ticks_since_progress: 0,
            threads: None,
        }
    }

    /// Add a directory path to be scanned later.
    ///
    /// If `dir` turns out to be a regular file, it is silently ignored.
    /// Otherwise, any problems reading the directory are reported.
    pub fn push_dir<P: Into<PathBuf>>(&mut self, dir: P) {
        self.dir_stack.push(dir.into());
    }

    /// Add a test to be executed later.
    ///
    /// Any problems reading `file` as a test case file will be reported as a test failure.
    pub fn push_test<P: Into<PathBuf>>(&mut self, file: P) {
        self.tests.push(QueueEntry {
            path: file.into(),
            state: State::New,
        });
    }

    /// Begin running tests concurrently.
    pub fn start_threads(&mut self) {
        assert!(self.threads.is_none());
        self.threads = Some(ConcurrentRunner::new());
    }

    /// Scan any directories pushed so far.
    /// Push any potential test cases found.
    pub fn scan_dirs(&mut self) {
        // This recursive search tries to minimize statting in a directory hierarchy containing
        // mostly test cases.
        //
        // - Directory entries with a "cton" extension are presumed to be test case files.
        // - Directory entries with no extension are presumed to be subdirectories.
        // - Anything else is ignored.
        //
        while let Some(dir) = self.dir_stack.pop() {
            match dir.read_dir() {
                Err(err) => {
                    // Fail silently if `dir` was actually a regular file.
                    // This lets us skip spurious extensionless files without statting everything
                    // needlessly.
                    if !dir.is_file() {
                        self.path_error(dir, err);
                    }
                }
                Ok(entries) => {
                    // Read all directory entries. Avoid statting.
                    for entry_result in entries {
                        match entry_result {
                            Err(err) => {
                                // Not sure why this would happen. `read_dir` succeeds, but there's
                                // a problem with an entry. I/O error during a getdirentries
                                // syscall seems to be the reason. The implementation in
                                // libstd/sys/unix/fs.rs seems to suggest that breaking now would
                                // be a good idea, or the iterator could keep returning the same
                                // error forever.
                                self.path_error(dir, err);
                                break;
                            }
                            Ok(entry) => {
                                let path = entry.path();
                                // Recognize directories and tests by extension.
                                // Yes, this means we ignore directories with '.' in their name.
                                match path.extension().and_then(OsStr::to_str) {
                                    Some("cton") => self.push_test(path),
                                    Some(_) => {}
                                    None => self.push_dir(path),
                                }
                            }
                        }
                    }
                }
            }
            // Get the new jobs running before moving on to the next directory.
            self.schedule_jobs();
        }
    }

    /// Report an error related to a path.
    fn path_error<E: Error>(&mut self, path: PathBuf, err: E) {
        self.errors += 1;
        println!("{}: {}", path.to_string_lossy(), err);
    }

    /// Report on the next in-order job, if it's done.
    fn report_job(&self) -> bool {
        let jobid = self.reported_tests;
        if let Some(&QueueEntry { state: State::Done(ref result), .. }) = self.tests.get(jobid) {
            if self.verbose || result.is_err() {
                println!("{}", self.tests[jobid]);
            }
            true
        } else {
            false
        }
    }

    /// Schedule any new jobs to run.
    fn schedule_jobs(&mut self) {
        for jobid in self.new_tests..self.tests.len() {
            assert_eq!(self.tests[jobid].state, State::New);
            if let Some(ref mut conc) = self.threads {
                // Queue test for concurrent execution.
                self.tests[jobid].state = State::Queued;
                conc.put(jobid, self.tests[jobid].path());
            } else {
                // Run test synchronously.
                self.tests[jobid].state = State::Running;
                let result = runone::run(self.tests[jobid].path());
                self.finish_job(jobid, result);
            }
            self.new_tests = jobid + 1;
        }

        // Check for any asynchronous replies without blocking.
        while let Some(reply) = self.threads.as_mut().and_then(ConcurrentRunner::try_get) {
            self.handle_reply(reply);
        }
    }

    /// Report the end of a job.
    fn finish_job(&mut self, jobid: usize, result: TestResult) {
        assert_eq!(self.tests[jobid].state, State::Running);
        if result.is_err() {
            self.errors += 1;
        }
        self.tests[jobid].state = State::Done(result);

        // Rports jobs in order.
        while self.report_job() {
            self.reported_tests += 1;
        }
    }

    /// Handle a reply from the async threads.
    fn handle_reply(&mut self, reply: Reply) {
        match reply {
            Reply::Starting { jobid, .. } => {
                assert_eq!(self.tests[jobid].state, State::Queued);
                self.tests[jobid].state = State::Running;
            }
            Reply::Done { jobid, result } => {
                self.ticks_since_progress = 0;
                self.finish_job(jobid, result)
            }
            Reply::Tick => {
                self.ticks_since_progress += 1;
                if self.ticks_since_progress == TIMEOUT_SLOW {
                    println!(
                        "STALLED for {} seconds with {}/{} tests finished",
                        self.ticks_since_progress,
                        self.reported_tests,
                        self.tests.len()
                    );
                    for jobid in self.reported_tests..self.tests.len() {
                        if self.tests[jobid].state == State::Running {
                            println!("slow: {}", self.tests[jobid]);
                        }
                    }
                }
                if self.ticks_since_progress >= TIMEOUT_PANIC {
                    panic!(
                        "worker threads stalled for {} seconds.",
                        self.ticks_since_progress
                    );
                }
            }
        }
    }

    /// Drain the async jobs and shut down the threads.
    fn drain_threads(&mut self) {
        if let Some(mut conc) = self.threads.take() {
            conc.shutdown();
            while self.reported_tests < self.tests.len() {
                match conc.get() {
                    Some(reply) => self.handle_reply(reply),
                    None => break,
                }
            }
            conc.join();
        }
    }

    /// Print out a report of slow tests.
    fn report_slow_tests(&self) {
        // Collect runtimes of succeeded tests.
        let mut times = self.tests
            .iter()
            .filter_map(|entry| match *entry {
                QueueEntry { state: State::Done(Ok(dur)), .. } => Some(dur),
                _ => None,
            })
            .collect::<Vec<_>>();

        // Get me some real data, kid.
        let len = times.len();
        if len < 4 {
            return;
        }

        // Compute quartiles.
        times.sort();
        let qlen = len / 4;
        let q1 = times[qlen];
        let q3 = times[len - 1 - qlen];
        // Inter-quartile range.
        let iqr = q3 - q1;

        // Cut-off for what we consider a 'slow' test: 1.5 IQR from the 75% quartile.
        // These are the data points that would be plotted as outliers outside a box plot.
        let cut = q3 + iqr * 2 / 3;
        if cut > *times.last().unwrap() {
            return;
        }

        for t in self.tests.iter().filter(|entry| match **entry {
            QueueEntry { state: State::Done(Ok(dur)), .. } => dur > cut,
            _ => false,
        })
        {
            println!("slow: {}", t)
        }

    }

    /// Scan pushed directories for tests and run them.
    pub fn run(&mut self) -> CommandResult {
        self.scan_dirs();
        self.schedule_jobs();
        self.drain_threads();
        self.report_slow_tests();
        println!("{} tests", self.tests.len());
        match self.errors {
            0 => Ok(()),
            1 => Err("1 failure".to_string()),
            n => Err(format!("{} failures", n)),
        }
    }
}
<commit_msg>Be more forgiving about what's a "slow" test.<commit_after>//! Test runner.
//!
//! This module implements the `TestRunner` struct which manages executing tests as well as
//! scanning directories for tests.

use std::error::Error;
use std::fmt::{self, Display};
use std::ffi::OsStr;
use std::path::{Path, PathBuf};
use filetest::{TestResult, runone};
use filetest::concurrent::{ConcurrentRunner, Reply};
use CommandResult;

// Timeout in seconds when we're not making progress.
const TIMEOUT_PANIC: usize = 10;

// Timeout for reporting slow tests without panicking.
const TIMEOUT_SLOW: usize = 3;

struct QueueEntry {
    path: PathBuf,
    state: State,
}

#[derive(PartialEq, Eq, Debug)]
enum State {
    New,
    Queued,
    Running,
    Done(TestResult),
}

impl QueueEntry {
    pub fn path(&self) -> &Path {
        self.path.as_path()
    }
}

impl Display for QueueEntry {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let p = self.path.to_string_lossy();
        match self.state {
            State::Done(Ok(dur)) => {
                write!(
                    f,
                    "{}.{:03} {}",
                    dur.as_secs(),
                    dur.subsec_nanos() / 1000000,
                    p
                )
            }
            State::Done(Err(ref e)) => write!(f, "FAIL {}: {}", p, e),
            _ => write!(f, "{}", p),
        }
    }
}

pub struct TestRunner {
    verbose: bool,

    // Directories that have not yet been scanned.
    dir_stack: Vec<PathBuf>,

    // Filenames of tests to run.
    tests: Vec<QueueEntry>,

    // Pointer into `tests` where the `New` entries begin.
    new_tests: usize,

    // Number of contiguous reported tests at the front of `tests`.
    reported_tests: usize,

    // Number of errors seen so far.
    errors: usize,

    // Number of ticks received since we saw any progress.
    ticks_since_progress: usize,

    threads: Option<ConcurrentRunner>,
}

impl TestRunner {
    /// Create a new blank TrstRunner.
    pub fn new(verbose: bool) -> Self {
        Self {
            verbose,
            dir_stack: Vec::new(),
            tests: Vec::new(),
            new_tests: 0,
            reported_tests: 0,
            errors: 0,
            ticks_since_progress: 0,
            threads: None,
        }
    }

    /// Add a directory path to be scanned later.
    ///
    /// If `dir` turns out to be a regular file, it is silently ignored.
    /// Otherwise, any problems reading the directory are reported.
    pub fn push_dir<P: Into<PathBuf>>(&mut self, dir: P) {
        self.dir_stack.push(dir.into());
    }

    /// Add a test to be executed later.
    ///
    /// Any problems reading `file` as a test case file will be reported as a test failure.
    pub fn push_test<P: Into<PathBuf>>(&mut self, file: P) {
        self.tests.push(QueueEntry {
            path: file.into(),
            state: State::New,
        });
    }

    /// Begin running tests concurrently.
    pub fn start_threads(&mut self) {
        assert!(self.threads.is_none());
        self.threads = Some(ConcurrentRunner::new());
    }

    /// Scan any directories pushed so far.
    /// Push any potential test cases found.
    pub fn scan_dirs(&mut self) {
        // This recursive search tries to minimize statting in a directory hierarchy containing
        // mostly test cases.
        //
        // - Directory entries with a "cton" extension are presumed to be test case files.
        // - Directory entries with no extension are presumed to be subdirectories.
        // - Anything else is ignored.
        //
        while let Some(dir) = self.dir_stack.pop() {
            match dir.read_dir() {
                Err(err) => {
                    // Fail silently if `dir` was actually a regular file.
                    // This lets us skip spurious extensionless files without statting everything
                    // needlessly.
                    if !dir.is_file() {
                        self.path_error(dir, err);
                    }
                }
                Ok(entries) => {
                    // Read all directory entries. Avoid statting.
                    for entry_result in entries {
                        match entry_result {
                            Err(err) => {
                                // Not sure why this would happen. `read_dir` succeeds, but there's
                                // a problem with an entry. I/O error during a getdirentries
                                // syscall seems to be the reason. The implementation in
                                // libstd/sys/unix/fs.rs seems to suggest that breaking now would
                                // be a good idea, or the iterator could keep returning the same
                                // error forever.
                                self.path_error(dir, err);
                                break;
                            }
                            Ok(entry) => {
                                let path = entry.path();
                                // Recognize directories and tests by extension.
                                // Yes, this means we ignore directories with '.' in their name.
                                match path.extension().and_then(OsStr::to_str) {
                                    Some("cton") => self.push_test(path),
                                    Some(_) => {}
                                    None => self.push_dir(path),
                                }
                            }
                        }
                    }
                }
            }
            // Get the new jobs running before moving on to the next directory.
            self.schedule_jobs();
        }
    }

    /// Report an error related to a path.
    fn path_error<E: Error>(&mut self, path: PathBuf, err: E) {
        self.errors += 1;
        println!("{}: {}", path.to_string_lossy(), err);
    }

    /// Report on the next in-order job, if it's done.
    fn report_job(&self) -> bool {
        let jobid = self.reported_tests;
        if let Some(&QueueEntry { state: State::Done(ref result), .. }) = self.tests.get(jobid) {
            if self.verbose || result.is_err() {
                println!("{}", self.tests[jobid]);
            }
            true
        } else {
            false
        }
    }

    /// Schedule any new jobs to run.
    fn schedule_jobs(&mut self) {
        for jobid in self.new_tests..self.tests.len() {
            assert_eq!(self.tests[jobid].state, State::New);
            if let Some(ref mut conc) = self.threads {
                // Queue test for concurrent execution.
                self.tests[jobid].state = State::Queued;
                conc.put(jobid, self.tests[jobid].path());
            } else {
                // Run test synchronously.
                self.tests[jobid].state = State::Running;
                let result = runone::run(self.tests[jobid].path());
                self.finish_job(jobid, result);
            }
            self.new_tests = jobid + 1;
        }

        // Check for any asynchronous replies without blocking.
        while let Some(reply) = self.threads.as_mut().and_then(ConcurrentRunner::try_get) {
            self.handle_reply(reply);
        }
    }

    /// Report the end of a job.
    fn finish_job(&mut self, jobid: usize, result: TestResult) {
        assert_eq!(self.tests[jobid].state, State::Running);
        if result.is_err() {
            self.errors += 1;
        }
        self.tests[jobid].state = State::Done(result);

        // Rports jobs in order.
        while self.report_job() {
            self.reported_tests += 1;
        }
    }

    /// Handle a reply from the async threads.
    fn handle_reply(&mut self, reply: Reply) {
        match reply {
            Reply::Starting { jobid, .. } => {
                assert_eq!(self.tests[jobid].state, State::Queued);
                self.tests[jobid].state = State::Running;
            }
            Reply::Done { jobid, result } => {
                self.ticks_since_progress = 0;
                self.finish_job(jobid, result)
            }
            Reply::Tick => {
                self.ticks_since_progress += 1;
                if self.ticks_since_progress == TIMEOUT_SLOW {
                    println!(
                        "STALLED for {} seconds with {}/{} tests finished",
                        self.ticks_since_progress,
                        self.reported_tests,
                        self.tests.len()
                    );
                    for jobid in self.reported_tests..self.tests.len() {
                        if self.tests[jobid].state == State::Running {
                            println!("slow: {}", self.tests[jobid]);
                        }
                    }
                }
                if self.ticks_since_progress >= TIMEOUT_PANIC {
                    panic!(
                        "worker threads stalled for {} seconds.",
                        self.ticks_since_progress
                    );
                }
            }
        }
    }

    /// Drain the async jobs and shut down the threads.
    fn drain_threads(&mut self) {
        if let Some(mut conc) = self.threads.take() {
            conc.shutdown();
            while self.reported_tests < self.tests.len() {
                match conc.get() {
                    Some(reply) => self.handle_reply(reply),
                    None => break,
                }
            }
            conc.join();
        }
    }

    /// Print out a report of slow tests.
    fn report_slow_tests(&self) {
        // Collect runtimes of succeeded tests.
        let mut times = self.tests
            .iter()
            .filter_map(|entry| match *entry {
                QueueEntry { state: State::Done(Ok(dur)), .. } => Some(dur),
                _ => None,
            })
            .collect::<Vec<_>>();

        // Get me some real data, kid.
        let len = times.len();
        if len < 4 {
            return;
        }

        // Compute quartiles.
        times.sort();
        let qlen = len / 4;
        let q1 = times[qlen];
        let q3 = times[len - 1 - qlen];
        // Inter-quartile range.
        let iqr = q3 - q1;

        // Cut-off for what we consider a 'slow' test: 3 IQR from the 75% quartile.
        //
        // Q3 + 1.5 IQR are the data points that would be plotted as outliers outside a box plot,
        // but we have a wider distribution of test times, so double it to 3 IQR.
        let cut = q3 + iqr * 3;
        if cut > *times.last().unwrap() {
            return;
        }

        for t in self.tests.iter().filter(|entry| match **entry {
            QueueEntry { state: State::Done(Ok(dur)), .. } => dur > cut,
            _ => false,
        })
        {
            println!("slow: {}", t)
        }

    }

    /// Scan pushed directories for tests and run them.
    pub fn run(&mut self) -> CommandResult {
        self.scan_dirs();
        self.schedule_jobs();
        self.drain_threads();
        self.report_slow_tests();
        println!("{} tests", self.tests.len());
        match self.errors {
            0 => Ok(()),
            1 => Err("1 failure".to_string()),
            n => Err(format!("{} failures", n)),
        }
    }
}
<|endoftext|>


<commit_before>use syntax::ast;
use syntax::ext::base;
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic;
use syntax::codemap;
use syntax::parse::token;
use syntax::ptr::P;

/// Expand #[vertex_format]
pub fn expand(ecx: &mut base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: &mut FnMut(P<ast::Item>))
{
    generic::TraitDef {
        span: span,
        attributes: Vec::new(),
        path: generic::ty::Path {
            path: vec!["glium", "Vertex"],
            lifetime: None,
            params: Vec::new(),
            global: true,
        },
        additional_bounds: Vec::new(),
        associated_types: Vec::new(),
        generics: generic::ty::LifetimeBounds::empty(),
        methods: vec![
            generic::MethodDef {
                name: "build_bindings",
                generics: generic::ty::LifetimeBounds::empty(),
                explicit_self: None,
                args: vec![],
                ret_ty: generic::ty::Literal(
                    generic::ty::Path::new(
                        vec!["glium", "VertexFormat"]
                    ),
                ),
                attributes: vec![
                    ecx.attribute(span.clone(), ecx.meta_list(span.clone(),
                        token::InternedString::new("allow"),
                        vec![ecx.meta_word(span.clone(),
                                token::InternedString::new("unused_assignments"))]
                    ))
                ],
                combine_substructure: generic::combine_substructure(Box::new(body)),
            },
        ],
    }.expand(ecx, meta_item, item, |i| push.call_mut((i,)));
}

fn body(ecx: &mut base::ExtCtxt, span: codemap::Span,
        substr: &generic::Substructure) -> P<ast::Expr>
{
    let ecx: &base::ExtCtxt = ecx;
    let self_ty = &substr.type_ident;

    match substr.fields {
        &generic::StaticStruct(ref definition, generic::Named(ref fields)) => {
            let content = definition.fields.iter().zip(fields.iter())
                .map(|(def, &(ident, _))| {
                    let ref elem_type = def.node.ty;
                    let ident_str = &*token::get_ident(ident);

                    quote_expr!(ecx, {
                        let offset = {
                            let dummy: &$self_ty = unsafe { mem::transmute(0us) };
                            let dummy_field = &dummy.$ident;
                            let dummy_field: usize = unsafe { mem::transmute(dummy_field) };
                            dummy_field
                        };

                        bindings.push((
                            Cow::Borrowed($ident_str),
                            offset,
                            <$elem_type as Attribute>::get_type(),
                        ));
                    })

                }).collect::<Vec<P<ast::Expr>>>();

            quote_expr!(ecx, {
                use glium::vertex::Attribute;
                use std::borrow::Cow;
                use std::mem;

                let mut bindings = Vec::new();
                $content;
                bindings
            })
        },

        _ => {
            ecx.span_err(span, "Unable to implement `glium::Vertex::build_bindings` \
                                on a non-structure");
            ecx.expr_int(span, 0)
        }
    }
}
<commit_msg>Fix deprecated prefix in glium_macros<commit_after>use syntax::ast;
use syntax::ext::base;
use syntax::ext::build::AstBuilder;
use syntax::ext::deriving::generic;
use syntax::codemap;
use syntax::parse::token;
use syntax::ptr::P;

/// Expand #[vertex_format]
pub fn expand(ecx: &mut base::ExtCtxt, span: codemap::Span,
              meta_item: &ast::MetaItem, item: &ast::Item,
              push: &mut FnMut(P<ast::Item>))
{
    generic::TraitDef {
        span: span,
        attributes: Vec::new(),
        path: generic::ty::Path {
            path: vec!["glium", "Vertex"],
            lifetime: None,
            params: Vec::new(),
            global: true,
        },
        additional_bounds: Vec::new(),
        associated_types: Vec::new(),
        generics: generic::ty::LifetimeBounds::empty(),
        methods: vec![
            generic::MethodDef {
                name: "build_bindings",
                generics: generic::ty::LifetimeBounds::empty(),
                explicit_self: None,
                args: vec![],
                ret_ty: generic::ty::Literal(
                    generic::ty::Path::new(
                        vec!["glium", "VertexFormat"]
                    ),
                ),
                attributes: vec![
                    ecx.attribute(span.clone(), ecx.meta_list(span.clone(),
                        token::InternedString::new("allow"),
                        vec![ecx.meta_word(span.clone(),
                                token::InternedString::new("unused_assignments"))]
                    ))
                ],
                combine_substructure: generic::combine_substructure(Box::new(body)),
            },
        ],
    }.expand(ecx, meta_item, item, |i| push.call_mut((i,)));
}

fn body(ecx: &mut base::ExtCtxt, span: codemap::Span,
        substr: &generic::Substructure) -> P<ast::Expr>
{
    let ecx: &base::ExtCtxt = ecx;
    let self_ty = &substr.type_ident;

    match substr.fields {
        &generic::StaticStruct(ref definition, generic::Named(ref fields)) => {
            let content = definition.fields.iter().zip(fields.iter())
                .map(|(def, &(ident, _))| {
                    let ref elem_type = def.node.ty;
                    let ident_str = &*token::get_ident(ident);

                    quote_expr!(ecx, {
                        let offset = {
                            let dummy: &$self_ty = unsafe { mem::transmute(0usize) };
                            let dummy_field = &dummy.$ident;
                            let dummy_field: usize = unsafe { mem::transmute(dummy_field) };
                            dummy_field
                        };

                        bindings.push((
                            Cow::Borrowed($ident_str),
                            offset,
                            <$elem_type as Attribute>::get_type(),
                        ));
                    })

                }).collect::<Vec<P<ast::Expr>>>();

            quote_expr!(ecx, {
                use glium::vertex::Attribute;
                use std::borrow::Cow;
                use std::mem;

                let mut bindings = Vec::new();
                $content;
                bindings
            })
        },

        _ => {
            ecx.span_err(span, "Unable to implement `glium::Vertex::build_bindings` \
                                on a non-structure");
            ecx.expr_int(span, 0)
        }
    }
}
<|endoftext|>


<commit_before>use std::sync::Arc;
use super::{Args, Configuration};
use client::Context;
use model::{Message, Permissions};
use std::collections::HashMap;
use std::fmt;

pub type Check = Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool
                     + Send
                     + Sync
                     + 'static;
pub type Exec = fn(&mut Context, &Message, Args) -> Result<(), Error>;
pub type Help = fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Args)
                   -> Result<(), Error>;
pub type BeforeHook = Fn(&mut Context, &Message, &str) -> bool + Send + Sync + 'static;
pub type AfterHook = Fn(&mut Context, &Message, &str, Result<(), Error>) + Send + Sync + 'static;
pub(crate) type InternalCommand = Arc<Command>;
pub type PrefixCheck = Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static;

pub enum CommandOrAlias {
    Alias(String),
    Command(InternalCommand),
}

/// An error from a command.
#[derive(Clone, Debug)]
pub struct Error(pub String);

// TODO: Have seperate `From<(&)String>` and `From<&str>` impls via specialization
impl<D: fmt::Display> From<D> for Error {
    fn from(d: D) -> Self {
        Error(format!("{}", d))
    }
}

/// Command function type. Allows to access internal framework things inside
/// your commands.
pub enum CommandType {
    StringResponse(String),
    Basic(Exec),
    WithCommands(Help),
}

pub struct CommandGroup {
    pub prefix: Option<String>,
    pub commands: HashMap<String, CommandOrAlias>,
    /// Some fields taken from Command
    pub bucket: Option<String>,
    pub required_permissions: Permissions,
    pub allowed_roles: Vec<String>,
    pub help_available: bool,
    pub dm_only: bool,
    pub guild_only: bool,
    pub owners_only: bool,
}

/// Command struct used to store commands internally.
pub struct Command {
    /// A set of checks to be called prior to executing the command. The checks
    /// will short-circuit on the first check that returns `false`.
    pub checks: Vec<Box<Check>>,
    /// Function called when the command is called.
    pub exec: CommandType,
    /// Ratelimit bucket.
    pub bucket: Option<String>,
    /// Command description, used by other commands.
    pub desc: Option<String>,
    /// Example arguments, used by other commands.
    pub example: Option<String>,
    /// Command usage schema, used by other commands.
    pub usage: Option<String>,
    /// Minumum amount of arguments that should be passed.
    pub min_args: Option<i32>,
    /// Maximum amount of arguments that can be passed.
    pub max_args: Option<i32>,
    /// Permissions required to use this command.
    pub required_permissions: Permissions,
    /// Roles allowed to use this command.
    pub allowed_roles: Vec<String>,
    /// Whether command should be displayed in help list or not, used by other commands.
    pub help_available: bool,
    /// Whether command can be used only privately or not.
    pub dm_only: bool,
    /// Whether command can be used only in guilds or not.
    pub guild_only: bool,
    /// Whether command can only be used by owners or not.
    pub owners_only: bool,
    pub(crate) aliases: Vec<String>,
}

impl Command {
    pub fn new(f: fn(&mut Context, &Message, Args) -> Result<(), Error>) -> Self {
        Command {
            exec: CommandType::Basic(f),
            ..Command::default()
        }
    }
}

impl Default for Command {
    fn default() -> Command {
        Command {
            aliases: Vec::new(),
            checks: Vec::default(),
            exec: CommandType::Basic(|_, _, _| Ok(())),
            desc: None,
            usage: None,
            example: None,
            min_args: None,
            bucket: None,
            max_args: None,
            required_permissions: Permissions::empty(),
            dm_only: false,
            guild_only: false,
            help_available: true,
            owners_only: false,
            allowed_roles: Vec::new(),
        }
    }
}

pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Option<Vec<usize>> {
    if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() {
        // Find out if they were mentioned. If not, determine if the prefix
        // was used. If not, return None.
        let mut positions: Vec<usize> = vec![];

        if let Some(mention_end) = find_mention_end(&msg.content, conf) {
            positions.push(mention_end);
        } else if let Some(ref func) = conf.dynamic_prefix {
            if let Some(x) = func(ctx, msg) {
                if msg.content.starts_with(&x) {
                    positions.push(x.len());
                }
            } else {
                for n in &conf.prefixes {
                    if msg.content.starts_with(n) {
                        positions.push(n.len());
                    }
                }
            }
        } else {
            for n in &conf.prefixes {
                if msg.content.starts_with(n) {
                    positions.push(n.len());
                }
            }
        };

        if positions.is_empty() {
            return None;
        }

        if conf.allow_whitespace {
            let pos = *unsafe { positions.get_unchecked(0) };

            positions.insert(0, pos + 1);
        }

        Some(positions)
    } else if conf.on_mention.is_some() {
        find_mention_end(&msg.content, conf).map(|mention_end| {
            let mut positions = vec![mention_end];

            if conf.allow_whitespace {
                positions.insert(0, mention_end + 1);
            }

            positions
        })
    } else {
        None
    }
}

fn find_mention_end(content: &str, conf: &Configuration) -> Option<usize> {
    conf.on_mention.as_ref().and_then(|mentions| {
        mentions
            .iter()
            .find(|mention| content.starts_with(&mention[..]))
            .map(|m| m.len())
    })
}
<commit_msg>Make `Command::aliases` public<commit_after>use std::sync::Arc;
use super::{Args, Configuration};
use client::Context;
use model::{Message, Permissions};
use std::collections::HashMap;
use std::fmt;

pub type Check = Fn(&mut Context, &Message, &mut Args, &Arc<Command>) -> bool
                     + Send
                     + Sync
                     + 'static;
pub type Exec = fn(&mut Context, &Message, Args) -> Result<(), Error>;
pub type Help = fn(&mut Context, &Message, HashMap<String, Arc<CommandGroup>>, Args)
                   -> Result<(), Error>;
pub type BeforeHook = Fn(&mut Context, &Message, &str) -> bool + Send + Sync + 'static;
pub type AfterHook = Fn(&mut Context, &Message, &str, Result<(), Error>) + Send + Sync + 'static;
pub(crate) type InternalCommand = Arc<Command>;
pub type PrefixCheck = Fn(&mut Context, &Message) -> Option<String> + Send + Sync + 'static;

pub enum CommandOrAlias {
    Alias(String),
    Command(InternalCommand),
}

/// An error from a command.
#[derive(Clone, Debug)]
pub struct Error(pub String);

// TODO: Have seperate `From<(&)String>` and `From<&str>` impls via specialization
impl<D: fmt::Display> From<D> for Error {
    fn from(d: D) -> Self {
        Error(format!("{}", d))
    }
}

/// Command function type. Allows to access internal framework things inside
/// your commands.
pub enum CommandType {
    StringResponse(String),
    Basic(Exec),
    WithCommands(Help),
}

pub struct CommandGroup {
    pub prefix: Option<String>,
    pub commands: HashMap<String, CommandOrAlias>,
    /// Some fields taken from Command
    pub bucket: Option<String>,
    pub required_permissions: Permissions,
    pub allowed_roles: Vec<String>,
    pub help_available: bool,
    pub dm_only: bool,
    pub guild_only: bool,
    pub owners_only: bool,
}

/// Command struct used to store commands internally.
pub struct Command {
    /// A set of checks to be called prior to executing the command. The checks
    /// will short-circuit on the first check that returns `false`.
    pub checks: Vec<Box<Check>>,
    /// Function called when the command is called.
    pub exec: CommandType,
    /// Ratelimit bucket.
    pub bucket: Option<String>,
    /// Command description, used by other commands.
    pub desc: Option<String>,
    /// Example arguments, used by other commands.
    pub example: Option<String>,
    /// Command usage schema, used by other commands.
    pub usage: Option<String>,
    /// Minumum amount of arguments that should be passed.
    pub min_args: Option<i32>,
    /// Maximum amount of arguments that can be passed.
    pub max_args: Option<i32>,
    /// Permissions required to use this command.
    pub required_permissions: Permissions,
    /// Roles allowed to use this command.
    pub allowed_roles: Vec<String>,
    /// Whether command should be displayed in help list or not, used by other commands.
    pub help_available: bool,
    /// Whether command can be used only privately or not.
    pub dm_only: bool,
    /// Whether command can be used only in guilds or not.
    pub guild_only: bool,
    /// Whether command can only be used by owners or not.
    pub owners_only: bool,
    /// Other names that can be used to call this command instead.
    pub aliases: Vec<String>,
}

impl Command {
    pub fn new(f: fn(&mut Context, &Message, Args) -> Result<(), Error>) -> Self {
        Command {
            exec: CommandType::Basic(f),
            ..Command::default()
        }
    }
}

impl Default for Command {
    fn default() -> Command {
        Command {
            aliases: Vec::new(),
            checks: Vec::default(),
            exec: CommandType::Basic(|_, _, _| Ok(())),
            desc: None,
            usage: None,
            example: None,
            min_args: None,
            bucket: None,
            max_args: None,
            required_permissions: Permissions::empty(),
            dm_only: false,
            guild_only: false,
            help_available: true,
            owners_only: false,
            allowed_roles: Vec::new(),
        }
    }
}

pub fn positions(ctx: &mut Context, msg: &Message, conf: &Configuration) -> Option<Vec<usize>> {
    if !conf.prefixes.is_empty() || conf.dynamic_prefix.is_some() {
        // Find out if they were mentioned. If not, determine if the prefix
        // was used. If not, return None.
        let mut positions: Vec<usize> = vec![];

        if let Some(mention_end) = find_mention_end(&msg.content, conf) {
            positions.push(mention_end);
        } else if let Some(ref func) = conf.dynamic_prefix {
            if let Some(x) = func(ctx, msg) {
                if msg.content.starts_with(&x) {
                    positions.push(x.len());
                }
            } else {
                for n in &conf.prefixes {
                    if msg.content.starts_with(n) {
                        positions.push(n.len());
                    }
                }
            }
        } else {
            for n in &conf.prefixes {
                if msg.content.starts_with(n) {
                    positions.push(n.len());
                }
            }
        };

        if positions.is_empty() {
            return None;
        }

        if conf.allow_whitespace {
            let pos = *unsafe { positions.get_unchecked(0) };

            positions.insert(0, pos + 1);
        }

        Some(positions)
    } else if conf.on_mention.is_some() {
        find_mention_end(&msg.content, conf).map(|mention_end| {
            let mut positions = vec![mention_end];

            if conf.allow_whitespace {
                positions.insert(0, mention_end + 1);
            }

            positions
        })
    } else {
        None
    }
}

fn find_mention_end(content: &str, conf: &Configuration) -> Option<usize> {
    conf.on_mention.as_ref().and_then(|mentions| {
        mentions
            .iter()
            .find(|mention| content.starts_with(&mention[..]))
            .map(|m| m.len())
    })
}
<|endoftext|>


<commit_before>use std::io::{Listener, Acceptor};
use std::io::net::ip::SocketAddr;
use time::precise_time_ns;

use std::io::net::tcp::TcpListener;

use buffer::BufferedStream;

pub use self::request::{RequestBuffer, Request};
pub use self::response::ResponseWriter;

pub mod request;
pub mod response;

pub trait Server: Send + Clone {
	fn handle_request(&self, request: Request, response: &mut ResponseWriter) -> ();

	// XXX: this could also be implemented on the serve methods
	fn get_config(&self) -> Config;

	/**
	 * Attempt to bind to the address and port and start serving forever.
	 *
	 * This will only return if the initial connection fails or something else blows up.
	 */
    fn serve_forever(self) {
        let config = self.get_config();
        debug!("About to bind to {}", config.bind_address);
        let mut acceptor = match TcpListener::bind(config.bind_address.ip.to_str().as_slice(), config.bind_address.port).listen() {
            Err(err) => {
                error!("bind or listen failed :-(: {}", err);
                return;
            },
            Ok(acceptor) => acceptor,
        };
        debug!("listening");
        let (perf_sender, perf_receiver) = channel();
        spawn(proc() {
            perf_dumper(perf_receiver);
        });
        loop {
            let time_start = precise_time_ns();
            let stream = match acceptor.accept() {
                Err(error) => {
                    debug!("accept failed: {}", error);
                    // Question: is this the correct thing to do? We should probably be more
                    // intelligent, for there are some accept failures that are likely to be
                    // permanent, such that continuing would be a very bad idea, such as
                    // ENOBUFS/ENOMEM; and some where it should just be ignored, e.g.
                    // ECONNABORTED. TODO.
                    continue;
                },
                Ok(socket) => socket,
            };
            let child_perf_sender = perf_sender.clone();
            let child_self = self.clone();
            spawn(proc() {
                let mut time_start = time_start;
                let mut stream = BufferedStream::new(stream);
                debug!("accepted connection");
                let mut first = true;
                loop {  // A keep-alive loop, condition at end
                    let mut time_spawned = precise_time_ns();
                    let (request, err_status) = Request::load(&mut stream);
                    let close_connection = request.close_connection;
                    let time_request_made = precise_time_ns();
                    if !first {
                        // Subsequent requests on this connection have no spawn time.
                        // Moreover we cannot detect the time spent parsing the request as we have
                        // not exposed the time when the first byte was received.
                        time_start = time_request_made;
                        time_spawned = time_request_made;
                    }
                    let mut response = box ResponseWriter::new(&mut stream);
                    let time_response_made = precise_time_ns();
                    match err_status {
                        Ok(()) => {
                            child_self.handle_request(request, &mut *response);
                            // Ensure that we actually do send a response:
                            match response.try_write_headers() {
                                Err(err) => {
                                    error!("Writing headers failed: {}", err);
                                    return;  // Presumably bad connection, so give up.
                                },
                                Ok(_) => (),
                            }
                        },
                        Err(status) => {
                            // Uh oh, it's a response that I as a server cannot cope with.
                            // No good user-agent should have caused this, so for the moment
                            // at least I am content to send no body in the response.
                            response.status = status;
                            response.headers.content_length = Some(0);
                            match response.write_headers() {
                                Err(err) => {
                                    error!("Writing headers failed: {}", err);
                                    return;  // Presumably bad connection, so give up.
                                },
                                Ok(_) => (),
                            }
                        },
                    }
                    // Ensure the request is flushed, any Transfer-Encoding completed, etc.
                    match response.finish_response() {
                        Err(err) => {
                            error!("finishing response failed: {}", err);
                            return;  // Presumably bad connection, so give up.
                        },
                        Ok(_) => (),
                    }
                    let time_finished = precise_time_ns();
                    child_perf_sender.send((time_start, time_spawned, time_request_made, time_response_made, time_finished));

                    if close_connection {
                        break;
                    }
                    first = false;
                }
            });
        }
    }
}

/// The necessary configuration for an HTTP server.
///
/// At present, only the IP address and port to bind to are needed, but it's possible that other
/// options may turn up later.
pub struct Config {
	pub bind_address: SocketAddr,
}

static PERF_DUMP_FREQUENCY : u64 = 10_000;

/// Simple function to dump out perf stats every `PERF_DUMP_FREQUENCY` requests
fn perf_dumper(perf_receiver: Receiver<(u64, u64, u64, u64, u64)>) {
    // Total durations
    let mut td_spawn = 0u64;
    let mut td_request = 0u64;
    let mut td_response = 0u64;
    let mut td_handle = 0u64;
    let mut td_total = 0u64;
    let mut i = 0u64;
    loop {
        let data = perf_receiver.recv();
        let (start, spawned, request_made, response_made, finished) = data;
        td_spawn += spawned - start;
        td_request += request_made - spawned;
        td_response += response_made - request_made;
        td_handle += finished - response_made;
        td_total += finished - start;
        i += 1;
        if i % PERF_DUMP_FREQUENCY == 0 {
            println!("");
            println!("{} requests made thus far. Current means:", i);
            println!("- Total:               100%, {:12f}",
                     td_total as f64 / i as f64);
            println!("- Spawn:               {:3f}%, {:12f}",
                     100f64 * td_spawn as f64 / td_total as f64,
                     td_spawn as f64 / i as f64);
            println!("- Load request:        {:3f}%, {:12f}",
                     100f64 * td_request as f64 / td_total as f64,
                     td_request as f64 / i as f64);
            println!("- Initialise response: {:3f}%, {:12f}",
                     100f64 * td_response as f64 / td_total as f64,
                     td_response as f64 / i as f64);
            println!("- Handle:              {:3f}%, {:12f}",
                     100f64 * td_handle as f64 / td_total as f64,
                     td_handle as f64 / i as f64);
        }
    }
}
<commit_msg>(refactor) Removed legacy box over ResponseWriter.<commit_after>use std::io::{Listener, Acceptor};
use std::io::net::ip::SocketAddr;
use time::precise_time_ns;

use std::io::net::tcp::TcpListener;

use buffer::BufferedStream;

pub use self::request::{RequestBuffer, Request};
pub use self::response::ResponseWriter;

pub mod request;
pub mod response;

pub trait Server: Send + Clone {
	fn handle_request(&self, request: Request, response: &mut ResponseWriter) -> ();

	// XXX: this could also be implemented on the serve methods
	fn get_config(&self) -> Config;

	/**
	 * Attempt to bind to the address and port and start serving forever.
	 *
	 * This will only return if the initial connection fails or something else blows up.
	 */
    fn serve_forever(self) {
        let config = self.get_config();
        debug!("About to bind to {}", config.bind_address);
        let mut acceptor = match TcpListener::bind(config.bind_address.ip.to_str().as_slice(), config.bind_address.port).listen() {
            Err(err) => {
                error!("bind or listen failed :-(: {}", err);
                return;
            },
            Ok(acceptor) => acceptor,
        };
        debug!("listening");
        let (perf_sender, perf_receiver) = channel();
        spawn(proc() {
            perf_dumper(perf_receiver);
        });
        loop {
            let time_start = precise_time_ns();
            let stream = match acceptor.accept() {
                Err(error) => {
                    debug!("accept failed: {}", error);
                    // Question: is this the correct thing to do? We should probably be more
                    // intelligent, for there are some accept failures that are likely to be
                    // permanent, such that continuing would be a very bad idea, such as
                    // ENOBUFS/ENOMEM; and some where it should just be ignored, e.g.
                    // ECONNABORTED. TODO.
                    continue;
                },
                Ok(socket) => socket,
            };
            let child_perf_sender = perf_sender.clone();
            let child_self = self.clone();
            spawn(proc() {
                let mut time_start = time_start;
                let mut stream = BufferedStream::new(stream);
                debug!("accepted connection");
                let mut first = true;
                loop {  // A keep-alive loop, condition at end
                    let mut time_spawned = precise_time_ns();
                    let (request, err_status) = Request::load(&mut stream);
                    let close_connection = request.close_connection;
                    let time_request_made = precise_time_ns();
                    if !first {
                        // Subsequent requests on this connection have no spawn time.
                        // Moreover we cannot detect the time spent parsing the request as we have
                        // not exposed the time when the first byte was received.
                        time_start = time_request_made;
                        time_spawned = time_request_made;
                    }
                    let mut response = ResponseWriter::new(&mut stream);
                    let time_response_made = precise_time_ns();
                    match err_status {
                        Ok(()) => {
                            child_self.handle_request(request, &mut response);
                            // Ensure that we actually do send a response:
                            match response.try_write_headers() {
                                Err(err) => {
                                    error!("Writing headers failed: {}", err);
                                    return;  // Presumably bad connection, so give up.
                                },
                                Ok(_) => (),
                            }
                        },
                        Err(status) => {
                            // Uh oh, it's a response that I as a server cannot cope with.
                            // No good user-agent should have caused this, so for the moment
                            // at least I am content to send no body in the response.
                            response.status = status;
                            response.headers.content_length = Some(0);
                            match response.write_headers() {
                                Err(err) => {
                                    error!("Writing headers failed: {}", err);
                                    return;  // Presumably bad connection, so give up.
                                },
                                Ok(_) => (),
                            }
                        },
                    }
                    // Ensure the request is flushed, any Transfer-Encoding completed, etc.
                    match response.finish_response() {
                        Err(err) => {
                            error!("finishing response failed: {}", err);
                            return;  // Presumably bad connection, so give up.
                        },
                        Ok(_) => (),
                    }
                    let time_finished = precise_time_ns();
                    child_perf_sender.send((time_start, time_spawned, time_request_made, time_response_made, time_finished));

                    if close_connection {
                        break;
                    }
                    first = false;
                }
            });
        }
    }
}

/// The necessary configuration for an HTTP server.
///
/// At present, only the IP address and port to bind to are needed, but it's possible that other
/// options may turn up later.
pub struct Config {
	pub bind_address: SocketAddr,
}

static PERF_DUMP_FREQUENCY : u64 = 10_000;

/// Simple function to dump out perf stats every `PERF_DUMP_FREQUENCY` requests
fn perf_dumper(perf_receiver: Receiver<(u64, u64, u64, u64, u64)>) {
    // Total durations
    let mut td_spawn = 0u64;
    let mut td_request = 0u64;
    let mut td_response = 0u64;
    let mut td_handle = 0u64;
    let mut td_total = 0u64;
    let mut i = 0u64;
    loop {
        let data = perf_receiver.recv();
        let (start, spawned, request_made, response_made, finished) = data;
        td_spawn += spawned - start;
        td_request += request_made - spawned;
        td_response += response_made - request_made;
        td_handle += finished - response_made;
        td_total += finished - start;
        i += 1;
        if i % PERF_DUMP_FREQUENCY == 0 {
            println!("");
            println!("{} requests made thus far. Current means:", i);
            println!("- Total:               100%, {:12f}",
                     td_total as f64 / i as f64);
            println!("- Spawn:               {:3f}%, {:12f}",
                     100f64 * td_spawn as f64 / td_total as f64,
                     td_spawn as f64 / i as f64);
            println!("- Load request:        {:3f}%, {:12f}",
                     100f64 * td_request as f64 / td_total as f64,
                     td_request as f64 / i as f64);
            println!("- Initialise response: {:3f}%, {:12f}",
                     100f64 * td_response as f64 / td_total as f64,
                     td_response as f64 / i as f64);
            println!("- Handle:              {:3f}%, {:12f}",
                     100f64 * td_handle as f64 / td_total as f64,
                     td_handle as f64 / i as f64);
        }
    }
}
<|endoftext|>


<commit_before>use std::boxed::FnBox;
use std::collections::{HashMap, HashSet};
use std::sync::{RwLock, Mutex, Arc};
use std::sync::atomic::{AtomicU64, Ordering};
use threadpool::ThreadPool;
use num_cpus;
use bifrost_hasher::{hash_str, hash_bytes};
use raft::RaftService;
use rpc;
use serde;
use super::super::{StateMachineCtl, OpType};
use super::super::super::RaftMsg;
use super::*;

lazy_static! {
    pub static ref SUBSCRIPTIONS: RwLock<HashMap<u64, Subscriptions>> = RwLock::new(HashMap::new());
    pub static ref THREAD_POOL: Mutex<ThreadPool> = Mutex::new(ThreadPool::new(
                        num_cpus::get()
                    ));
}

pub struct Subscriber {
    pub session_id: u64,
    pub client: Arc<SyncServiceClient>
}

pub struct Subscriptions {
    next_id: u64,
    subscribers: HashMap<u64, Subscriber>,
    suber_subs: HashMap<u64, HashSet<u64>>, //suber_id -> sub_id
    subscriptions: HashMap<SubKey, HashSet<u64>>, // key -> sub_id
    sub_suber: HashMap<u64, u64>,
    sub_to_key: HashMap<u64, SubKey>, //sub_id -> sub_key
}

impl Subscriptions {

    pub fn new() -> Subscriptions {
        Subscriptions {
            next_id: 0,
            subscribers: HashMap::new(),
            suber_subs: HashMap::new(),
            subscriptions: HashMap::new(),
            sub_suber: HashMap::new(),
            sub_to_key: HashMap::new(),
        }
    }

    pub fn subscribe(&mut self, key: SubKey, address: String, session_id: u64) -> Result<u64, ()> {
        let sub_service_id = DEFAULT_SERVICE_ID;
        let suber_id = hash_str(address.clone());
        let suber_exists = self.subscribers.contains_key(&suber_id);
        let sub_id = self.next_id;
        let require_reload_suber = if suber_exists {
            let session_match = self.subscribers.get(&suber_id).unwrap().session_id == session_id;
            if !session_match {
                self.remove_subscriber(suber_id);
                true
            } else {false}
        } else {true};
        if !self.subscribers.contains_key(&suber_id) {
            self.subscribers.insert(suber_id, Subscriber {
                session_id: session_id,
                client: {
                    if let Ok(client) = rpc::DEFAULT_CLIENT_POOL.get(&address) {
                        SyncServiceClient::new(sub_service_id, client)
                    } else {
                        return Err(());
                    }
                }
            });
        }
        self.suber_subs.entry(suber_id).or_insert_with(|| HashSet::new()).insert(sub_id);
        self.subscriptions.entry(key).or_insert_with(|| HashSet::new()).insert(sub_id);
        self.sub_to_key.insert(sub_id, key);
        self.sub_suber.insert(sub_id, suber_id);

        self.next_id += 1;
        Ok(sub_id)
    }

    pub fn remove_subscriber(&mut self, suber_id: u64) {
        let suber_subs = if let Some(sub_ids) = self.suber_subs.get(&suber_id) {
            sub_ids.iter().cloned().collect()
        } else {Vec::<u64>::new()};
        for subs_id in suber_subs {
            self.remove_subscription(subs_id)
        }
        self.subscribers.remove(&suber_id);
        self.suber_subs.remove(&suber_id);
    }

    pub fn remove_subscription(&mut self, id: u64) {
        let sub_key = self.sub_to_key.remove(&id);
        if let Some(sub_key) = sub_key {
            if let Some(ref mut sub_subers) = self.subscriptions.get_mut(&sub_key) {
                sub_subers.remove(&id);
                self.sub_suber.remove(&id);
            }
        }
    }
}

pub struct SMCallback {
    pub raft_service: Arc<RaftService>,
    pub sm_id: u64,
}

impl SMCallback {
    pub fn new(state_machine_id: u64, raft_service: Arc<RaftService>) -> SMCallback {
        SMCallback {
            raft_service: raft_service,
            sm_id: state_machine_id,
        }
    }
    pub fn notify<R>(&self, func: &RaftMsg<R>, data: R)
    where R: serde::Serialize + Clone + Send + Sync {
        let (fn_id, op_type, pattern_data) = func.encode();
        match op_type {
            OpType::SUBSCRIBE => {
                let pattern_id = hash_bytes(&pattern_data.as_slice());
                let raft_sid = self.raft_service.options.service_id;
                let sm_id = self.sm_id;
                let key = (raft_sid, sm_id, fn_id, pattern_id);
                let subscriptions_map = SUBSCRIPTIONS.read().unwrap();
                if let Some(subscriptions) = subscriptions_map.get(&raft_sid) {
                    if let Some(sub_ids) = subscriptions.subscriptions.get(&key) {
                        let data = serialize!(&data);
                        for sub_id in sub_ids {
                            if let Some(subscriber_id) = subscriptions.sub_suber.get(&sub_id) {
                                if let Some(subscriber) = subscriptions.subscribers.get(&subscriber_id) {
                                    let data = data.clone();
                                    let client = subscriber.client.clone();
                                    THREAD_POOL.lock().unwrap().execute(move || {
                                        client.notify(key, data);
                                    });
                                }
                            }
                        }
                    }
                }
            },
            _ => {}
        }
    }
}
<commit_msg>add prints for debug<commit_after>use std::boxed::FnBox;
use std::collections::{HashMap, HashSet};
use std::sync::{RwLock, Mutex, Arc};
use std::sync::atomic::{AtomicU64, Ordering};
use threadpool::ThreadPool;
use num_cpus;
use bifrost_hasher::{hash_str, hash_bytes};
use raft::RaftService;
use rpc;
use serde;
use super::super::{StateMachineCtl, OpType};
use super::super::super::RaftMsg;
use super::*;

lazy_static! {
    pub static ref SUBSCRIPTIONS: RwLock<HashMap<u64, Subscriptions>> = RwLock::new(HashMap::new());
    pub static ref THREAD_POOL: Mutex<ThreadPool> = Mutex::new(ThreadPool::new(
                        num_cpus::get()
                    ));
}

pub struct Subscriber {
    pub session_id: u64,
    pub client: Arc<SyncServiceClient>
}

pub struct Subscriptions {
    next_id: u64,
    subscribers: HashMap<u64, Subscriber>,
    suber_subs: HashMap<u64, HashSet<u64>>, //suber_id -> sub_id
    subscriptions: HashMap<SubKey, HashSet<u64>>, // key -> sub_id
    sub_suber: HashMap<u64, u64>,
    sub_to_key: HashMap<u64, SubKey>, //sub_id -> sub_key
}

impl Subscriptions {

    pub fn new() -> Subscriptions {
        Subscriptions {
            next_id: 0,
            subscribers: HashMap::new(),
            suber_subs: HashMap::new(),
            subscriptions: HashMap::new(),
            sub_suber: HashMap::new(),
            sub_to_key: HashMap::new(),
        }
    }

    pub fn subscribe(&mut self, key: SubKey, address: String, session_id: u64) -> Result<u64, ()> {
        let sub_service_id = DEFAULT_SERVICE_ID;
        let suber_id = hash_str(address.clone());
        let suber_exists = self.subscribers.contains_key(&suber_id);
        let sub_id = self.next_id;
        let require_reload_suber = if suber_exists {
            let session_match = self.subscribers.get(&suber_id).unwrap().session_id == session_id;
            if !session_match {
                println!("SESSION ID NOT MATCH!!!");
                self.remove_subscriber(suber_id);
                true
            } else {false}
        } else {true};
        if !self.subscribers.contains_key(&suber_id) {
            self.subscribers.insert(suber_id, Subscriber {
                session_id: session_id,
                client: {
                    if let Ok(client) = rpc::DEFAULT_CLIENT_POOL.get(&address) {
                        SyncServiceClient::new(sub_service_id, client)
                    } else {
                        return Err(());
                    }
                }
            });
        }
        self.suber_subs.entry(suber_id).or_insert_with(|| HashSet::new()).insert(sub_id);
        self.subscriptions.entry(key).or_insert_with(|| HashSet::new()).insert(sub_id);
        self.sub_to_key.insert(sub_id, key);
        self.sub_suber.insert(sub_id, suber_id);

        self.next_id += 1;
        Ok(sub_id)
    }

    pub fn remove_subscriber(&mut self, suber_id: u64) {
        let suber_subs = if let Some(sub_ids) = self.suber_subs.get(&suber_id) {
            sub_ids.iter().cloned().collect()
        } else {Vec::<u64>::new()};
        for subs_id in suber_subs {
            self.remove_subscription(subs_id)
        }
        self.subscribers.remove(&suber_id);
        self.suber_subs.remove(&suber_id);
    }

    pub fn remove_subscription(&mut self, id: u64) {
        let sub_key = self.sub_to_key.remove(&id);
        if let Some(sub_key) = sub_key {
            if let Some(ref mut sub_subers) = self.subscriptions.get_mut(&sub_key) {
                sub_subers.remove(&id);
                self.sub_suber.remove(&id);
            }
        }
    }
}

pub struct SMCallback {
    pub raft_service: Arc<RaftService>,
    pub sm_id: u64,
}

impl SMCallback {
    pub fn new(state_machine_id: u64, raft_service: Arc<RaftService>) -> SMCallback {
        SMCallback {
            raft_service: raft_service,
            sm_id: state_machine_id,
        }
    }
    pub fn notify<R>(&self, func: &RaftMsg<R>, data: R)
    where R: serde::Serialize + Clone + Send + Sync {
        println!("===============");
        let (fn_id, op_type, pattern_data) = func.encode();
        match op_type {
            OpType::SUBSCRIBE => {
                let pattern_id = hash_bytes(&pattern_data.as_slice());
                let raft_sid = self.raft_service.options.service_id;
                let sm_id = self.sm_id;
                let key = (raft_sid, sm_id, fn_id, pattern_id);
                let subscriptions_map = SUBSCRIPTIONS.read().unwrap();
                if let Some(svr_subs) = subscriptions_map.get(&raft_sid) {
                    if let Some(sub_ids) = svr_subs.subscriptions.get(&key) {
                        let data = serialize!(&data);
                        for sub_id in sub_ids {
                            if let Some(subscriber_id) = svr_subs.sub_suber.get(&sub_id) {
                                if let Some(subscriber) = svr_subs.subscribers.get(&subscriber_id) {
                                    let data = data.clone();
                                    let client = subscriber.client.clone();
                                    println!("--------");
                                    THREAD_POOL.lock().unwrap().execute(move || {
                                        client.notify(key, data);
                                    });
                                }
                            } else {println!("||||||||||||||||");}
                        }
                    } else {
                        let keys: Vec<SubKey> = svr_subs.subscriptions.keys().cloned().collect();
                        println!(">>>>>>>>>>>>>> {:?} -> {:?}", keys, key);
                    }
                } else {println!("<<<<<<<<<<<<<<<");}
            },
            _ => {}

        }
    }
}
<|endoftext|>


<commit_before>pub struct AmazonRegion {
  pub id: &'static str,
  pub name: &'static str,
  pub endpoint: &'static str,
  pub marketplace_id_list: Vec<&'static str>,
  pub marketplaces: Vec<&'static AmazonMarketplace>,
}

pub struct AmazonMarketplace {
  pub id: &'static str,
  pub name: &'static str,
  pub region_id: &'static str,
  pub country_id: &'static str,
}

pub const REGION_ID_NA: &'static str = "na";
pub const REGION_ID_EU: &'static str = "eu";
pub const REGION_ID_IN: &'static str = "in";
pub const REGION_ID_CN: &'static str = "cn";
pub const REGION_ID_FE: &'static str = "fe";

pub const MARKETPLACE_ID_BR: &'static str = "A2Q3Y263D00KWC";
pub const MARKETPLACE_ID_CA: &'static str = "A2EUQ1WTGCTBG2";
pub const MARKETPLACE_ID_US: &'static str = "ATVPDKIKX0DER";
pub const MARKETPLACE_ID_MX: &'static str = "A1AM78C64UM0Y8";

pub const MARKETPLACE_ID_AE: &'static str = "A2VIGQ35RCS4UG";
pub const MARKETPLACE_ID_DE: &'static str = "A1PA6795UKMFR9";
pub const MARKETPLACE_ID_EG: &'static str = "ARBP9OOSHTCHU";
pub const MARKETPLACE_ID_ES: &'static str = "A1RKKUPIHCS9HS";
pub const MARKETPLACE_ID_FR: &'static str = "A13V1IB3VIYZZH";
pub const MARKETPLACE_ID_GB: &'static str = "A1F83G8C2ARO7P";
pub const MARKETPLACE_ID_IN: &'static str = "A21TJRUUN4KGV";
pub const MARKETPLACE_ID_IT: &'static str = "APJ6JRA9NG5V4";
pub const MARKETPLACE_ID_NL: &'static str = "A1805IZSGTT6HS";
pub const MARKETPLACE_ID_PL: &'static str = "A1C3SOZRARQ6R3";
pub const MARKETPLACE_ID_SA: &'static str = "A17E79C6D8DWNP";
pub const MARKETPLACE_ID_SE: &'static str = "A2NODRKZP88ZB9";
pub const MARKETPLACE_ID_TR: &'static str = "A33AVAJ2PDY3EV";

pub const MARKETPLACE_ID_JP: &'static str = "A1VC38T7YXB528";
pub const MARKETPLACE_ID_AU: &'static str = "A39IBJ37TRP1C6";
pub const MARKETPLACE_ID_SG: &'static str = "A19VAU5U5O7RUS";

pub const MARKETPLACE_ID_CN: &'static str = "AAHKV2X7AFYLW";

lazy_static! {
  pub static ref MARKETPLACES: Vec<AmazonMarketplace> = {
    macro_rules! item {
      ($id:expr, $region_id:expr, $name:expr, $country_id:expr) => {
        AmazonMarketplace {
          id: $id,
          name: $name,
          region_id: $region_id,
          country_id: $country_id,
        }
      };
    }
    let mut items = vec![];
    items.push(item!(MARKETPLACE_ID_CA, "na", "Brazil", "BR"));
    items.push(item!(MARKETPLACE_ID_CA, "na", "Canada", "CA"));
    items.push(item!(MARKETPLACE_ID_MX, "na", "Mexico", "MX"));
    items.push(item!(MARKETPLACE_ID_US, "na", "USA", "US"));

    items.push(item!(MARKETPLACE_ID_AE, "eu", "United Arab Emirates (U.A.E.)", "AE"));
    items.push(item!(MARKETPLACE_ID_DE, "eu", "Germany", "DE"));
    items.push(item!(MARKETPLACE_ID_EG, "eu", "Egypt", "EG"));
    items.push(item!(MARKETPLACE_ID_ES, "eu", "Spain", "ES"));
    items.push(item!(MARKETPLACE_ID_FR, "eu", "France", "FR"));
    items.push(item!(MARKETPLACE_ID_GB, "eu", "United Kingdom", "GB"));
    items.push(item!(MARKETPLACE_ID_IN, "in", "India", "IN"));
    items.push(item!(MARKETPLACE_ID_IT, "eu", "Italy", "IT"));
    items.push(item!(MARKETPLACE_ID_NL, "eu", "Netherlands", "NL"));
    items.push(item!(MARKETPLACE_ID_PL, "eu", "Poland", "PL"));
    items.push(item!(MARKETPLACE_ID_SA, "eu", "Saudi Arabia", "SA"));
    items.push(item!(MARKETPLACE_ID_SE, "eu", "Sweden", "SE"));
    items.push(item!(MARKETPLACE_ID_TR, "eu", "Turkey", "TR"));

    items.push(item!(MARKETPLACE_ID_JP, "fe", "Japan", "JP"));
    items.push(item!(MARKETPLACE_ID_AU, "fe", "Australia", "AU"));
    items.push(item!(MARKETPLACE_ID_SG, "fe", "Singapore", "SG"));

    items.push(item!(MARKETPLACE_ID_CN, "cn", "China", "CN"));

    items
  };
  pub static ref REGIONS: Vec<AmazonRegion> = {
    vec![
      AmazonRegion {
        id: REGION_ID_NA,
        name: "North America (NA)",
        endpoint: "mws.amazonservices.com",
        marketplace_id_list: get_region_marketplace_id_list("na"),
        marketplaces: get_region_marketplace_list("na"),
      },
      AmazonRegion {
        id: REGION_ID_EU,
        name: "Europe (EU)",
        endpoint: "mws-eu.amazonservices.com",
        marketplace_id_list: get_region_marketplace_id_list("eu"),
        marketplaces: get_region_marketplace_list("eu"),
      },
      AmazonRegion {
        id: REGION_ID_IN,
        name: "India (IN)",
        endpoint: "mws.amazonservices.com",
        marketplace_id_list: get_region_marketplace_id_list("in"),
        marketplaces: get_region_marketplace_list("in"),
      },
      AmazonRegion {
        id: REGION_ID_CN,
        name: "China (CN)",
        endpoint: "mws.amazonservices.com.cn",
        marketplace_id_list: get_region_marketplace_id_list("cn"),
        marketplaces: get_region_marketplace_list("cn"),
      },
      AmazonRegion {
        id: REGION_ID_FE,
        name: "Far East (FE)",
        endpoint: "mws.amazonservices.com.au",
        marketplace_id_list: get_region_marketplace_id_list("fe"),
        marketplaces: get_region_marketplace_list("fe"),
      },
    ]
  };
}

pub fn get_marketplace_region(id: &str) -> Option<&str> {
  get_marketplace(id).map(|m| m.region_id)
}

pub fn get_region_marketplace_id_list(region_id: &str) -> Vec<&'static str> {
  MARKETPLACES
    .iter()
    .filter_map(|m| {
      if m.region_id == region_id {
        Some(m.id)
      } else {
        None
      }
    })
    .collect()
}

pub fn get_region_marketplace_list(region_id: &str) -> Vec<&'static AmazonMarketplace> {
  MARKETPLACES
    .iter()
    .filter_map(|m| {
      if m.region_id == region_id {
        Some(m)
      } else {
        None
      }
    })
    .collect()
}

pub fn get_region(id: &str) -> Option<&'static AmazonRegion> {
  REGIONS.iter().find(|r| r.id == id)
}

pub fn get_marketplace(id: &str) -> Option<&'static AmazonMarketplace> {
  MARKETPLACES.iter().find(|r| r.id == id)
}

impl AmazonMarketplace {
  pub fn resolve_state_code(&self, country_id: &str, state: &str) -> Option<String> {
    match self.id {
      MARKETPLACE_ID_US if country_id == "US" => resolve_usa_state_code(state),
      _ => Some(state.to_string()),
    }
  }
}

const US_STATES: &'static [(&'static str, &'static str)] = &[
  ("AK", "alaska"),
  ("AL", "alabama"),
  ("AP", "apo/fpo: asia, pacific"),
  ("AR", "arkansas"),
  ("AZ", "arizona"),
  ("CA", "california"),
  ("CO", "colorado"),
  ("CT", "connecticut"),
  ("DC", "district of columbia"),
  ("DE", "delaware"),
  ("FL", "florida"),
  ("GA", "georgia"),
  ("HI", "hawaii"),
  ("IA", "iowa"),
  ("ID", "idaho"),
  ("IL", "illinois"),
  ("IN", "indiana"),
  ("KS", "kansas"),
  ("KY", "kentucky"),
  ("LA", "louisiana"),
  ("MA", "massachusetts"),
  ("MD", "maryland"),
  ("ME", "maine"),
  ("MI", "michigan"),
  ("MN", "minnesota"),
  ("MO", "missouri"),
  ("MS", "mississippi"),
  ("MT", "montana"),
  ("NC", "north carolina"),
  ("ND", "north dakota"),
  ("NE", "nebraska"),
  ("NH", "new hampshire"),
  ("NJ", "new jersey"),
  ("NM", "new mexico"),
  ("NV", "nevada"),
  ("NY", "new york"),
  ("OH", "ohio"),
  ("OK", "oklahoma"),
  ("OR", "oregon"),
  ("PA", "pennsylvania"),
  ("RI", "rhode island"),
  ("SC", "south carolina"),
  ("SD", "south dakota"),
  ("TN", "tennessee"),
  ("TX", "texas"),
  ("UT", "utah"),
  ("VA", "virginia"),
  ("VT", "vermont"),
  ("WA", "washington"),
  ("WI", "wisconsin"),
  ("WV", "west virginia"),
  ("WY", "wyoming"),
];

fn resolve_usa_state_code(state: &str) -> Option<String> {
  let v = normalize(state);
  if v.len() == 2 {
    let id_match = US_STATES.iter().find(|(id, _)| id == &v.to_uppercase());
    if let Some((id, _)) = id_match {
      return Some(id.to_string());
    }
  }

  let text_match = US_STATES.iter().find(|(_, text)| text == &v);
  if let Some((id, _)) = text_match {
    return Some(id.to_string());
  }

  None
}

#[test]
fn test_resolve_usa_state_code() {
  [
    "Al",
    "AL",
    "Alabama",
    "ALABAMA",
    "Alaska",
    "AR",
    "Arizona",
    "ARIZONA",
    "Arkansas",
    "az",
    "Az",
    "AZ",
    "ca",
    "Ca",
    "Ca.",
    "CA",
    "California",
    "CALIFORNIA",
    "Co",
    "CO",
    "Colorado",
    "COLORADO",
    "Connecticut",
    "CONNECTICUT",
    "Ct",
    "CT",
    "DC",
    "DE",
    "Delaware",
    "DELAWARE",
    "District of Columbia",
    "DISTRICT OF COLUMBIA",
    "fl",
    "Fl",
    "FL",
    "florida",
    "Florida",
    "FLORIDA",
    "Ga",
    "GA",
    "Georgia",
    "GEORGIA",
    "Hawaii",
    "HI",
    "IA",
    "ID",
    "Idaho",
    "il",
    "Il",
    "IL",
    "Illinois",
    "ILLINOIS",
    "IN",
    "Indiana",
    "INDIANA",
    "Iowa",
    "IOWA",
    "Kansas",
    "KANSAS",
    "Kentucky",
    "KENTUCKY",
    "ks",
    "KS",
    "Ky",
    "KY",
    "LA",
    "Louisiana",
    "ma",
    "Ma",
    "MA",
    "Maine",
    "MAINE",
    "Maryland",
    "MARYLAND",
    "massachusetts",
    "Massachusetts",
    "MASSACHUSETTS",
    "Md",
    "MD",
    "Me",
    "ME",
    "mi",
    "MI",
    "Michigan",
    "MICHIGAN",
    "Minnesota",
    "MINNESOTA",
    "Mississippi",
    "MISSISSIPPI",
    "Missouri",
    "MISSOURI",
    "mn",
    "MN",
    "mo",
    "Mo",
    "MO",
    "Montana",
    "MONTANA",
    "MS",
    "MT",
    "nc",
    "Nc",
    "NC",
    "ND",
    "NE",
    "Nebraska",
    "Nevada",
    "NEVADA",
    "New Hampshire",
    "New Jersey",
    "NEW JERSEY",
    "New Mexico",
    "NEW MEXICO",
    "new york",
    "New York",
    "NEW YORK",
    "NH",
    "nj",
    "Nj",
    "NJ",
    "N.J.",
    "NM",
    "North Carolina",
    "North Dakota",
    "NV",
    "ny",
    "Ny",
    "NY",
    "N.Y.",
    "OH",
    "ohio",
    "Ohio",
    "OHIO",
    "OK",
    "Oklahoma",
    "OKLAHOMA",
    "OR",
    "Oregon",
    "OREGON",
    "pa",
    "Pa",
    "PA",
    "Pennsylvania",
    "PENNSYLVANIA",
    "Rhode Island",
    "RHODE ISLAND",
    "RI",
    "SC",
    "SD",
    "South Carolina",
    "SOUTH CAROLINA",
    "Tennessee",
    "texas",
    "Texas",
    "TEXAS",
    "tn",
    "TN",
    "Tx",
    "TX",
    "TX ",
    "UT",
    "Utah",
    "UTAH",
    "Va",
    "VA",
    "Vermont",
    "VERMONT",
    "Virginia",
    "VIRGINIA",
    "VT",
    "wa",
    "Wa",
    "WA",
    "WA ",
    "Washington",
    "WASHINGTON",
    "West Virginia",
    "WEST VIRGINIA",
    "wi",
    "WI",
    "Wisconsin",
    "WISCONSIN",
    "WV",
    "WY",
    "Wyoming",
    "WYOMING",
  ]
  .iter()
  .for_each(|v| {
    assert!(
      resolve_usa_state_code(v).is_some(),
      "{}: {}",
      v,
      normalize(v)
    )
  })
}

fn normalize(text: &str) -> String {
  text
    .to_lowercase()
    .chars()
    .filter(|c| c.is_alphabetic() || c.is_whitespace())
    .collect::<String>()
    .split_whitespace()
    .collect::<Vec<_>>()
    .join(" ")
}
<commit_msg>add AmazonMarketplace::fulfillment_countries<commit_after>pub struct AmazonRegion {
  pub id: &'static str,
  pub name: &'static str,
  pub endpoint: &'static str,
  pub marketplace_id_list: Vec<&'static str>,
  pub marketplaces: Vec<&'static AmazonMarketplace>,
}

pub struct AmazonMarketplace {
  pub id: &'static str,
  pub name: &'static str,
  pub region_id: &'static str,
  pub country_id: &'static str,
  pub fulfillment_countries: &'static [&'static str],
}

pub const REGION_ID_NA: &'static str = "na";
pub const REGION_ID_EU: &'static str = "eu";
pub const REGION_ID_IN: &'static str = "in";
pub const REGION_ID_CN: &'static str = "cn";
pub const REGION_ID_FE: &'static str = "fe";

pub const MARKETPLACE_ID_BR: &'static str = "A2Q3Y263D00KWC";
pub const MARKETPLACE_ID_CA: &'static str = "A2EUQ1WTGCTBG2";
pub const MARKETPLACE_ID_US: &'static str = "ATVPDKIKX0DER";
pub const MARKETPLACE_ID_MX: &'static str = "A1AM78C64UM0Y8";

pub const MARKETPLACE_ID_AE: &'static str = "A2VIGQ35RCS4UG";
pub const MARKETPLACE_ID_DE: &'static str = "A1PA6795UKMFR9";
pub const MARKETPLACE_ID_EG: &'static str = "ARBP9OOSHTCHU";
pub const MARKETPLACE_ID_ES: &'static str = "A1RKKUPIHCS9HS";
pub const MARKETPLACE_ID_FR: &'static str = "A13V1IB3VIYZZH";
pub const MARKETPLACE_ID_GB: &'static str = "A1F83G8C2ARO7P";
pub const MARKETPLACE_ID_IN: &'static str = "A21TJRUUN4KGV";
pub const MARKETPLACE_ID_IT: &'static str = "APJ6JRA9NG5V4";
pub const MARKETPLACE_ID_NL: &'static str = "A1805IZSGTT6HS";
pub const MARKETPLACE_ID_PL: &'static str = "A1C3SOZRARQ6R3";
pub const MARKETPLACE_ID_SA: &'static str = "A17E79C6D8DWNP";
pub const MARKETPLACE_ID_SE: &'static str = "A2NODRKZP88ZB9";
pub const MARKETPLACE_ID_TR: &'static str = "A33AVAJ2PDY3EV";

pub const MARKETPLACE_ID_JP: &'static str = "A1VC38T7YXB528";
pub const MARKETPLACE_ID_AU: &'static str = "A39IBJ37TRP1C6";
pub const MARKETPLACE_ID_SG: &'static str = "A19VAU5U5O7RUS";

pub const MARKETPLACE_ID_CN: &'static str = "AAHKV2X7AFYLW";

lazy_static! {
  pub static ref MARKETPLACES: Vec<AmazonMarketplace> = {
    macro_rules! item {
      ($id:expr, $region_id:expr, $name:expr, $country_id:expr) => {
        AmazonMarketplace {
          id: $id,
          name: $name,
          region_id: $region_id,
          country_id: $country_id,
          fulfillment_countries: &[$country_id]
        }
      };
      ($id:expr, $region_id:expr, $name:expr, $country_id:expr, [$($fulfillment_country:expr),*]) => {
        AmazonMarketplace {
          id: $id,
          name: $name,
          region_id: $region_id,
          country_id: $country_id,
          fulfillment_countries: &[$($fulfillment_country),*],
        }
      };
    }
    let mut items = vec![];
    items.push(item!(MARKETPLACE_ID_CA, "na", "Brazil", "BR"));
    items.push(item!(MARKETPLACE_ID_CA, "na", "Canada", "CA"));
    items.push(item!(MARKETPLACE_ID_MX, "na", "Mexico", "MX"));
    items.push(item!(MARKETPLACE_ID_US, "na", "USA", "US"));

    items.push(item!(MARKETPLACE_ID_AE, "eu", "United Arab Emirates (U.A.E.)", "AE"));
    items.push(item!(MARKETPLACE_ID_DE, "eu", "Germany", "DE", ["DE", "PL", "AT"]));
    items.push(item!(MARKETPLACE_ID_EG, "eu", "Egypt", "EG"));
    items.push(item!(MARKETPLACE_ID_ES, "eu", "Spain", "ES"));
    items.push(item!(MARKETPLACE_ID_FR, "eu", "France", "FR"));
    items.push(item!(MARKETPLACE_ID_GB, "eu", "United Kingdom", "GB"));
    items.push(item!(MARKETPLACE_ID_IN, "in", "India", "IN"));
    items.push(item!(MARKETPLACE_ID_IT, "eu", "Italy", "IT"));
    items.push(item!(MARKETPLACE_ID_NL, "eu", "Netherlands", "NL"));
    items.push(item!(MARKETPLACE_ID_PL, "eu", "Poland", "PL"));
    items.push(item!(MARKETPLACE_ID_SA, "eu", "Saudi Arabia", "SA"));
    items.push(item!(MARKETPLACE_ID_SE, "eu", "Sweden", "SE"));
    items.push(item!(MARKETPLACE_ID_TR, "eu", "Turkey", "TR"));

    items.push(item!(MARKETPLACE_ID_JP, "fe", "Japan", "JP"));
    items.push(item!(MARKETPLACE_ID_AU, "fe", "Australia", "AU"));
    items.push(item!(MARKETPLACE_ID_SG, "fe", "Singapore", "SG"));

    items.push(item!(MARKETPLACE_ID_CN, "cn", "China", "CN"));

    items
  };
  pub static ref REGIONS: Vec<AmazonRegion> = {
    vec![
      AmazonRegion {
        id: REGION_ID_NA,
        name: "North America (NA)",
        endpoint: "mws.amazonservices.com",
        marketplace_id_list: get_region_marketplace_id_list("na"),
        marketplaces: get_region_marketplace_list("na"),
      },
      AmazonRegion {
        id: REGION_ID_EU,
        name: "Europe (EU)",
        endpoint: "mws-eu.amazonservices.com",
        marketplace_id_list: get_region_marketplace_id_list("eu"),
        marketplaces: get_region_marketplace_list("eu"),
      },
      AmazonRegion {
        id: REGION_ID_IN,
        name: "India (IN)",
        endpoint: "mws.amazonservices.com",
        marketplace_id_list: get_region_marketplace_id_list("in"),
        marketplaces: get_region_marketplace_list("in"),
      },
      AmazonRegion {
        id: REGION_ID_CN,
        name: "China (CN)",
        endpoint: "mws.amazonservices.com.cn",
        marketplace_id_list: get_region_marketplace_id_list("cn"),
        marketplaces: get_region_marketplace_list("cn"),
      },
      AmazonRegion {
        id: REGION_ID_FE,
        name: "Far East (FE)",
        endpoint: "mws.amazonservices.com.au",
        marketplace_id_list: get_region_marketplace_id_list("fe"),
        marketplaces: get_region_marketplace_list("fe"),
      },
    ]
  };
}

pub fn get_marketplace_region(id: &str) -> Option<&str> {
  get_marketplace(id).map(|m| m.region_id)
}

pub fn get_region_marketplace_id_list(region_id: &str) -> Vec<&'static str> {
  MARKETPLACES
    .iter()
    .filter_map(|m| {
      if m.region_id == region_id {
        Some(m.id)
      } else {
        None
      }
    })
    .collect()
}

pub fn get_region_marketplace_list(region_id: &str) -> Vec<&'static AmazonMarketplace> {
  MARKETPLACES
    .iter()
    .filter_map(|m| {
      if m.region_id == region_id {
        Some(m)
      } else {
        None
      }
    })
    .collect()
}

pub fn get_region(id: &str) -> Option<&'static AmazonRegion> {
  REGIONS.iter().find(|r| r.id == id)
}

pub fn get_marketplace(id: &str) -> Option<&'static AmazonMarketplace> {
  MARKETPLACES.iter().find(|r| r.id == id)
}

impl AmazonMarketplace {
  pub fn resolve_state_code(&self, country_id: &str, state: &str) -> Option<String> {
    match self.id {
      MARKETPLACE_ID_US if country_id == "US" => resolve_usa_state_code(state),
      _ => Some(state.to_string()),
    }
  }
}

const US_STATES: &'static [(&'static str, &'static str)] = &[
  ("AK", "alaska"),
  ("AL", "alabama"),
  ("AP", "apo/fpo: asia, pacific"),
  ("AR", "arkansas"),
  ("AZ", "arizona"),
  ("CA", "california"),
  ("CO", "colorado"),
  ("CT", "connecticut"),
  ("DC", "district of columbia"),
  ("DE", "delaware"),
  ("FL", "florida"),
  ("GA", "georgia"),
  ("HI", "hawaii"),
  ("IA", "iowa"),
  ("ID", "idaho"),
  ("IL", "illinois"),
  ("IN", "indiana"),
  ("KS", "kansas"),
  ("KY", "kentucky"),
  ("LA", "louisiana"),
  ("MA", "massachusetts"),
  ("MD", "maryland"),
  ("ME", "maine"),
  ("MI", "michigan"),
  ("MN", "minnesota"),
  ("MO", "missouri"),
  ("MS", "mississippi"),
  ("MT", "montana"),
  ("NC", "north carolina"),
  ("ND", "north dakota"),
  ("NE", "nebraska"),
  ("NH", "new hampshire"),
  ("NJ", "new jersey"),
  ("NM", "new mexico"),
  ("NV", "nevada"),
  ("NY", "new york"),
  ("OH", "ohio"),
  ("OK", "oklahoma"),
  ("OR", "oregon"),
  ("PA", "pennsylvania"),
  ("RI", "rhode island"),
  ("SC", "south carolina"),
  ("SD", "south dakota"),
  ("TN", "tennessee"),
  ("TX", "texas"),
  ("UT", "utah"),
  ("VA", "virginia"),
  ("VT", "vermont"),
  ("WA", "washington"),
  ("WI", "wisconsin"),
  ("WV", "west virginia"),
  ("WY", "wyoming"),
];

fn resolve_usa_state_code(state: &str) -> Option<String> {
  let v = normalize(state);
  if v.len() == 2 {
    let id_match = US_STATES.iter().find(|(id, _)| id == &v.to_uppercase());
    if let Some((id, _)) = id_match {
      return Some(id.to_string());
    }
  }

  let text_match = US_STATES.iter().find(|(_, text)| text == &v);
  if let Some((id, _)) = text_match {
    return Some(id.to_string());
  }

  None
}

#[test]
fn test_resolve_usa_state_code() {
  [
    "Al",
    "AL",
    "Alabama",
    "ALABAMA",
    "Alaska",
    "AR",
    "Arizona",
    "ARIZONA",
    "Arkansas",
    "az",
    "Az",
    "AZ",
    "ca",
    "Ca",
    "Ca.",
    "CA",
    "California",
    "CALIFORNIA",
    "Co",
    "CO",
    "Colorado",
    "COLORADO",
    "Connecticut",
    "CONNECTICUT",
    "Ct",
    "CT",
    "DC",
    "DE",
    "Delaware",
    "DELAWARE",
    "District of Columbia",
    "DISTRICT OF COLUMBIA",
    "fl",
    "Fl",
    "FL",
    "florida",
    "Florida",
    "FLORIDA",
    "Ga",
    "GA",
    "Georgia",
    "GEORGIA",
    "Hawaii",
    "HI",
    "IA",
    "ID",
    "Idaho",
    "il",
    "Il",
    "IL",
    "Illinois",
    "ILLINOIS",
    "IN",
    "Indiana",
    "INDIANA",
    "Iowa",
    "IOWA",
    "Kansas",
    "KANSAS",
    "Kentucky",
    "KENTUCKY",
    "ks",
    "KS",
    "Ky",
    "KY",
    "LA",
    "Louisiana",
    "ma",
    "Ma",
    "MA",
    "Maine",
    "MAINE",
    "Maryland",
    "MARYLAND",
    "massachusetts",
    "Massachusetts",
    "MASSACHUSETTS",
    "Md",
    "MD",
    "Me",
    "ME",
    "mi",
    "MI",
    "Michigan",
    "MICHIGAN",
    "Minnesota",
    "MINNESOTA",
    "Mississippi",
    "MISSISSIPPI",
    "Missouri",
    "MISSOURI",
    "mn",
    "MN",
    "mo",
    "Mo",
    "MO",
    "Montana",
    "MONTANA",
    "MS",
    "MT",
    "nc",
    "Nc",
    "NC",
    "ND",
    "NE",
    "Nebraska",
    "Nevada",
    "NEVADA",
    "New Hampshire",
    "New Jersey",
    "NEW JERSEY",
    "New Mexico",
    "NEW MEXICO",
    "new york",
    "New York",
    "NEW YORK",
    "NH",
    "nj",
    "Nj",
    "NJ",
    "N.J.",
    "NM",
    "North Carolina",
    "North Dakota",
    "NV",
    "ny",
    "Ny",
    "NY",
    "N.Y.",
    "OH",
    "ohio",
    "Ohio",
    "OHIO",
    "OK",
    "Oklahoma",
    "OKLAHOMA",
    "OR",
    "Oregon",
    "OREGON",
    "pa",
    "Pa",
    "PA",
    "Pennsylvania",
    "PENNSYLVANIA",
    "Rhode Island",
    "RHODE ISLAND",
    "RI",
    "SC",
    "SD",
    "South Carolina",
    "SOUTH CAROLINA",
    "Tennessee",
    "texas",
    "Texas",
    "TEXAS",
    "tn",
    "TN",
    "Tx",
    "TX",
    "TX ",
    "UT",
    "Utah",
    "UTAH",
    "Va",
    "VA",
    "Vermont",
    "VERMONT",
    "Virginia",
    "VIRGINIA",
    "VT",
    "wa",
    "Wa",
    "WA",
    "WA ",
    "Washington",
    "WASHINGTON",
    "West Virginia",
    "WEST VIRGINIA",
    "wi",
    "WI",
    "Wisconsin",
    "WISCONSIN",
    "WV",
    "WY",
    "Wyoming",
    "WYOMING",
  ]
  .iter()
  .for_each(|v| {
    assert!(
      resolve_usa_state_code(v).is_some(),
      "{}: {}",
      v,
      normalize(v)
    )
  })
}

fn normalize(text: &str) -> String {
  text
    .to_lowercase()
    .chars()
    .filter(|c| c.is_alphabetic() || c.is_whitespace())
    .collect::<String>()
    .split_whitespace()
    .collect::<Vec<_>>()
    .join(" ")
}
<|endoftext|>