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

//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
//! We implement the prefixed `@-moz-document`.

use crate::media_queries::Device;
use crate::parser::{Parse, ParserContext};
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter;
use crate::stylesheets::CssRules;
use crate::values::CssUrl;
use cssparser::{Parser, SourceLocation};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};

#[derive(Debug, ToShmem)]
/// A @-moz-document rule
pub struct DocumentRule {
    /// The parsed condition
    pub condition: DocumentCondition,
    /// Child rules
    pub rules: Arc<Locked<CssRules>>,
    /// The line and column of the rule's source code.
    pub source_location: SourceLocation,
}

impl DocumentRule {
    /// Measure heap usage.
    #[cfg(feature = "gecko")]
    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
        // Measurement of other fields may be added later.
        self.rules.unconditional_shallow_size_of(ops) +
            self.rules.read_with(guard).size_of(guard, ops)
    }
}

impl ToCssWithGuard for DocumentRule {
    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
        dest.write_str("@-moz-document ")?;
        self.condition.to_css(&mut CssWriter::new(dest))?;
        dest.write_str(" {")?;
        for rule in self.rules.read_with(guard).0.iter() {
            dest.write_str(" ")?;
            rule.to_css(guard, dest)?;
        }
        dest.write_str(" }")
    }
}

impl DeepCloneWithLock for DocumentRule {
    /// Deep clones this DocumentRule.
    fn deep_clone_with_lock(
        &self,
        lock: &SharedRwLock,
        guard: &SharedRwLockReadGuard,
        params: &DeepCloneParams,
    ) -> Self {
        let rules = self.rules.read_with(guard);
        DocumentRule {
            condition: self.condition.clone(),
            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
            source_location: self.source_location.clone(),
        }
    }
}

/// The kind of media document that the rule will match.
#[derive(Clone, Copy, Debug, Parse, PartialEq, ToCss, ToShmem)]
#[allow(missing_docs)]
pub enum MediaDocumentKind {
    All,
    Plugin,
    Image,
    Video,
}

/// A matching function for a `@document` rule's condition.
#[derive(Clone, Debug, ToCss, ToShmem)]
pub enum DocumentMatchingFunction {
    /// Exact URL matching function. It evaluates to true whenever the
    /// URL of the document being styled is exactly the URL given.
    Url(CssUrl),
    /// URL prefix matching function. It evaluates to true whenever the
    /// URL of the document being styled has the argument to the
    /// function as an initial substring (which is true when the two
    /// strings are equal). When the argument is the empty string,
    /// it evaluates to true for all documents.
    #[css(function)]
    UrlPrefix(String),
    /// Domain matching function. It evaluates to true whenever the URL
    /// of the document being styled has a host subcomponent and that
    /// host subcomponent is exactly the argument to the ‘domain()’
    /// function or a final substring of the host component is a
    /// period (U+002E) immediately followed by the argument to the
    /// ‘domain()’ function.
    #[css(function)]
    Domain(String),
    /// Regular expression matching function. It evaluates to true
    /// whenever the regular expression matches the entirety of the URL
    /// of the document being styled.
    #[css(function)]
    Regexp(String),
    /// Matching function for a media document.
    #[css(function)]
    MediaDocument(MediaDocumentKind),
}

macro_rules! parse_quoted_or_unquoted_string {
    ($input:ident, $url_matching_function:expr) => {
        $input.parse_nested_block(|input| {
            let start = input.position();
            input
                .parse_entirely(|input| {
                    let string = input.expect_string()?;
                    Ok($url_matching_function(string.as_ref().to_owned()))
                })
                .or_else(|_: ParseError| {
                    while let Ok(_) = input.next() {}
                    Ok($url_matching_function(input.slice_from(start).to_string()))
                })
        })
    };
}

impl DocumentMatchingFunction {
    /// Parse a URL matching function for a`@document` rule's condition.
    pub fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if let Ok(url) = input.try(|input| CssUrl::parse(context, input)) {
            return Ok(DocumentMatchingFunction::Url(url));
        }

        let location = input.current_source_location();
        let function = input.expect_function()?.clone();
        match_ignore_ascii_case! { &function,
            "url-prefix" => {
                parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::UrlPrefix)
            },
            "domain" => {
                parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::Domain)
            },
            "regexp" => {
                input.parse_nested_block(|input| {
                    Ok(DocumentMatchingFunction::Regexp(
                        input.expect_string()?.as_ref().to_owned(),
                    ))
                })
            },
            "media-document" => {
                input.parse_nested_block(|input| {
                    let kind = MediaDocumentKind::parse(input)?;
                    Ok(DocumentMatchingFunction::MediaDocument(kind))
                })
            },
            _ => {
                Err(location.new_custom_error(
                    StyleParseErrorKind::UnexpectedFunction(function.clone())
                ))
            },
        }
    }

    #[cfg(feature = "gecko")]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, device: &Device) -> bool {
        use crate::gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
        use crate::gecko_bindings::structs::DocumentMatchingFunction as GeckoDocumentMatchingFunction;
        use nsstring::nsCStr;

        let func = match *self {
            DocumentMatchingFunction::Url(_) => GeckoDocumentMatchingFunction::URL,
            DocumentMatchingFunction::UrlPrefix(_) => GeckoDocumentMatchingFunction::URLPrefix,
            DocumentMatchingFunction::Domain(_) => GeckoDocumentMatchingFunction::Domain,
            DocumentMatchingFunction::Regexp(_) => GeckoDocumentMatchingFunction::RegExp,
            DocumentMatchingFunction::MediaDocument(_) => {
                GeckoDocumentMatchingFunction::MediaDocument
            },
        };

        let pattern = nsCStr::from(match *self {
            DocumentMatchingFunction::Url(ref url) => url.as_str(),
            DocumentMatchingFunction::UrlPrefix(ref pat) |
            DocumentMatchingFunction::Domain(ref pat) |
            DocumentMatchingFunction::Regexp(ref pat) => pat,
            DocumentMatchingFunction::MediaDocument(kind) => match kind {
                MediaDocumentKind::All => "all",
                MediaDocumentKind::Image => "image",
                MediaDocumentKind::Plugin => "plugin",
                MediaDocumentKind::Video => "video",
            },
        });
        unsafe { Gecko_DocumentRule_UseForPresentation(device.document(), &*pattern, func) }
    }

    #[cfg(not(feature = "gecko"))]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, _: &Device) -> bool {
        false
    }
}

/// A `@document` rule's condition.
///
/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document>
///
/// The `@document` rule's condition is written as a comma-separated list of
/// URL matching functions, and the condition evaluates to true whenever any
/// one of those functions evaluates to true.
#[css(comma)]
#[derive(Clone, Debug, ToCss, ToShmem)]
pub struct DocumentCondition(#[css(iterable)] Vec<DocumentMatchingFunction>);

impl DocumentCondition {
    /// Parse a document condition.
    pub fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let conditions =
            input.parse_comma_separated(|input| DocumentMatchingFunction::parse(context, input))?;

        let condition = DocumentCondition(conditions);
        if !condition.allowed_in(context) {
            return Err(
                input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(
                    "-moz-document".into(),
                )),
            );
        }
        Ok(condition)
    }

    /// Evaluate a document condition.
    pub fn evaluate(&self, device: &Device) -> bool {
        self.0
            .iter()
            .any(|url_matching_function| url_matching_function.evaluate(device))
    }

    #[cfg(feature = "servo")]
    fn allowed_in(&self, _: &ParserContext) -> bool {
        false
    }

    #[cfg(feature = "gecko")]
    fn allowed_in(&self, context: &ParserContext) -> bool {
        use crate::stylesheets::Origin;
        use static_prefs::pref;

        if context.stylesheet_origin != Origin::Author {
            return true;
        }

        if pref!("layout.css.moz-document.content.enabled") {
            return true;
        }

        if !pref!("layout.css.moz-document.url-prefix-hack.enabled") {
            return false;
        }

        // Allow a single url-prefix() for compatibility.
        //
        // See bug 1446470 and dependencies.
        if self.0.len() != 1 {
            return false;
        }

        // NOTE(emilio): This technically allows url-prefix("") too, but...
        match self.0[0] {
            DocumentMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(),
            _ => false,
        }
    }
}
<commit_msg>style: Remove the @-moz-document url-prefix() hack preference, enable it everywhere.<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */

//! [@document rules](https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document)
//! initially in CSS Conditional Rules Module Level 3, @document has been postponed to the level 4.
//! We implement the prefixed `@-moz-document`.

use crate::media_queries::Device;
use crate::parser::{Parse, ParserContext};
use crate::shared_lock::{DeepCloneParams, DeepCloneWithLock, Locked};
use crate::shared_lock::{SharedRwLock, SharedRwLockReadGuard, ToCssWithGuard};
use crate::str::CssStringWriter;
use crate::stylesheets::CssRules;
use crate::values::CssUrl;
use cssparser::{Parser, SourceLocation};
#[cfg(feature = "gecko")]
use malloc_size_of::{MallocSizeOfOps, MallocUnconditionalShallowSizeOf};
use servo_arc::Arc;
use std::fmt::{self, Write};
use style_traits::{CssWriter, ParseError, StyleParseErrorKind, ToCss};

#[derive(Debug, ToShmem)]
/// A @-moz-document rule
pub struct DocumentRule {
    /// The parsed condition
    pub condition: DocumentCondition,
    /// Child rules
    pub rules: Arc<Locked<CssRules>>,
    /// The line and column of the rule's source code.
    pub source_location: SourceLocation,
}

impl DocumentRule {
    /// Measure heap usage.
    #[cfg(feature = "gecko")]
    pub fn size_of(&self, guard: &SharedRwLockReadGuard, ops: &mut MallocSizeOfOps) -> usize {
        // Measurement of other fields may be added later.
        self.rules.unconditional_shallow_size_of(ops) +
            self.rules.read_with(guard).size_of(guard, ops)
    }
}

impl ToCssWithGuard for DocumentRule {
    fn to_css(&self, guard: &SharedRwLockReadGuard, dest: &mut CssStringWriter) -> fmt::Result {
        dest.write_str("@-moz-document ")?;
        self.condition.to_css(&mut CssWriter::new(dest))?;
        dest.write_str(" {")?;
        for rule in self.rules.read_with(guard).0.iter() {
            dest.write_str(" ")?;
            rule.to_css(guard, dest)?;
        }
        dest.write_str(" }")
    }
}

impl DeepCloneWithLock for DocumentRule {
    /// Deep clones this DocumentRule.
    fn deep_clone_with_lock(
        &self,
        lock: &SharedRwLock,
        guard: &SharedRwLockReadGuard,
        params: &DeepCloneParams,
    ) -> Self {
        let rules = self.rules.read_with(guard);
        DocumentRule {
            condition: self.condition.clone(),
            rules: Arc::new(lock.wrap(rules.deep_clone_with_lock(lock, guard, params))),
            source_location: self.source_location.clone(),
        }
    }
}

/// The kind of media document that the rule will match.
#[derive(Clone, Copy, Debug, Parse, PartialEq, ToCss, ToShmem)]
#[allow(missing_docs)]
pub enum MediaDocumentKind {
    All,
    Plugin,
    Image,
    Video,
}

/// A matching function for a `@document` rule's condition.
#[derive(Clone, Debug, ToCss, ToShmem)]
pub enum DocumentMatchingFunction {
    /// Exact URL matching function. It evaluates to true whenever the
    /// URL of the document being styled is exactly the URL given.
    Url(CssUrl),
    /// URL prefix matching function. It evaluates to true whenever the
    /// URL of the document being styled has the argument to the
    /// function as an initial substring (which is true when the two
    /// strings are equal). When the argument is the empty string,
    /// it evaluates to true for all documents.
    #[css(function)]
    UrlPrefix(String),
    /// Domain matching function. It evaluates to true whenever the URL
    /// of the document being styled has a host subcomponent and that
    /// host subcomponent is exactly the argument to the ‘domain()’
    /// function or a final substring of the host component is a
    /// period (U+002E) immediately followed by the argument to the
    /// ‘domain()’ function.
    #[css(function)]
    Domain(String),
    /// Regular expression matching function. It evaluates to true
    /// whenever the regular expression matches the entirety of the URL
    /// of the document being styled.
    #[css(function)]
    Regexp(String),
    /// Matching function for a media document.
    #[css(function)]
    MediaDocument(MediaDocumentKind),
}

macro_rules! parse_quoted_or_unquoted_string {
    ($input:ident, $url_matching_function:expr) => {
        $input.parse_nested_block(|input| {
            let start = input.position();
            input
                .parse_entirely(|input| {
                    let string = input.expect_string()?;
                    Ok($url_matching_function(string.as_ref().to_owned()))
                })
                .or_else(|_: ParseError| {
                    while let Ok(_) = input.next() {}
                    Ok($url_matching_function(input.slice_from(start).to_string()))
                })
        })
    };
}

impl DocumentMatchingFunction {
    /// Parse a URL matching function for a`@document` rule's condition.
    pub fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        if let Ok(url) = input.try(|input| CssUrl::parse(context, input)) {
            return Ok(DocumentMatchingFunction::Url(url));
        }

        let location = input.current_source_location();
        let function = input.expect_function()?.clone();
        match_ignore_ascii_case! { &function,
            "url-prefix" => {
                parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::UrlPrefix)
            },
            "domain" => {
                parse_quoted_or_unquoted_string!(input, DocumentMatchingFunction::Domain)
            },
            "regexp" => {
                input.parse_nested_block(|input| {
                    Ok(DocumentMatchingFunction::Regexp(
                        input.expect_string()?.as_ref().to_owned(),
                    ))
                })
            },
            "media-document" => {
                input.parse_nested_block(|input| {
                    let kind = MediaDocumentKind::parse(input)?;
                    Ok(DocumentMatchingFunction::MediaDocument(kind))
                })
            },
            _ => {
                Err(location.new_custom_error(
                    StyleParseErrorKind::UnexpectedFunction(function.clone())
                ))
            },
        }
    }

    #[cfg(feature = "gecko")]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, device: &Device) -> bool {
        use crate::gecko_bindings::bindings::Gecko_DocumentRule_UseForPresentation;
        use crate::gecko_bindings::structs::DocumentMatchingFunction as GeckoDocumentMatchingFunction;
        use nsstring::nsCStr;

        let func = match *self {
            DocumentMatchingFunction::Url(_) => GeckoDocumentMatchingFunction::URL,
            DocumentMatchingFunction::UrlPrefix(_) => GeckoDocumentMatchingFunction::URLPrefix,
            DocumentMatchingFunction::Domain(_) => GeckoDocumentMatchingFunction::Domain,
            DocumentMatchingFunction::Regexp(_) => GeckoDocumentMatchingFunction::RegExp,
            DocumentMatchingFunction::MediaDocument(_) => {
                GeckoDocumentMatchingFunction::MediaDocument
            },
        };

        let pattern = nsCStr::from(match *self {
            DocumentMatchingFunction::Url(ref url) => url.as_str(),
            DocumentMatchingFunction::UrlPrefix(ref pat) |
            DocumentMatchingFunction::Domain(ref pat) |
            DocumentMatchingFunction::Regexp(ref pat) => pat,
            DocumentMatchingFunction::MediaDocument(kind) => match kind {
                MediaDocumentKind::All => "all",
                MediaDocumentKind::Image => "image",
                MediaDocumentKind::Plugin => "plugin",
                MediaDocumentKind::Video => "video",
            },
        });
        unsafe { Gecko_DocumentRule_UseForPresentation(device.document(), &*pattern, func) }
    }

    #[cfg(not(feature = "gecko"))]
    /// Evaluate a URL matching function.
    pub fn evaluate(&self, _: &Device) -> bool {
        false
    }
}

/// A `@document` rule's condition.
///
/// <https://www.w3.org/TR/2012/WD-css3-conditional-20120911/#at-document>
///
/// The `@document` rule's condition is written as a comma-separated list of
/// URL matching functions, and the condition evaluates to true whenever any
/// one of those functions evaluates to true.
#[css(comma)]
#[derive(Clone, Debug, ToCss, ToShmem)]
pub struct DocumentCondition(#[css(iterable)] Vec<DocumentMatchingFunction>);

impl DocumentCondition {
    /// Parse a document condition.
    pub fn parse<'i, 't>(
        context: &ParserContext,
        input: &mut Parser<'i, 't>,
    ) -> Result<Self, ParseError<'i>> {
        let conditions =
            input.parse_comma_separated(|input| DocumentMatchingFunction::parse(context, input))?;

        let condition = DocumentCondition(conditions);
        if !condition.allowed_in(context) {
            return Err(
                input.new_custom_error(StyleParseErrorKind::UnsupportedAtRule(
                    "-moz-document".into(),
                )),
            );
        }
        Ok(condition)
    }

    /// Evaluate a document condition.
    pub fn evaluate(&self, device: &Device) -> bool {
        self.0
            .iter()
            .any(|url_matching_function| url_matching_function.evaluate(device))
    }

    #[cfg(feature = "servo")]
    fn allowed_in(&self, _: &ParserContext) -> bool {
        false
    }

    #[cfg(feature = "gecko")]
    fn allowed_in(&self, context: &ParserContext) -> bool {
        use crate::stylesheets::Origin;
        use static_prefs::pref;

        if context.stylesheet_origin != Origin::Author {
            return true;
        }

        if pref!("layout.css.moz-document.content.enabled") {
            return true;
        }

        // Allow a single url-prefix() for compatibility.
        //
        // See bug 1446470 and dependencies.
        if self.0.len() != 1 {
            return false;
        }

        // NOTE(emilio): This technically allows url-prefix("") too, but...
        match self.0[0] {
            DocumentMatchingFunction::UrlPrefix(ref prefix) => prefix.is_empty(),
            _ => false,
        }
    }
}
<|endoftext|>


<commit_before>extern crate sovrin;

#[macro_use]
extern crate lazy_static;

#[path = "utils/mod.rs"]
mod utils;

use sovrin::api::ErrorCode;
use sovrin::api::pool::{sovrin_create_pool_ledger_config, sovrin_open_pool_ledger};
use sovrin::api::ledger::sovrin_submit_request;

#[path = "../src/utils/environment.rs"]
mod environment;

use environment::EnvironmentUtils;

use std::fs;

use std::ptr::null;

use std::time::Duration;

use utils::callbacks::CallbacksHelpers;

use std::sync::mpsc::{channel};
use std::ffi::{CString};

#[test]
fn sovrin_create_pool_ledger_can_be_called() {
    let (sender, receiver) = channel();

    let cb = Box::new(move |err| {
        sender.send(err).unwrap();
    });

    let (command_handle, callback) = CallbacksHelpers::closure_to_create_pool_ledger_cb(cb);

    let pool_name = CString::new("test_open").unwrap();
    let pool_config = CString::new("{\"genesis_txn\": \"./test_open_src.txn\"}").unwrap();
    std::fs::File::create("./test_open_src.txn").unwrap();

    let err = sovrin_create_pool_ledger_config(command_handle,
                                               pool_name.as_ptr(),
                                               pool_config.as_ptr(),
                                               callback);

    assert_eq!(ErrorCode::Success, err);

    let err = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
    std::fs::remove_file("./test_open_src.txn").unwrap();
    std::fs::remove_dir_all(EnvironmentUtils::pool_path("test_open")).unwrap();
    assert_eq!(ErrorCode::Success, err);
}

#[test]
fn sovrin_open_pool_ledger_can_be_called() {
    let (sender, receiver) = channel();
    let sender2 = sender.clone();

    let cb = Box::new(move |err, _| {
        sender.send(err).unwrap();
    });
    let cb2 = Box::new(move |err, _| {
        sender2.send(err).unwrap();
    });

    let (command_handle, callback) = CallbacksHelpers::closure_to_open_pool_ledger_cb(cb);

    let pool_name: String = "test1".to_string();
    let c_pool_name = CString::new(pool_name.as_str()).unwrap();
    let mut path = environment::EnvironmentUtils::pool_path(pool_name.as_str());
    fs::create_dir_all(path.as_path()).unwrap();
    path.push(pool_name);
    path.set_extension("txn");
    fs::File::create(path.as_path()).unwrap();

    let err = sovrin_open_pool_ledger(command_handle,
                                      c_pool_name.as_ptr(),
                                      null(),
                                      callback);

    assert_eq!(ErrorCode::Success, err);
    let err = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
    assert_eq!(ErrorCode::Success, err);

    //TODO separate test to check error after open the same pool multiply times
    let (command_handle, callback) = CallbacksHelpers::closure_to_open_pool_ledger_cb(cb2);
    let err = sovrin_open_pool_ledger(command_handle,
                                      c_pool_name.as_ptr(),
                                      null(),
                                      callback);

    assert_eq!(ErrorCode::Success, err);
    let err = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
    assert_eq!(ErrorCode::PoolLedgerInvalidPoolHandle, err);
}

#[test]
#[ignore] //required nodes pool available from CI
fn sovrin_submit_request_can_be_called() {
    //TODO call create pool ledger before open
    let (sender, receiver) = channel();
    let cb = Box::new(move |err, handle| {
        sender.send((err, handle)).unwrap();
    });
    let (command_handle, callback) = CallbacksHelpers::closure_to_open_pool_ledger_cb(cb);
    let pool_name = CString::new("test").unwrap();
    let err = sovrin_open_pool_ledger(command_handle,
                                      pool_name.as_ptr(),
                                      null(),
                                      callback);
    assert_eq!(ErrorCode::Success, err);
    let (err, pool_handle) = receiver.recv_timeout(std::time::Duration::from_secs(1)).unwrap();
    assert_eq!(ErrorCode::Success, err);
    std::thread::sleep(std::time::Duration::from_secs(1)); //TODO

    let (sender, receiver) = channel();
    let cb_send = Box::new(move |err, resp| {
        sender.send((err, resp)).unwrap();
    });
    let json = "{\
            \"reqId\":1491566332010860,\
            \"identifier\":\"Th7MpTaRZVRYnPiabds81Y\",\
            \"operation\":{\
                \"type\":\"105\",\
                \"dest\":\"FYmoFw55GeQH7SRFa37dkx1d2dZ3zUF8ckg7wmL7ofN4\",\
            },\
            \"signature\":\"4o86XfkiJ4e2r3J6Ufoi17UU3W5Zi9sshV6FjBjkVw4sgEQFQov9dxqDEtLbAJAWffCWd5KfAk164QVo7mYwKkiV\"\
        }\
        ";
    let req = CString::new(json).unwrap();
    let (command_handle, callback) = CallbacksHelpers::closure_to_send_tx_cb(cb_send);

    let err = sovrin_submit_request(command_handle,
                                    pool_handle,
                                    req.as_ptr(),
                                    callback);

    assert_eq!(ErrorCode::Success, err);
    let recv = receiver.recv_timeout(std::time::Duration::from_secs(1));
    recv.unwrap(); //TODO check data in ok
}
<commit_msg>Update integration test sovrin_submit_request_can_be_called.<commit_after>extern crate sovrin;

#[macro_use]
extern crate serde_derive;
extern crate serde_json;
#[macro_use]
extern crate lazy_static;

#[path = "utils/mod.rs"]
mod utils;

use sovrin::api::ErrorCode;
use sovrin::api::pool::{sovrin_create_pool_ledger_config, sovrin_open_pool_ledger};
use sovrin::api::ledger::sovrin_submit_request;

#[path = "../src/utils/environment.rs"]
mod environment;

use environment::EnvironmentUtils;

use std::fs;

use std::ptr::null;

use std::time::Duration;

use utils::callbacks::CallbacksHelpers;

use std::sync::mpsc::{channel};
use std::ffi::{CString};
use std::io::Write;

fn _create_default_pool_transaction_file() {
    let mut f = fs::File::create("./pool_transactions_sandbox").unwrap();
    let data = format!("{}\n{}\n{}\n{}\n",
                       "{\"data\":{\"alias\":\"Node1\",\"client_ip\":\"10.0.0.2\",\"client_port\":9702,\"node_ip\":\"10.0.0.2\",\"node_port\":9701,\"services\":[\"VALIDATOR\"]},\"dest\":\"Gw6pDLhcBcoQesN72qfotTgFa7cbuqZpkX3Xo6pLhPhv\",\"identifier\":\"FYmoFw55GeQH7SRFa37dkx1d2dZ3zUF8ckg7wmL7ofN4\",\"txnId\":\"fea82e10e894419fe2bea7d96296a6d46f50f93f9eeda954ec461b2ed2950b62\",\"type\":\"0\"}",
                       "{\"data\":{\"alias\":\"Node2\",\"client_ip\":\"10.0.0.3\",\"client_port\":9704,\"node_ip\":\"10.0.0.3\",\"node_port\":9703,\"services\":[\"VALIDATOR\"]},\"dest\":\"8ECVSk179mjsjKRLWiQtssMLgp6EPhWXtaYyStWPSGAb\",\"identifier\":\"8QhFxKxyaFsJy4CyxeYX34dFH8oWqyBv1P4HLQCsoeLy\",\"txnId\":\"1ac8aece2a18ced660fef8694b61aac3af08ba875ce3026a160acbc3a3af35fc\",\"type\":\"0\"}",
                       "{\"data\":{\"alias\":\"Node3\",\"client_ip\":\"10.0.0.4\",\"client_port\":9706,\"node_ip\":\"10.0.0.4\",\"node_port\":9705,\"services\":[\"VALIDATOR\"]},\"dest\":\"DKVxG2fXXTU8yT5N7hGEbXB3dfdAnYv1JczDUHpmDxya\",\"identifier\":\"2yAeV5ftuasWNgQwVYzeHeTuM7LwwNtPR3Zg9N4JiDgF\",\"txnId\":\"7e9f355dffa78ed24668f0e0e369fd8c224076571c51e2ea8be5f26479edebe4\",\"type\":\"0\"}",
                       "{\"data\":{\"alias\":\"Node4\",\"client_ip\":\"10.0.0.5\",\"client_port\":9708,\"node_ip\":\"10.0.0.5\",\"node_port\":9707,\"services\":[\"VALIDATOR\"]},\"dest\":\"4PS3EDQ3dW1tci1Bp6543CfuuebjFrg36kLAUcskGfaA\",\"identifier\":\"FTE95CVthRtrBnK2PYCBbC9LghTcGwi9Zfi1Gz2dnyNx\",\"txnId\":\"aa5e817d7cc626170eca175822029339a444eb0ee8f0bd20d3b0b76e566fb008\",\"type\":\"0\"}");
    f.write_all(data.as_bytes()).unwrap();
    f.flush().unwrap();
}

#[test]
fn sovrin_create_pool_ledger_can_be_called() {
    let (sender, receiver) = channel();

    let cb = Box::new(move |err| {
        sender.send(err).unwrap();
    });

    let (command_handle, callback) = CallbacksHelpers::closure_to_create_pool_ledger_cb(cb);

    let pool_name = CString::new("test_open").unwrap();
    let pool_config = CString::new("{\"genesis_txn\": \"./pool_transactions_sandbox\"}").unwrap();
    _create_default_pool_transaction_file();

    let err = sovrin_create_pool_ledger_config(command_handle,
                                               pool_name.as_ptr(),
                                               pool_config.as_ptr(),
                                               callback);

    assert_eq!(ErrorCode::Success, err);

    let err = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
    std::fs::remove_file("./test_open_src.txn").unwrap();
    std::fs::remove_dir_all(EnvironmentUtils::pool_path("test_open")).unwrap();
    assert_eq!(ErrorCode::Success, err);
}

#[test]
fn sovrin_open_pool_ledger_can_be_called() {
    let (sender, receiver) = channel();
    let sender2 = sender.clone();

    let cb = Box::new(move |err, _| {
        sender.send(err).unwrap();
    });
    let cb2 = Box::new(move |err, _| {
        sender2.send(err).unwrap();
    });

    let (command_handle, callback) = CallbacksHelpers::closure_to_open_pool_ledger_cb(cb);

    let pool_name: String = "test1".to_string();
    let c_pool_name = CString::new(pool_name.as_str()).unwrap();
    let mut path = environment::EnvironmentUtils::pool_path(pool_name.as_str());
    fs::create_dir_all(path.as_path()).unwrap();
    path.push(pool_name);
    path.set_extension("txn");
    fs::File::create(path.as_path()).unwrap();

    let err = sovrin_open_pool_ledger(command_handle,
                                      c_pool_name.as_ptr(),
                                      null(),
                                      callback);

    assert_eq!(ErrorCode::Success, err);
    let err = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
    assert_eq!(ErrorCode::Success, err);

    //TODO separate test to check error after open the same pool multiply times
    let (command_handle, callback) = CallbacksHelpers::closure_to_open_pool_ledger_cb(cb2);
    let err = sovrin_open_pool_ledger(command_handle,
                                      c_pool_name.as_ptr(),
                                      null(),
                                      callback);

    assert_eq!(ErrorCode::Success, err);
    let err = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
    assert_eq!(ErrorCode::PoolLedgerInvalidPoolHandle, err);
}

#[test]
#[ignore] //required nodes pool available from CI
fn sovrin_submit_request_can_be_called() {
    std::fs::remove_dir_all(EnvironmentUtils::pool_path("test")).unwrap();
    //create pool
    let (sender, receiver) = channel();
    let cb = Box::new(move |err| {
        sender.send(err).unwrap();
    });
    let (command_handle, callback) = CallbacksHelpers::closure_to_create_pool_ledger_cb(cb);
    let pool_name = CString::new("test").unwrap();
    let pool_config = CString::new("{\"genesis_txn\": \"./pool_transactions_sandbox\"}").unwrap();
    _create_default_pool_transaction_file();
    let err = sovrin_create_pool_ledger_config(command_handle,
                                               pool_name.as_ptr(),
                                               pool_config.as_ptr(),
                                               callback);
    assert_eq!(ErrorCode::Success, err);
    let err = receiver.recv_timeout(Duration::from_secs(1)).unwrap();
    assert_eq!(ErrorCode::Success, err);
    //open pool
    let (sender, receiver) = channel();
    let cb = Box::new(move |err, handle| {
        sender.send((err, handle)).unwrap();
    });
    let (command_handle, callback) = CallbacksHelpers::closure_to_open_pool_ledger_cb(cb);
    let pool_name = CString::new("test").unwrap();
    let err = sovrin_open_pool_ledger(command_handle,
                                      pool_name.as_ptr(),
                                      null(),
                                      callback);
    assert_eq!(ErrorCode::Success, err);
    let (err, pool_handle) = receiver.recv_timeout(std::time::Duration::from_secs(1)).unwrap();
    assert_eq!(ErrorCode::Success, err);
    std::thread::sleep(std::time::Duration::from_secs(1)); //TODO
    //test communication
    let (sender, receiver) = channel();
    let cb_send = Box::new(move |err, resp| {
        sender.send((err, resp)).unwrap();
    });
    let json = "{\
            \"reqId\":1491566332010860,\
            \"identifier\":\"Th7MpTaRZVRYnPiabds81Y\",\
            \"operation\":{\
                \"type\":\"105\",\
                \"dest\":\"FYmoFw55GeQH7SRFa37dkx1d2dZ3zUF8ckg7wmL7ofN4\"\
            },\
            \"signature\":\"4o86XfkiJ4e2r3J6Ufoi17UU3W5Zi9sshV6FjBjkVw4sgEQFQov9dxqDEtLbAJAWffCWd5KfAk164QVo7mYwKkiV\"\
        }\
        ";
    let req = CString::new(json).unwrap();
    let (command_handle, callback) = CallbacksHelpers::closure_to_send_tx_cb(cb_send);

    let err = sovrin_submit_request(command_handle,
                                    pool_handle,
                                    req.as_ptr(),
                                    callback);

    assert_eq!(ErrorCode::Success, err);
    let recv = receiver.recv_timeout(std::time::Duration::from_secs(1));

    #[derive(Deserialize, Eq, PartialEq, Debug)]
    struct Reply {
        op: String,
        result: ReplyResult,
    }
    #[derive(Deserialize, Eq, PartialEq, Debug)]
    #[serde(rename_all = "camelCase")]
    struct ReplyResult {
        txn_id: String,
        req_id: u64,
    }
    let exp_reply = Reply {
        op: "REPLY".to_string(),
        result: ReplyResult {
            req_id: 1491566332010860,
            txn_id: "5511e5493c1d37dfa67b73269a392a7aca5b71e9d10ac106adc7f9e552aee560".to_string(),
        }
    };
    let act_reply: Reply = serde_json::from_str(recv.unwrap().1.as_str()).unwrap();
    assert_eq!(act_reply, exp_reply);
    //    std::fs::remove_dir_all(EnvironmentUtils::pool_path("test")).unwrap();
}
<|endoftext|>


<commit_before>extern crate gluon_language_server;
extern crate debugserver_types;
extern crate languageserver_types;

extern crate jsonrpc_core;
extern crate serde_json;
extern crate serde;

#[macro_use]
extern crate lazy_static;

#[allow(dead_code)]
mod support;

use std::net::TcpStream;
use std::path::PathBuf;
use std::process::Command;
use std::io::BufReader;
use std::sync::Mutex;

use serde_json::{Value, from_str};

use debugserver_types::*;

use gluon_language_server::rpc::read_message;

macro_rules! request {
    ($stream: expr, $id: ident, $command: expr, $seq: expr, $expr: expr) => {
        let request = $id {
            arguments: $expr,
            command: $command.to_string(),
            seq: { $seq += 1; $seq },
            type_: "request".into(),
        };
        support::write_message($stream, request).unwrap();
    }
}

lazy_static! {
    static ref PORT: Mutex<i32> = Mutex::new(4711);
}

fn run_debugger<F>(f: F)
    where F: FnOnce(&mut i64, &TcpStream, &mut BufReader<&TcpStream>),
{
    let port = {
        let mut port = PORT.lock().unwrap();
        *port += 1;
        *port
    };
    let path = PathBuf::from(::std::env::var("OUT_DIR").unwrap());
    let debugger = path.parent()
        .and_then(|path| path.parent())
        .and_then(|path| path.parent())
        .expect("debugger executable")
        .join("debugger");

    let mut child = Command::new(&debugger)
        .arg(port.to_string())
        .spawn()
        .unwrap_or_else(|_| panic!("Expected exe: {}", debugger.display()));

    let stream = TcpStream::connect(&format!("localhost:{}", port)[..]).unwrap();

    let mut seq = 0;
    let mut read = BufReader::new(&stream);

    request! {
        &stream,
        InitializeRequest,
        "initialize",
        seq,
        InitializeRequestArguments {
            adapter_id: "".into(),
            columns_start_at_1: None,
            lines_start_at_1: None,
            path_format: None,
            supports_run_in_terminal_request: None,
            supports_variable_paging: None,
            supports_variable_type: None,
        }
    };

    let value = read_message(&mut read).unwrap().unwrap();
    let _: InitializedEvent = from_str(&value).unwrap();

    let value = read_message(&mut read).unwrap().unwrap();
    let initialize_response: InitializeResponse = from_str(&value).unwrap();
    assert!(initialize_response.success);

    f(&mut seq, &stream, &mut read);

    request! {
        &stream,
        DisconnectRequest,
        "disconnect",
        seq,
        None
    };

    child.wait().unwrap();
}

#[test]
fn launch() {
    run_debugger(|seq, stream, mut read| {
        request! {
            stream,
            Request,
            "launch",
            *seq,
            Some(Value::Object(vec![("program".to_string(),
                                     Value::String("tests/main.glu".to_string()))]
                                    .into_iter()
                                    .collect()))
        };

        let value = read_message(&mut read).unwrap().unwrap();
        let launch_response: LaunchResponse = from_str(&value).unwrap();
        assert_eq!(launch_response.request_seq, *seq);
        assert!(launch_response.success);

        request! {
            stream,
            ConfigurationDoneRequest,
            "configurationDone",
            *seq,
            None
        };
        let value = read_message(&mut read).unwrap().unwrap();
        let _: ConfigurationDoneResponse = from_str(&value).unwrap();

        let value = read_message(&mut read).unwrap().unwrap();
        let _: TerminatedEvent = from_str(&value).unwrap();
    });
}

#[test]
fn infinite_loops_are_terminated() {
    run_debugger(|seq, stream, mut read| {
        request! {
            stream,
            Request,
            "launch",
            *seq,
            Some(Value::Object(vec![("program".to_string(),
                                     Value::String("tests/infinite_loop.glu".to_string()))]
                                    .into_iter()
                                    .collect()))
        };

        let value = read_message(&mut read).unwrap().unwrap();
        let launch_response: LaunchResponse = from_str(&value).unwrap();
        assert_eq!(launch_response.request_seq, *seq);
        assert!(launch_response.success);

        request! {
            stream,
            ConfigurationDoneRequest,
            "configurationDone",
            *seq,
            None
        };
        let value = read_message(&mut read).unwrap().unwrap();
        let _: ConfigurationDoneResponse = from_str(&value).unwrap();
    });
}
<commit_msg>refactor: De-duplicate some patterns in the debugger tests<commit_after>extern crate gluon_language_server;
extern crate debugserver_types;
extern crate languageserver_types;

extern crate jsonrpc_core;
extern crate serde_json;
extern crate serde;

#[macro_use]
extern crate lazy_static;

#[allow(dead_code)]
mod support;

use std::net::TcpStream;
use std::path::PathBuf;
use std::process::Command;
use std::io::{BufRead, BufReader, Write};
use std::sync::Mutex;

use serde_json::{Value, from_str};

use debugserver_types::*;

use gluon_language_server::rpc::read_message;

macro_rules! request {
    ($stream: expr, $id: ident, $command: expr, $seq: expr, $expr: expr) => {
        let request = $id {
            arguments: $expr,
            command: $command.to_string(),
            seq: { $seq += 1; $seq },
            type_: "request".into(),
        };
        support::write_message($stream, request).unwrap();
    }
}

lazy_static! {
    static ref PORT: Mutex<i32> = Mutex::new(4711);
}

fn run_debugger<F>(f: F)
    where F: FnOnce(&mut i64, &TcpStream, &mut BufReader<&TcpStream>),
{
    let port = {
        let mut port = PORT.lock().unwrap();
        *port += 1;
        *port
    };
    let path = PathBuf::from(::std::env::var("OUT_DIR").unwrap());
    let debugger = path.parent()
        .and_then(|path| path.parent())
        .and_then(|path| path.parent())
        .expect("debugger executable")
        .join("debugger");

    let mut child = Command::new(&debugger)
        .arg(port.to_string())
        .spawn()
        .unwrap_or_else(|_| panic!("Expected exe: {}", debugger.display()));

    let stream = TcpStream::connect(&format!("localhost:{}", port)[..]).unwrap();

    let mut seq = 0;
    let mut read = BufReader::new(&stream);

    request! {
        &stream,
        InitializeRequest,
        "initialize",
        seq,
        InitializeRequestArguments {
            adapter_id: "".into(),
            columns_start_at_1: None,
            lines_start_at_1: None,
            path_format: None,
            supports_run_in_terminal_request: None,
            supports_variable_paging: None,
            supports_variable_type: None,
        }
    };

    let _: InitializedEvent = expect_message(&mut read);

    let initialize_response: InitializeResponse = expect_message(&mut read);
    assert!(initialize_response.success);

    f(&mut seq, &stream, &mut read);

    request! {
        &stream,
        DisconnectRequest,
        "disconnect",
        seq,
        None
    };

    child.wait().unwrap();
}

fn launch<W>(stream: W, seq: &mut i64, program: &str)
    where W: Write,
{
    request! {
        stream,
        Request,
        "launch",
        *seq,
        Some(Value::Object(vec![("program".to_string(),
                                    Value::String(program.to_string()))]
                                .into_iter()
                                .collect()))
    };
}

fn expect_message<M, R>(read: R) -> M
    where M: serde::Deserialize,
          R: BufRead,
{
    let value = read_message(read).unwrap().unwrap();
    from_str(&value).unwrap()
}

#[test]
fn launch_program() {
    run_debugger(|seq, stream, mut read| {
        launch(stream, seq, "tests/main.glu");

        let launch_response: LaunchResponse = expect_message(&mut read);
        assert_eq!(launch_response.request_seq, *seq);
        assert!(launch_response.success);

        request! {
            stream,
            ConfigurationDoneRequest,
            "configurationDone",
            *seq,
            None
        };
        let _: ConfigurationDoneResponse = expect_message(&mut read);

        let _: TerminatedEvent = expect_message(&mut read);
    });
}

#[test]
fn infinite_loops_are_terminated() {
    run_debugger(|seq, stream, mut read| {
        launch(stream, seq, "tests/infinite_loop.glu");

        let launch_response: LaunchResponse = expect_message(&mut read);
        assert_eq!(launch_response.request_seq, *seq);
        assert!(launch_response.success);

        request! {
            stream,
            ConfigurationDoneRequest,
            "configurationDone",
            *seq,
            None
        };
        let _: ConfigurationDoneResponse = expect_message(&mut read);
    });
}
<|endoftext|>


<commit_before>#[cfg(feature = "tonic")]
use crate::proto::resource::v1::Resource;

#[cfg(feature = "tonic")]
use crate::proto::trace::v1::{
    span, status, InstrumentationLibrarySpans, ResourceSpans, Span, Status,
};

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use crate::proto::grpcio::resource::Resource;

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use crate::proto::grpcio::trace::{
    InstrumentationLibrarySpans, ResourceSpans, Span, Span_Event, Span_Link, Span_SpanKind, Status,
    Status_StatusCode,
};

use crate::transform::common::{to_nanos, Attributes};
use opentelemetry::sdk::{export::trace::SpanData, self};
use opentelemetry::trace::{Link, SpanKind, StatusCode};

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use protobuf::reflect::ProtobufValue;

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use protobuf::{RepeatedField, SingularPtrField};

#[cfg(feature = "tonic")]
impl From<SpanKind> for span::SpanKind {
    fn from(span_kind: SpanKind) -> Self {
        match span_kind {
            SpanKind::Client => span::SpanKind::Client,
            SpanKind::Consumer => span::SpanKind::Consumer,
            SpanKind::Internal => span::SpanKind::Internal,
            SpanKind::Producer => span::SpanKind::Producer,
            SpanKind::Server => span::SpanKind::Server,
        }
    }
}

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
impl From<SpanKind> for Span_SpanKind {
    fn from(span_kind: SpanKind) -> Self {
        match span_kind {
            SpanKind::Client => Span_SpanKind::SPAN_KIND_CLIENT,
            SpanKind::Consumer => Span_SpanKind::SPAN_KIND_CONSUMER,
            SpanKind::Internal => Span_SpanKind::SPAN_KIND_INTERNAL,
            SpanKind::Producer => Span_SpanKind::SPAN_KIND_PRODUCER,
            SpanKind::Server => Span_SpanKind::SPAN_KIND_SERVER,
        }
    }
}

#[cfg(feature = "tonic")]
impl From<StatusCode> for status::StatusCode {
    fn from(status_code: StatusCode) -> Self {
        match status_code {
            StatusCode::Ok => status::StatusCode::Ok,
            StatusCode::Unset => status::StatusCode::Unset,
            StatusCode::Error => status::StatusCode::Error,
        }
    }
}

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
impl From<StatusCode> for Status_StatusCode {
    fn from(status_code: StatusCode) -> Self {
        match status_code {
            StatusCode::Ok => Status_StatusCode::STATUS_CODE_OK,
            StatusCode::Unset => Status_StatusCode::STATUS_CODE_UNSET,
            StatusCode::Error => Status_StatusCode::STATUS_CODE_ERROR,
        }
    }
}

#[cfg(feature = "tonic")]
impl From<Link> for span::Link {
    fn from(link: Link) -> Self {
        span::Link {
            trace_id: link
                .span_context()
                .trace_id()
                .to_u128()
                .to_be_bytes()
                .to_vec(),
            span_id: link
                .span_context()
                .span_id()
                .to_u64()
                .to_be_bytes()
                .to_vec(),
            trace_state: link.span_context().trace_state().header(),
            attributes: Attributes::from(link.attributes().clone()).0,
            dropped_attributes_count: 0,
        }
    }
}

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
impl From<Link> for Span_Link {
    fn from(link: Link) -> Self {
        Span_Link {
            trace_id: link
                .span_context()
                .trace_id()
                .to_u128()
                .to_be_bytes()
                .to_vec(),
            span_id: link
                .span_context()
                .span_id()
                .to_u64()
                .to_be_bytes()
                .to_vec(),
            trace_state: link.span_context().trace_state().header(),
            attributes: Attributes::from(link.attributes().clone()).0,
            dropped_attributes_count: 0,
            ..Default::default()
        }
    }
}

fn resource_attributes(resource: &sdk::Resource) -> Attributes {
    resource
        .iter()
        .map(|(k, v)| opentelemetry::KeyValue::new(k.clone(), v.clone()))
        .collect::<Vec<_>>()
        .into()
}

impl From<SpanData> for ResourceSpans {
    #[cfg(feature = "tonic")]
    fn from(source_span: SpanData) -> Self {
        let span_kind: span::SpanKind = source_span.span_kind.into();
        ResourceSpans {
            resource: Some(Resource {
                attributes: resource_attributes(&source_span.resource).0,
                dropped_attributes_count: 0,
            }),
            instrumentation_library_spans: vec![InstrumentationLibrarySpans {
                instrumentation_library: Default::default(),
                spans: vec![Span {
                    trace_id: source_span
                        .span_context
                        .trace_id()
                        .to_u128()
                        .to_be_bytes()
                        .to_vec(),
                    span_id: source_span
                        .span_context
                        .span_id()
                        .to_u64()
                        .to_be_bytes()
                        .to_vec(),
                    trace_state: source_span.span_context.trace_state().header(),
                    parent_span_id: {
                        if source_span.parent_span_id.to_u64() > 0 {
                            source_span.parent_span_id.to_u64().to_be_bytes().to_vec()
                        } else {
                            vec![]
                        }
                    },
                    name: source_span.name,
                    kind: span_kind as i32,
                    start_time_unix_nano: to_nanos(source_span.start_time),
                    end_time_unix_nano: to_nanos(source_span.end_time),
                    dropped_attributes_count: source_span.attributes.dropped_count(),
                    attributes: Attributes::from(source_span.attributes).0,
                    dropped_events_count: source_span.message_events.dropped_count(),
                    events: source_span
                        .message_events
                        .into_iter()
                        .map(|event| span::Event {
                            time_unix_nano: to_nanos(event.timestamp),
                            name: event.name,
                            attributes: Attributes::from(event.attributes).0,
                            dropped_attributes_count: 0,
                        })
                        .collect(),
                    dropped_links_count: source_span.links.dropped_count(),
                    links: source_span.links.into_iter().map(Into::into).collect(),
                    status: Some(Status {
                        code: status::StatusCode::from(source_span.status_code).into(),
                        message: source_span.status_message,
                        ..Default::default()
                    }),
                }],
            }],
        }
    }

    #[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
    fn from(source_span: SpanData) -> Self {
        ResourceSpans {
            resource: SingularPtrField::from(Some(Resource {
                attributes: resource_attributes(&source_span.resource).0,
                dropped_attributes_count: 0,
                ..Default::default()
            })),
            instrumentation_library_spans: RepeatedField::from_vec(vec![
                InstrumentationLibrarySpans {
                    instrumentation_library: Default::default(),
                    spans: RepeatedField::from_vec(vec![Span {
                        trace_id: source_span
                            .span_context
                            .trace_id()
                            .to_u128()
                            .to_be_bytes()
                            .to_vec(),
                        span_id: source_span
                            .span_context
                            .span_id()
                            .to_u64()
                            .to_be_bytes()
                            .to_vec(),
                        trace_state: source_span.span_context.trace_state().header(),
                        parent_span_id: {
                            if source_span.parent_span_id.to_u64().is_non_zero() {
                                source_span.parent_span_id.to_u64().to_be_bytes().to_vec()
                            } else {
                                vec![]
                            }
                        },
                        name: source_span.name,
                        kind: source_span.span_kind.into(),
                        start_time_unix_nano: to_nanos(source_span.start_time),
                        end_time_unix_nano: to_nanos(source_span.end_time),
                        dropped_attributes_count: source_span.attributes.dropped_count(),
                        attributes: Attributes::from(source_span.attributes).0,
                        dropped_events_count: source_span.message_events.dropped_count(),
                        events: RepeatedField::from_vec(
                            source_span
                                .message_events
                                .into_iter()
                                .map(|event| Span_Event {
                                    time_unix_nano: to_nanos(event.timestamp),
                                    name: event.name,
                                    attributes: Attributes::from(event.attributes).0,
                                    dropped_attributes_count: 0,
                                    ..Default::default()
                                })
                                .collect(),
                        ),
                        dropped_links_count: source_span.links.dropped_count(),
                        links: RepeatedField::from_vec(
                            source_span.links.into_iter().map(Into::into).collect(),
                        ),
                        status: SingularPtrField::some(Status {
                            code: Status_StatusCode::from(source_span.status_code),
                            message: source_span.status_message,
                            ..Default::default()
                        }),
                        ..Default::default()
                    }]),
                    ..Default::default()
                },
            ]),
            ..Default::default()
        }
    }
}
<commit_msg>Propagate Resource from Span to proto (Tonic) (#386) (#391)<commit_after>#[cfg(feature = "tonic")]
use crate::proto::resource::v1::Resource;

#[cfg(feature = "tonic")]
use crate::proto::trace::v1::{
    span, status, InstrumentationLibrarySpans, ResourceSpans, Span, Status,
};

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use crate::proto::grpcio::resource::Resource;

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use crate::proto::grpcio::trace::{
    InstrumentationLibrarySpans, ResourceSpans, Span, Span_Event, Span_Link, Span_SpanKind, Status,
    Status_StatusCode,
};

use crate::transform::common::{to_nanos, Attributes};
use opentelemetry::sdk::{self, export::trace::SpanData};
use opentelemetry::trace::{Link, SpanKind, StatusCode};

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use protobuf::reflect::ProtobufValue;

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
use protobuf::{RepeatedField, SingularPtrField};

#[cfg(feature = "tonic")]
impl From<SpanKind> for span::SpanKind {
    fn from(span_kind: SpanKind) -> Self {
        match span_kind {
            SpanKind::Client => span::SpanKind::Client,
            SpanKind::Consumer => span::SpanKind::Consumer,
            SpanKind::Internal => span::SpanKind::Internal,
            SpanKind::Producer => span::SpanKind::Producer,
            SpanKind::Server => span::SpanKind::Server,
        }
    }
}

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
impl From<SpanKind> for Span_SpanKind {
    fn from(span_kind: SpanKind) -> Self {
        match span_kind {
            SpanKind::Client => Span_SpanKind::SPAN_KIND_CLIENT,
            SpanKind::Consumer => Span_SpanKind::SPAN_KIND_CONSUMER,
            SpanKind::Internal => Span_SpanKind::SPAN_KIND_INTERNAL,
            SpanKind::Producer => Span_SpanKind::SPAN_KIND_PRODUCER,
            SpanKind::Server => Span_SpanKind::SPAN_KIND_SERVER,
        }
    }
}

#[cfg(feature = "tonic")]
impl From<StatusCode> for status::StatusCode {
    fn from(status_code: StatusCode) -> Self {
        match status_code {
            StatusCode::Ok => status::StatusCode::Ok,
            StatusCode::Unset => status::StatusCode::Unset,
            StatusCode::Error => status::StatusCode::Error,
        }
    }
}

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
impl From<StatusCode> for Status_StatusCode {
    fn from(status_code: StatusCode) -> Self {
        match status_code {
            StatusCode::Ok => Status_StatusCode::STATUS_CODE_OK,
            StatusCode::Unset => Status_StatusCode::STATUS_CODE_UNSET,
            StatusCode::Error => Status_StatusCode::STATUS_CODE_ERROR,
        }
    }
}

#[cfg(feature = "tonic")]
impl From<Link> for span::Link {
    fn from(link: Link) -> Self {
        span::Link {
            trace_id: link
                .span_context()
                .trace_id()
                .to_u128()
                .to_be_bytes()
                .to_vec(),
            span_id: link
                .span_context()
                .span_id()
                .to_u64()
                .to_be_bytes()
                .to_vec(),
            trace_state: link.span_context().trace_state().header(),
            attributes: Attributes::from(link.attributes().clone()).0,
            dropped_attributes_count: 0,
        }
    }
}

#[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
impl From<Link> for Span_Link {
    fn from(link: Link) -> Self {
        Span_Link {
            trace_id: link
                .span_context()
                .trace_id()
                .to_u128()
                .to_be_bytes()
                .to_vec(),
            span_id: link
                .span_context()
                .span_id()
                .to_u64()
                .to_be_bytes()
                .to_vec(),
            trace_state: link.span_context().trace_state().header(),
            attributes: Attributes::from(link.attributes().clone()).0,
            dropped_attributes_count: 0,
            ..Default::default()
        }
    }
}

fn resource_attributes(resource: &sdk::Resource) -> Attributes {
    resource
        .iter()
        .map(|(k, v)| opentelemetry::KeyValue::new(k.clone(), v.clone()))
        .collect::<Vec<_>>()
        .into()
}

impl From<SpanData> for ResourceSpans {
    #[cfg(feature = "tonic")]
    fn from(source_span: SpanData) -> Self {
        let span_kind: span::SpanKind = source_span.span_kind.into();
        ResourceSpans {
            resource: Some(Resource {
                attributes: resource_attributes(&source_span.resource).0,
                dropped_attributes_count: 0,
            }),
            instrumentation_library_spans: vec![InstrumentationLibrarySpans {
                instrumentation_library: Default::default(),
                spans: vec![Span {
                    trace_id: source_span
                        .span_context
                        .trace_id()
                        .to_u128()
                        .to_be_bytes()
                        .to_vec(),
                    span_id: source_span
                        .span_context
                        .span_id()
                        .to_u64()
                        .to_be_bytes()
                        .to_vec(),
                    trace_state: source_span.span_context.trace_state().header(),
                    parent_span_id: {
                        if source_span.parent_span_id.to_u64() > 0 {
                            source_span.parent_span_id.to_u64().to_be_bytes().to_vec()
                        } else {
                            vec![]
                        }
                    },
                    name: source_span.name,
                    kind: span_kind as i32,
                    start_time_unix_nano: to_nanos(source_span.start_time),
                    end_time_unix_nano: to_nanos(source_span.end_time),
                    dropped_attributes_count: source_span.attributes.dropped_count(),
                    attributes: Attributes::from(source_span.attributes).0,
                    dropped_events_count: source_span.message_events.dropped_count(),
                    events: source_span
                        .message_events
                        .into_iter()
                        .map(|event| span::Event {
                            time_unix_nano: to_nanos(event.timestamp),
                            name: event.name,
                            attributes: Attributes::from(event.attributes).0,
                            dropped_attributes_count: 0,
                        })
                        .collect(),
                    dropped_links_count: source_span.links.dropped_count(),
                    links: source_span.links.into_iter().map(Into::into).collect(),
                    status: Some(Status {
                        code: status::StatusCode::from(source_span.status_code).into(),
                        message: source_span.status_message,
                        ..Default::default()
                    }),
                }],
            }],
        }
    }

    #[cfg(all(feature = "grpc-sys", not(feature = "tonic")))]
    fn from(source_span: SpanData) -> Self {
        ResourceSpans {
            resource: SingularPtrField::from(Some(Resource {
                attributes: resource_attributes(&source_span.resource).0,
                dropped_attributes_count: 0,
                ..Default::default()
            })),
            instrumentation_library_spans: RepeatedField::from_vec(vec![
                InstrumentationLibrarySpans {
                    instrumentation_library: Default::default(),
                    spans: RepeatedField::from_vec(vec![Span {
                        trace_id: source_span
                            .span_context
                            .trace_id()
                            .to_u128()
                            .to_be_bytes()
                            .to_vec(),
                        span_id: source_span
                            .span_context
                            .span_id()
                            .to_u64()
                            .to_be_bytes()
                            .to_vec(),
                        trace_state: source_span.span_context.trace_state().header(),
                        parent_span_id: {
                            if source_span.parent_span_id.to_u64().is_non_zero() {
                                source_span.parent_span_id.to_u64().to_be_bytes().to_vec()
                            } else {
                                vec![]
                            }
                        },
                        name: source_span.name,
                        kind: source_span.span_kind.into(),
                        start_time_unix_nano: to_nanos(source_span.start_time),
                        end_time_unix_nano: to_nanos(source_span.end_time),
                        dropped_attributes_count: source_span.attributes.dropped_count(),
                        attributes: Attributes::from(source_span.attributes).0,
                        dropped_events_count: source_span.message_events.dropped_count(),
                        events: RepeatedField::from_vec(
                            source_span
                                .message_events
                                .into_iter()
                                .map(|event| Span_Event {
                                    time_unix_nano: to_nanos(event.timestamp),
                                    name: event.name,
                                    attributes: Attributes::from(event.attributes).0,
                                    dropped_attributes_count: 0,
                                    ..Default::default()
                                })
                                .collect(),
                        ),
                        dropped_links_count: source_span.links.dropped_count(),
                        links: RepeatedField::from_vec(
                            source_span.links.into_iter().map(Into::into).collect(),
                        ),
                        status: SingularPtrField::some(Status {
                            code: Status_StatusCode::from(source_span.status_code),
                            message: source_span.status_message,
                            ..Default::default()
                        }),
                        ..Default::default()
                    }]),
                    ..Default::default()
                },
            ]),
            ..Default::default()
        }
    }
}
<|endoftext|>


<commit_before>use asn1;

use std::cmp::Ordering;
use std::io;

pub trait StreamDecodee {
  /// This function is called when an ASN.1 tag is encountered. In other
  /// words, at the start of an ASN.1 element.
  fn start_element(&mut self, tag: asn1::Tag) -> ParseResult {
    ParseResult::Ok
  }

  /// This function is called when an ASN.1 element has finished decoding.
  /// Specifically, this is called for both constructed, and un-constructed
  /// elements.
  fn end_element(&mut self) -> ParseResult {
    ParseResult::Ok
  }

  // FIXME: Currently it's the function's responsibility to decode the element
  // with the correct amounts of bytes. Without heap allocation, this might be
  // the only way.
  /// This is called when a primitve element is encountered. The start_element
  /// function is always called before this.
  fn primitive<I: Iterator<Item=io::Result<u8>>>(&mut self, reader: &mut asn1::ByteReader<I>,
      len: asn1::LenNum) -> ParseResult {
    for _ in 0..len {
      match reader.read() {
        Err(e) => return e.into(),
        _ => {},
      }
    }
    ParseResult::Ok
  }

  fn warning(err: asn1::DecodeError) -> ParseResult {
    ParseResult::Stop
  }

  /// This is called when a fatal decoding error occurs.
  fn error(err: asn1::DecodeError) {
  }
}

/// A decoder that calls into an object implementing the StreamDecodee
/// trait.
pub struct StreamDecoder<I: Iterator<Item=io::Result<u8>>, S: StreamDecodee> {
  /// Internal reader with an included byte counter.
  reader: asn1::ByteReader<I>,
  /// Object implementing StreamDecodee trait, called into during decoding.
  decodee: S,
}

impl<I: Iterator<Item=io::Result<u8>>, S: StreamDecodee> StreamDecoder<I, S> {
  pub fn new<T: Into<asn1::ByteReader<I>>>(reader: T, decodee: S) -> Self {
    StreamDecoder {
      reader: reader.into(),
      decodee: decodee,
    }
  }

  /// Decode an asn1 element.
  pub fn decode(&mut self) -> Result<(), asn1::DecodeError> {
    self._decode().and(Ok(()))
  }

  // FIXME: Convert explicit decoded_len to use diff of internal reader count.
  /// Internal decode function.
  fn _decode(&mut self) -> Result<asn1::Tag, asn1::DecodeError> {
    let pre_tag_count: asn1::LenNum = self.reader.count;

    // Decode tag.
    let tag = try!(asn1::Tag::decode_tag(&mut self.reader));
    let post_tag_count: asn1::LenNum  = self.reader.count;

    // Call the decodee start element callback;
    self.decodee.start_element(tag);

    // If this type is constructed, decode child element..
    if tag.constructed {
      // Loop over child elements.
      loop {
        let decoded_len = self.reader.count - post_tag_count;
        // Compare decoded length with length in tag.
        // Put this first to handle zero-length elements.
        match tag.len.partial_cmp(&decoded_len) {
          // Return an error when we've decoded too much.
          Some(Ordering::Less) => return Err(asn1::DecodeError::GreaterLen),
          // Finish loop when equal, we must be finished.
          Some(Ordering::Equal) => break,
          // Continue when we are still decoding.
          Some(Ordering::Greater) => {},
          // Continue when using indefinite length encoding.
          None => {},
        };

        // Decode each child element.
        let child_tag = try!(self._decode());

        // If applicable, identify end of indefinite length encoding.
        // When decoding indefinite length encoding, stop on '00 00'
        // tag.
        if child_tag.len == asn1::Len::Def(0) &&
           child_tag.class == asn1::Class::Universal &&
           child_tag.tagnum == 0 {
          break;
        }
      }
    // Otherwise decode primitive value.
    } else {
      let len_num = try!(match tag.len {
        asn1::Len::Def(l) => Ok(l),
        asn1::Len::Indef =>
          Err(asn1::DecodeError::PrimIndef),
      });

      // Call decodee primitive decode callback.
      self.decodee.primitive(&mut self.reader, len_num);
    }

    let post_decode_count = self.reader.count;

    // FIXME: If decoded length is larger than tag length, error here.

    // Call decodee end element callback.
    self.decodee.end_element();

    // Return decoded + tag_len, which is total decoded length.
    Ok(tag)
  }
}

// FIXME: This seems to have two mixed meanings, perhaps split it?
/// The result of a parsing function.
pub enum ParseResult {
  /// Everything went okay.
  Ok,
  /// Decoding should stop.
  Stop,
  /// Decoding should skip next element.
  Skip,
  /// An error occured decoding an element.
  DecodeError(asn1::DecodeError),
  /// An IO error occured.
  IO(io::Error),
}

impl From<asn1::DecodeError> for ParseResult {
  fn from(err: asn1::DecodeError) -> Self {
    ParseResult::DecodeError(err)
  }
}

impl From<io::Error> for ParseResult {
  fn from(err: io::Error) -> Self {
    ParseResult::IO(err)
  }
}
<commit_msg>Notes on TreeParser<commit_after>use asn1;

use std::cmp::Ordering;
use std::io;

pub trait StreamDecodee {
  /// This function is called when an ASN.1 tag is encountered. In other
  /// words, at the start of an ASN.1 element.
  fn start_element(&mut self, tag: asn1::Tag) -> ParseResult {
    ParseResult::Ok
  }

  /// This function is called when an ASN.1 element has finished decoding.
  /// Specifically, this is called for both constructed, and un-constructed
  /// elements.
  fn end_element(&mut self) -> ParseResult {
    ParseResult::Ok
  }

  // FIXME: Currently it's the function's responsibility to decode the element
  // with the correct amounts of bytes. Without heap allocation, this might be
  // the only way.
  /// This is called when a primitve element is encountered. The start_element
  /// function is always called before this.
  fn primitive<I: Iterator<Item=io::Result<u8>>>(&mut self, reader: &mut asn1::ByteReader<I>,
      len: asn1::LenNum) -> ParseResult {
    for _ in 0..len {
      match reader.read() {
        Err(e) => return e.into(),
        _ => {},
      }
    }
    ParseResult::Ok
  }

  fn warning(err: asn1::DecodeError) -> ParseResult {
    ParseResult::Stop
  }

  /// This is called when a fatal decoding error occurs.
  fn error(err: asn1::DecodeError) {
  }
}

/// A decoder that calls into an object implementing the StreamDecodee
/// trait.
pub struct StreamDecoder<I: Iterator<Item=io::Result<u8>>, S: StreamDecodee> {
  /// Internal reader with an included byte counter.
  reader: asn1::ByteReader<I>,
  /// Object implementing StreamDecodee trait, called into during decoding.
  decodee: S,
}

impl<I: Iterator<Item=io::Result<u8>>, S: StreamDecodee> StreamDecoder<I, S> {
  pub fn new<T: Into<asn1::ByteReader<I>>>(reader: T, decodee: S) -> Self {
    StreamDecoder {
      reader: reader.into(),
      decodee: decodee,
    }
  }

  /// Decode an asn1 element.
  pub fn decode(&mut self) -> Result<(), asn1::DecodeError> {
    self._decode().and(Ok(()))
  }

  // FIXME: Convert explicit decoded_len to use diff of internal reader count.
  /// Internal decode function.
  fn _decode(&mut self) -> Result<asn1::Tag, asn1::DecodeError> {
    let pre_tag_count: asn1::LenNum = self.reader.count;

    // Decode tag.
    let tag = try!(asn1::Tag::decode_tag(&mut self.reader));
    let post_tag_count: asn1::LenNum  = self.reader.count;

    // Call the decodee start element callback;
    self.decodee.start_element(tag);

    // If this type is constructed, decode child element..
    if tag.constructed {
      // Loop over child elements.
      loop {
        let decoded_len = self.reader.count - post_tag_count;
        // Compare decoded length with length in tag.
        // Put this first to handle zero-length elements.
        match tag.len.partial_cmp(&decoded_len) {
          // Return an error when we've decoded too much.
          Some(Ordering::Less) => return Err(asn1::DecodeError::GreaterLen),
          // Finish loop when equal, we must be finished.
          Some(Ordering::Equal) => break,
          // Continue when we are still decoding.
          Some(Ordering::Greater) => {},
          // Continue when using indefinite length encoding.
          None => {},
        };

        // Decode each child element.
        let child_tag = try!(self._decode());

        // If applicable, identify end of indefinite length encoding.
        // When decoding indefinite length encoding, stop on '00 00'
        // tag.
        if child_tag.len == asn1::Len::Def(0) &&
           child_tag.class == asn1::Class::Universal &&
           child_tag.tagnum == 0 {
          break;
        }
      }
    // Otherwise decode primitive value.
    } else {
      let len_num = try!(match tag.len {
        asn1::Len::Def(l) => Ok(l),
        asn1::Len::Indef =>
          Err(asn1::DecodeError::PrimIndef),
      });

      // Call decodee primitive decode callback.
      self.decodee.primitive(&mut self.reader, len_num);
    }

    let post_decode_count = self.reader.count;

    // FIXME: If decoded length is larger than tag length, error here.

    // Call decodee end element callback.
    self.decodee.end_element();

    // Return decoded + tag_len, which is total decoded length.
    Ok(tag)
  }
}

/// Ber decoder that allows a client to control the decoding process.
///
/// Clients are given
struct TreeParser {
}

enum TreeParserNode {
  Constructed,
  Primitive
}

// Some kind of api like this:
//
// Given either:
// // SKip this (decode in the background, to consume the bytes).
// fn next/skip() -> TreeParserNode
// // Skip all and go back up.
// fn up/finish() -> ()
//
//
// Given a Constructed, allow:
// // Go down a level
// fn into/deeper() -> TreeParserNode
//
// Given a Primitive, allow:
// // Decode this primitive, or return a limited iterator to consume this elemnt.
// fn decode/reader/slice()
// fn up/finish()
//
// This api should be stack-only. Unlike StreamDecoder, the user won't need to use the heap to store an element stack.
// This doesn't have any asn1 type info though, so maybe it will be part of a larger system (or perhaps not).

// FIXME: This seems to have two mixed meanings, perhaps split it?
/// The result of a parsing function.
pub enum ParseResult {
  /// Everything went okay.
  Ok,
  /// Decoding should stop.
  Stop,
  /// Decoding should skip next element.
  Skip,
  /// An error occured decoding an element.
  DecodeError(asn1::DecodeError),
  /// An IO error occured.
  IO(io::Error),
}

impl From<asn1::DecodeError> for ParseResult {
  fn from(err: asn1::DecodeError) -> Self {
    ParseResult::DecodeError(err)
  }
}

impl From<io::Error> for ParseResult {
  fn from(err: io::Error) -> Self {
    ParseResult::IO(err)
  }
}
<|endoftext|>


<commit_before>use card_strategy::CardStrategy;

use card::Card;
use card::Suit;
use card::Rank;
use game_status::GameStatus;
use player::PlayerName;

use std::collections::BTreeMap;
use std::collections::BTreeSet;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

/*
    Play 2 of clubs if in hand.
    If going to win a deal, do so with highest ranking card.
*/
impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus) -> (i32, i32, i32, i32, i32, i32, i32) {
        let potential_points = Self::potential_points(card, game_status);
        let definite_points = if Self::will_win_deal(card, game_status) {
            potential_points
        } else {
            0
        };
        let later_potential_points = 0 - Self::later_potential_points(card, game_status);
        let card_penalty_to_me = Self::card_penalty_to_me(card, game_status);
        let card_penalty = 0 - Self::card_penalty(card, game_status);
        let trouble = Self::trouble_score(card, game_status);
        let card_rank = 0 - (u32::from(card.rank) as i32);
        (definite_points, potential_points, later_potential_points, card_penalty_to_me, card_penalty, trouble, card_rank)
    }

    fn card_penalty(card: &Card, game_status: &GameStatus) -> i32 {
        game_status.round_parameters.card_points.get(card)
            .map(|penalty| *penalty)
            .unwrap_or_default()
    }

    fn card_penalty_to_me(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            Self::card_penalty(card, game_status)
        } else {
            0
        }
    }

    fn trouble_score(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            let trouble = u32::from(card.rank) as i32;
            if Self::card_penalty_to_me(card, game_status) < 0 {
                0 - trouble
            } else {
                if Self::will_win_deal(card, game_status) {
                    u32::from(Rank::Ace) as i32
                } else {
                    trouble
                }
            }
        } else {
            0
        }
    }

    fn can_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        game_status.my_in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }

    fn will_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        Self::can_win_deal(card, game_status) && (Self::plays_left(game_status).len() == 0 ||
            Self::deal_suit(game_status)
                .and_then(|suit| Self::remaining_cards(game_status).iter()
                    .filter(|card| card.suit == *suit)
                    .max()
                    .map(|winning_card| card.rank > winning_card.rank)).unwrap_or(true))
    }

    fn deal_suit(game_status: &GameStatus) -> Option<&Suit> {
        game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit.as_ref())
    }

    fn plays_left(game_status: &GameStatus) -> BTreeSet<&PlayerName> {
        let mut players: BTreeSet<&PlayerName> = game_status.my_game_players.iter()
            .filter(|player| !player.has_turn)
            .map(|player| &player.team_name)
            .collect();

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                players.remove(&deal_card.player_name);
            }
        }

        players
    }

    fn remaining_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        let mut cards = Card::all();

        for deal in &game_status.my_game_deals {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        for card in &game_status.my_current_hand {
            cards.remove(&card);
        }

        cards
    }

    fn dealt_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        game_status.my_in_progress_deal.as_ref()
            .map(|deal| deal.deal_cards.iter().map(|deal_card| deal_card.card).collect())
            .unwrap_or_default()
    }

    fn potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        // potential points for this deal should also have a chance modifier
        if Self::can_win_deal(card, game_status) {
            let mut cards = Self::remaining_cards(game_status);
            cards.extend(Self::dealt_cards(game_status));
            let cards = cards;

            let suit_points: i32 = cards.iter()
                .filter(|other| other.suit == card.suit)
                .filter(|other| other.rank < card.rank)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let other_points: i32 = cards.iter()
                .filter(|other| other.suit != card.suit)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            suit_points + ((Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32)
        } else {
            0
        }
    }

    fn later_potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        let cards = Self::remaining_cards(game_status);

        let suit_points: i32 = cards.iter()
            .filter(|other| other.suit == card.suit)
            .filter(|other| other.rank < card.rank)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        let other_points: i32 = cards.iter()
            .filter(|other| other.suit != card.suit)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        suit_points + ((Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32)
    }

    fn chance_of_later_win(card: &Card, game_status: &GameStatus) -> f32 {
        let cards = Self::remaining_cards(game_status);
        let suit_cards = cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
        let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
        if suit_cards.is_empty() {
            1.0
        } else {
            (will_win_count as f32) / (suit_cards.len() as f32)
        }
    }

}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        game_status.my_initial_hand.iter()
            .map(|card| ((0 - Self::later_potential_points(card, game_status), card), card))
            .collect::<BTreeMap<_, &Card>>()
            .into_iter()
            .map(|kv| kv.1)
            .take(3)
            .collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let current_suit = game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit);
        let mut valid_cards = game_status.my_current_hand.iter()
            .filter(|card| Some(card.suit) == current_suit)
            .collect::<Vec<&Card>>();

        if valid_cards.is_empty() {
            valid_cards.extend(&game_status.my_current_hand);
        }

        let evaluation = valid_cards.into_iter()
            .map(|card| ((Self::score_card(card, game_status), card), card))
            .collect::<BTreeMap<_,&Card>>();

        // println!("Evaluation: {:?}", evaluation);

        evaluation.values()
            .next()
            .expect("No valid cards to play!")
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use player::PlayerName;
    use card::Card;
    use card::Rank::*;
    use card::Suit::*;
    use game_status::GameStatus;
    use game_status::dto::GameStatusDto;
    use card_strategy::CardStrategy;

    use try_from::TryFrom;
    use error::Error;

    extern crate serde;
    extern crate serde_json;

    use std::fs::File;
    use std::io::Read;

    fn open_scenario(name: &str) -> GameStatus {
        let file_name = format!("samples/scenarios/{}.json", name);
        let mut game_status_file = File::open(file_name).unwrap();
        let mut game_status_string = String::new();
        game_status_file.read_to_string(&mut game_status_string).unwrap();
        let game_status_dto: GameStatusDto = serde_json::from_str(&game_status_string).map_err(Error::from).unwrap();
        GameStatus::try_from(game_status_dto).unwrap()
    }

    fn should_play(name: &str, expected_card: Card) {
        let player_name = PlayerName::new("Derek Williams");
        let game_status = open_scenario(name);
        let card = DefensiveCardStrategy.play_card(&game_status, &player_name).clone();
        assert_eq!(expected_card, card);
    }

    #[test]
    fn normal_1() {
        should_play("normal 1", Jack.of(Diamond));
    }

    #[test]
    fn normal_2() {
        should_play("normal 2", King.of(Club));
    }

    #[test]
    fn normal_3() {
        should_play("normal 3", Six.of(Spade));
    }

    #[test]
    fn should_play_heart_1() {
        should_play("should play heart 1", Seven.of(Heart));
    }

    #[test]
    fn should_play_heart_2() {
        should_play("should play heart 2", Four.of(Heart));
    }

    #[test]
    fn should_play_high_rank_1() {
        should_play("should play high rank 1", King.of(Diamond));
    }

    #[test]
    fn should_play_high_rank_2() {
        should_play("should play high rank 2", Ace.of(Spade));
    }

    // #[test]
    // fn should_play_high_rank_3() { // Should get rid of high risk high rank
    //     should_play("should play high rank 3", Ace.of(Spade));
    // }

    #[test]
    fn should_not_crash_during_card_play() {
        should_play("crashed during card play", Ten.of(Heart));
    }

}<commit_msg>tweaking the strats<commit_after>use card_strategy::CardStrategy;

use card::Card;
use card::Suit;
use card::Rank;
use game_status::GameStatus;
use player::PlayerName;

use std::collections::BTreeMap;
use std::collections::BTreeSet;

#[derive(Debug)]
pub struct DefensiveCardStrategy;

/*
    Play 2 of clubs if in hand.
    If going to win a deal, do so with highest ranking card.
*/
impl DefensiveCardStrategy {
    fn score_card<'a>(card: &'a Card, game_status: &'a GameStatus) -> (i32, i32, i32, i32, i32, i32, i32) {
        let potential_points = Self::potential_points(card, game_status);
        let definite_points = if Self::will_win_deal(card, game_status) {
            potential_points
        } else {
            0
        };
        let later_potential_points = 0 - Self::later_potential_points(card, game_status);
        let card_penalty_to_me = Self::card_penalty_to_me(card, game_status);
        let card_penalty = 0 - Self::card_penalty(card, game_status);
        let trouble = Self::trouble_score(card, game_status);
        let card_rank = 0 - (u32::from(card.rank) as i32);
        (definite_points, potential_points, later_potential_points, card_penalty_to_me, card_penalty, trouble, card_rank)
    }

    fn card_penalty(card: &Card, game_status: &GameStatus) -> i32 {
        game_status.round_parameters.card_points.get(card)
            .map(|penalty| *penalty)
            .unwrap_or_default()
    }

    fn card_penalty_to_me(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            Self::card_penalty(card, game_status)
        } else {
            0
        }
    }

    fn trouble_score(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            let trouble = u32::from(card.rank) as i32;
            if Self::card_penalty_to_me(card, game_status) < 0 {
                0 - trouble
            } else {
                if Self::will_win_deal(card, game_status) {
                    u32::from(Rank::Ace) as i32
                } else {
                    trouble
                }
            }
        } else {
            0
        }
    }

    fn can_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        game_status.my_in_progress_deal.as_ref().and_then(|deal|
            deal.suit.and_then(|suit|
                deal.deal_cards.iter()
                    .map(|deal_card| deal_card.card)
                    .filter(|card| card.suit == suit)
                    .max()
                    .map(|winning_card| card.suit == suit && card.rank > winning_card.rank))).unwrap_or(true)
    }

    fn will_win_deal(card: &Card, game_status: &GameStatus) -> bool {
        Self::can_win_deal(card, game_status) && (Self::plays_left(game_status).len() == 0 ||
            Self::deal_suit(game_status)
                .and_then(|suit| Self::remaining_cards(game_status).iter()
                    .filter(|card| card.suit == *suit)
                    .max()
                    .map(|winning_card| card.rank > winning_card.rank)).unwrap_or(true))
    }

    fn deal_suit(game_status: &GameStatus) -> Option<&Suit> {
        game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit.as_ref())
    }

    fn plays_left(game_status: &GameStatus) -> BTreeSet<&PlayerName> {
        let mut players: BTreeSet<&PlayerName> = game_status.my_game_players.iter()
            .filter(|player| !player.has_turn)
            .map(|player| &player.team_name)
            .collect();

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                players.remove(&deal_card.player_name);
            }
        }

        players
    }

    fn remaining_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        let mut cards = Card::all();

        for deal in &game_status.my_game_deals {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        if let &Some(ref deal) = &game_status.my_in_progress_deal {
            for deal_card in &deal.deal_cards {
                cards.remove(&deal_card.card);
            }
        }

        for card in &game_status.my_current_hand {
            cards.remove(&card);
        }

        cards
    }

    fn dealt_cards(game_status: &GameStatus) -> BTreeSet<Card> {
        game_status.my_in_progress_deal.as_ref()
            .map(|deal| deal.deal_cards.iter().map(|deal_card| deal_card.card).collect())
            .unwrap_or_default()
    }

    fn potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        if Self::can_win_deal(card, game_status) {
            let cards = Self::remaining_cards(game_status);

            let dealt_points: i32 = Self::dealt_cards(game_status).iter()
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let suit_points: i32 = cards.iter()
                .filter(|other| other.suit == card.suit)
                .filter(|other| other.rank < card.rank)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let other_points: i32 = cards.iter()
                .filter(|other| other.suit != card.suit)
                .map(|other| Self::card_penalty(other, game_status))
                .sum();

            let suit_win_points = (Self::chance_of_win(card, game_status) * (suit_points as f32)) as i32;
            let other_win_points = (Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32;

            dealt_points + suit_win_points + other_win_points
        } else {
            0
        }
    }

    fn later_potential_points(card: &Card, game_status: &GameStatus) -> i32 {
        let cards = Self::remaining_cards(game_status);

        let suit_points: i32 = cards.iter()
            .filter(|other| other.suit == card.suit)
            .filter(|other| other.rank < card.rank)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        let other_points: i32 = cards.iter()
            .filter(|other| other.suit != card.suit)
            .map(|other| Self::card_penalty(other, game_status))
            .sum();

        suit_points + ((Self::chance_of_later_win(card, game_status) * (other_points as f32)) as i32)
    }

    fn chance_of_later_win(card: &Card, game_status: &GameStatus) -> f32 {
        let cards = Self::remaining_cards(game_status);
        let suit_cards = cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
        let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
        if suit_cards.is_empty() {
            1.0
        } else {
            (will_win_count as f32) / (suit_cards.len() as f32)
        }
    }

    fn chance_of_win(card: &Card, game_status: &GameStatus) -> f32 {
        if Self::will_win_deal(card, game_status) {
            1.0
        } else {
            if Self::deal_suit(game_status).map(|suit| suit == &card.suit).unwrap_or(true) && Self::plays_left(game_status).len() > 0 {
                let cards = Self::remaining_cards(game_status);
                let suit_cards = cards.iter().filter(|other| other.suit == card.suit).collect::<Vec<_>>();
                let will_win_count = suit_cards.iter().filter(|other| other.rank < card.rank).collect::<Vec<_>>().len();
                if suit_cards.is_empty() {
                    1.0
                } else {
                    (will_win_count as f32) / (suit_cards.len() as f32)
                }
            } else {
                0.0
            }
        }
    }

}

impl CardStrategy for DefensiveCardStrategy {

    fn pass_cards<'a>(&mut self, game_status: &'a GameStatus) -> Vec<&'a Card> {
        game_status.my_initial_hand.iter()
            .map(|card| ((0 - Self::later_potential_points(card, game_status), card), card))
            .collect::<BTreeMap<_, &Card>>()
            .into_iter()
            .map(|kv| kv.1)
            .take(3)
            .collect()
    }

    fn play_card<'a>(&mut self, game_status: &'a GameStatus, player_name: &PlayerName) -> &'a Card {
        let current_suit = game_status.my_in_progress_deal.as_ref().and_then(|deal| deal.suit);
        let mut valid_cards = game_status.my_current_hand.iter()
            .filter(|card| Some(card.suit) == current_suit)
            .collect::<Vec<&Card>>();

        if valid_cards.is_empty() {
            valid_cards.extend(&game_status.my_current_hand);
        }

        let evaluation = valid_cards.into_iter()
            .map(|card| ((Self::score_card(card, game_status), card), card))
            .collect::<BTreeMap<_,&Card>>();

        // println!("Evaluation: {:?}", evaluation);

        evaluation.values()
            .next()
            .expect("No valid cards to play!")
    }

}

#[cfg(test)]
mod tests {
    use super::*;
    use player::PlayerName;
    use card::Card;
    use card::Rank::*;
    use card::Suit::*;
    use game_status::GameStatus;
    use game_status::dto::GameStatusDto;
    use card_strategy::CardStrategy;

    use try_from::TryFrom;
    use error::Error;

    extern crate serde;
    extern crate serde_json;

    use std::fs::File;
    use std::io::Read;

    fn open_scenario(name: &str) -> GameStatus {
        let file_name = format!("samples/scenarios/{}.json", name);
        let mut game_status_file = File::open(file_name).unwrap();
        let mut game_status_string = String::new();
        game_status_file.read_to_string(&mut game_status_string).unwrap();
        let game_status_dto: GameStatusDto = serde_json::from_str(&game_status_string).map_err(Error::from).unwrap();
        GameStatus::try_from(game_status_dto).unwrap()
    }

    fn should_play(name: &str, expected_card: Card) {
        let player_name = PlayerName::new("Derek Williams");
        let game_status = open_scenario(name);
        let card = DefensiveCardStrategy.play_card(&game_status, &player_name).clone();
        assert_eq!(expected_card, card);
    }

    #[test]
    fn normal_1() {
        should_play("normal 1", Jack.of(Diamond));
    }

    #[test]
    fn normal_2() {
        should_play("normal 2", King.of(Club));
    }

    #[test]
    fn normal_3() {
        should_play("normal 3", Six.of(Spade));
    }

    #[test]
    fn should_play_heart_1() {
        should_play("should play heart 1", Seven.of(Heart));
    }

    #[test]
    fn should_play_heart_2() {
        should_play("should play heart 2", Four.of(Heart));
    }

    #[test]
    fn should_play_high_rank_1() {
        should_play("should play high rank 1", King.of(Diamond));
    }

    #[test]
    fn should_play_high_rank_2() {
        should_play("should play high rank 2", Ace.of(Spade));
    }

    // #[test]
    // fn should_play_high_rank_3() { // Should get rid of high risk high rank
    //     should_play("should play high rank 3", Ace.of(Spade));
    // }

    #[test]
    fn should_not_crash_during_card_play() {
        should_play("crashed during card play", Ten.of(Heart));
    }

}<|endoftext|>