<commit_before>use devices::{NodeId, ServiceKind, Service, Input, Output};
use util::{Exactly, Id};
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<NodeId>>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: Id<NodeId>) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
/// use fxbox_taxonomy::util::Id;
///
/// let selector = InputSelector::new()
///   .with_parent(Id<NodeId>::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Input>>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: Id<Input>) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Output>>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: Id<Output>) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) => false,
            (&Some(_), &None) => false,
            _ => true,
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<commit_msg>[fxbox/taxonomy] Fixed doctest, part 2<commit_after>use devices::{NodeId, ServiceKind, Service, Input, Output};
use util::{Exactly, Id};
use values;

use serde::ser::Serializer;
use serde::de::Deserializer;

use std::cmp;

fn merge<T>(mut a: Vec<T>, mut b: Vec<T>) -> Vec<T> where T: Ord {
    a.append(&mut b);
    a.sort();
    a.dedup();
    a
}

/// A selector for one or more nodes.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
///
/// let selector = NodeSelector::new()
///   .with_tags(vec!["entrance".to_owned()])
///   .with_inputs(vec![InputSelector::new() /* can be more restrictive */]);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct NodeSelector {
    /// If `Exactly(id)`, return only the node with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<NodeId>>,

    ///  Restrict results to nodes that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// Restrict results to nodes that have all the inputs in `inputs`.
    #[serde(default)]
    pub inputs: Vec<InputSelector>,

    /// Restrict results to nodes that have all the outputs in `outputs`.
    #[serde(default)]
    pub outputs: Vec<OutputSelector>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl NodeSelector {
    /// Create a new selector that accepts all nodes.
    pub fn new() -> Self {
        Self::default()
    }

    /// Selector for a node with a specific id.
    pub fn with_id(self, id: Id<NodeId>) -> Self {
        NodeSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    ///  Restrict results to nodes that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        NodeSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict results to nodes that have all the inputs in `inputs`.
    pub fn with_inputs(mut self, mut inputs: Vec<InputSelector>) -> Self {
        NodeSelector {
            inputs: {self.inputs.append(&mut inputs); self.inputs},
            .. self
        }
    }

    /// Restrict results to nodes that have all the outputs in `outputs`.
    pub fn with_outputs(mut self, mut outputs: Vec<OutputSelector>) -> Self {
        NodeSelector {
            outputs: {self.outputs.append(&mut outputs); self.outputs},
            .. self
        }
    }

    /// Restrict results to nodes that are accepted by two selector.
    pub fn and(mut self, mut other: NodeSelector) -> Self {
        NodeSelector {
            id: self.id.and(other.id),
            tags: merge(self.tags, other.tags),
            inputs: {self.inputs.append(&mut other.inputs); self.inputs},
            outputs: {self.outputs.append(&mut other.outputs); self.outputs},
            private: (),
        }
    }
}

/// A selector for one or more input services.
///
///
/// # Example
///
/// ```
/// use fxbox_taxonomy::selector::*;
/// use fxbox_taxonomy::devices::*;
/// use fxbox_taxonomy::util::Id;
///
/// let selector = InputSelector::new()
///   .with_parent(Id::new("foxbox".to_owned()))
///   .with_kind(ServiceKind::CurrentTimeOfDay);
/// ```
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct InputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Input>>,

    /// If `Eactly(id)`, return only services that are children of
    /// node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exatly(k)`, restrict results to services that produce values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support polling
    /// with the acceptable period.
    #[serde(default)]
    pub poll: Option<Period>,

    /// If `Some(r)`, restrict results to services that support trigger
    /// with the acceptable period.
    #[serde(default)]
    pub trigger: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}
impl InputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        Self::default()
    }

    /// Restrict to a service with a specific id.
    pub fn with_id(self, id: Id<Input>) -> Self {
        InputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        InputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Restrict to a service with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        InputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        InputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support polling with the acceptable
    /// period
    pub fn with_poll(self, period: Period) -> Self {
        InputSelector {
            poll: Period::and_option(self.poll, Some(period)),
            .. self
        }
    }

    /// Restrict to services that support trigger with the acceptable
    /// period
    pub fn with_trigger(self, period: Period) -> Self {
        InputSelector {
            trigger: Period::and_option(self.trigger, Some(period)),
            .. self
        }
    }

    /// Restrict to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        InputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            poll: Period::and_option(self.poll, other.poll),
            trigger: Period::and_option(self.trigger, other.trigger),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Input>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.poll, &service.mechanism.poll) {
            return false;
        }
        if !Period::matches_option(&self.trigger, &service.mechanism.trigger) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// A selector for one or more output services.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct OutputSelector {
    /// If `Exactly(id)`, return only the service with the corresponding id.
    #[serde(default)]
    pub id: Exactly<Id<Output>>,

    /// If `Exactly(id)`, return only services that are immediate children
    /// of node `id`.
    #[serde(default)]
    pub parent: Exactly<Id<NodeId>>,

    ///  Restrict results to services that have all the tags in `tags`.
    #[serde(default)]
    pub tags: Vec<String>,

    /// If `Exactly(k)`, restrict results to services that accept values
    /// of kind `k`.
    #[serde(default)]
    pub kind: Exactly<ServiceKind>,

    /// If `Some(r)`, restrict results to services that support pushing
    /// with the acceptable period.
    #[serde(default)]
    pub push: Option<Period>,

    /// Make sure that we can't instantiate from another crate.
    #[serde(default, skip_serializing)]
    private: (),
}

impl OutputSelector {
    /// Create a new selector that accepts all input services.
    pub fn new() -> Self {
        OutputSelector::default()
    }

    /// Selector to a service with a specific id.
    pub fn with_id(self, id: Id<Output>) -> Self {
        OutputSelector {
            id: self.id.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific parent.
    pub fn with_parent(self, id: Id<NodeId>) -> Self {
        OutputSelector {
            parent: self.parent.and(Exactly::Exactly(id)),
            .. self
        }
    }

    /// Selector to services with a specific kind.
    pub fn with_kind(self, kind: ServiceKind) -> Self {
        OutputSelector {
            kind: self.kind.and(Exactly::Exactly(kind)),
            .. self
        }
    }

    ///  Restrict to services that have all the tags in `tags`.
    pub fn with_tags(self, tags: Vec<String>) -> Self {
        OutputSelector {
            tags: merge(self.tags, tags),
            .. self
        }
    }

    /// Restrict to services that support push with the acceptable
    /// period
    pub fn with_push(self, period: Period) -> Self {
        OutputSelector {
            push: Period::and_option(self.push, Some(period)),
            .. self
        }
    }

    /// Restrict results to services that are accepted by two selector.
    pub fn and(self, other: Self) -> Self {
        OutputSelector {
            id: self.id.and(other.id),
            parent: self.parent.and(other.parent),
            tags: merge(self.tags, other.tags),
            kind: self.kind.and(other.kind),
            push: Period::and_option(self.push, other.push),
            private: (),
        }
    }

    /// Determine if a service is matched by this selector.
    pub fn matches(&self, service: &Service<Output>) -> bool {
        if !self.id.matches(&service.id) {
            return false;
        }
        if !self.parent.matches(&service.node) {
            return false;
        }
        if !self.kind.matches(&service.mechanism.kind) {
            return false;
        }
        if !Period::matches_option(&self.push, &service.mechanism.push) {
            return false;
        }
        if !has_selected_tags(&self.tags, &service.tags) {
            return false;
        }
        return true;
    }
}

/// An acceptable interval of time.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
pub struct Period {
    #[serde(default)]
    pub min: Option<values::ValDuration>,
    #[serde(default)]
    pub max: Option<values::ValDuration>,
}
impl Period {
    pub fn and(self, other: Self) -> Self {
        let min = match (self.min, other.min) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(min1), Some(min2)) => Some(cmp::max(min1, min2))
        };
        let max = match (self.max, other.max) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(max1), Some(max2)) => Some(cmp::min(max1, max2))
        };
        Period {
            min: min,
            max: max
        }
    }

    pub fn and_option(a: Option<Self>, b: Option<Self>) -> Option<Self> {
        match (a, b) {
            (None, x@_) => x,
            (x@_, None) => x,
            (Some(a), Some(b)) => Some(a.and(b))
        }
    }

    pub fn matches(&self, duration: &values::ValDuration) -> bool {
        if let Some(ref min) = self.min {
            if min > duration {
                return false;
            }
        }
        if let Some(ref max) = self.max {
            if max < duration {
                return false;
            }
        }
        return true;
    }

    pub fn matches_option(period: &Option<Self>, duration: &Option<values::ValDuration>) -> bool {
        match (period, duration) {
            (&Some(ref period), &Some(ref duration))
                if !period.matches(duration) => false,
            (&Some(_), &None) => false,
            _ => true,
        }
    }
}

fn has_selected_tags(actual: &Vec<String>, requested: &Vec<String>) -> bool {
    for tag in &*actual {
        if requested.iter().find(|x| *x == tag).is_none() {
            return false;
        }
    }
    return true;
}
<|endoftext|>


<commit_before>use crate::errors::OscError;
use crate::regex::Regex;
use crate::types::Result;

fn validate_address(addr: &str) -> Result<()> {
    if !addr.is_ascii() {
        return Err(OscError::BadAddress(
            "Address must only contain ASCII characters",
        ));
    }
    if addr.is_empty() {
        return Err(OscError::BadAddress(
            "Address must be at least 1 character long",
        ));
    }

    // Check if address starts with '/'
    if !addr.starts_with('/') {
        return Err(OscError::BadAddress("Address must start with '/'"));
    }

    // Check if address ends with '/'
    if addr.ends_with('/') {
        return Err(OscError::BadAddress("Address must not end with '/'"));
    }

    Ok(())
}

/// Check if the address of an OSC method is valid
pub fn validate_method_address(addr: &str) -> Result<()> {
    match validate_address(addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }

    let chars = addr.chars();

    // Check if address contains illegal characters
    for char in chars {
        if ((char as u8) < 0x20) | ((char as u8) > 0x7E) {
            return Err(OscError::BadAddress(
                "Address may only contain printable ASCII characters",
            ));
        }
        if " #*,?[]{}".chars().any(|x| char == x) {
            return Err(OscError::BadAddress(
                "Address may not contain any of the following characters: ' #*,?[]{}'",
            ));
        }
    }

    Ok(())
}

/// Check if the address of an OSC message is valid
pub fn validate_message_address(addr: &str) -> Result<()> {
    match validate_address(addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }

    let chars = addr.chars();

    // Validate rest of address
    let mut in_character_range = false;
    let mut in_string_list = false;
    for char in chars {
        if ((char as u8) < 0x20) | ((char as u8) > 0x7E) {
            return Err(OscError::BadAddress(
                "Address may only contain printable ASCII characters",
            ));
        }
        if " #".chars().any(|x| char == x) {
            return Err(OscError::BadAddress(
                "Address may not contain any of the following characters: ' #'",
            ));
        }
        if !in_string_list && char == ',' {
            return Err(OscError::BadAddress("Address may not contain any of the following characters outside of string lists: ','"));
        }
        if char == '[' {
            in_character_range = true;
            if in_string_list {
                return Err(OscError::BadAddress("Can not start a character range (square brackets) within string list (curly brackets)"));
            }
        }
        if (char == '/') & in_character_range {
            return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the next address part started"));
        }
        if char == ']' {
            in_character_range = false;
        }
        if char == '{' {
            in_string_list = true;
        }
        if (char == '/') & in_string_list {
            return Err(OscError::BadAddress("String list (curly brackets) was started but not closed before the next address part started"));
        }
        if char == '}' {
            in_string_list = false;
            if in_character_range {
                return Err(OscError::BadAddress("Can not start a string list (curly brackets) within character range (square brackets)"));
            }
        }
    }
    if in_character_range {
        return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the end of the address"));
    }
    if in_string_list {
        return Err(OscError::BadAddress(
            "String list (curly brackets) was started but not closed before the end of the address",
        ));
    }

    Ok(())
}

/// Match a single part of two OSC addresses
fn match_part(message_part: &str, method_part: &str) -> bool {
    // direct match
    if message_part == method_part {
        return true;
    }

    // Use regex for everything else
    let mut pattern = message_part
        .to_string()
        .replace("*", "\\w*")
        .replace("?", "\\w")
        .replace("[!", "[^")
        .replace(",", ")|(?:")
        .replace("{", "(?:")
        .replace("}", ")");

    // Anchor the pattern to beginning and end of string
    pattern = format!("^{}$", pattern);

    let re = Regex::new(pattern.as_str()).unwrap();

    re.is_match(method_part)
}

/// Check if a message address matches a method address
pub fn match_address(message_addr: &str, method_addr: &str) -> Result<bool> {
    match validate_message_address(message_addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }
    match validate_method_address(method_addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }

    let message_addr_parts: Vec<&str> = message_addr.split('/').collect();
    let method_addr_parts: Vec<&str> = method_addr.split('/').collect();

    if message_addr_parts.len() != method_addr_parts.len() {
        // Both addresses must have the same amount of parts
        return Ok(false);
    }

    for i in 1..message_addr_parts.len() {
        //println!("Parts: '{}' '{}' {}", message_addr_parts[i], method_addr_parts[i], match_part(message_addr_parts[i], method_addr_parts[i]));
        if !match_part(message_addr_parts[i], method_addr_parts[i]) {
            return Ok(false);
        }
    }

    Ok(true)
}
<commit_msg>Use match instead of if-else<commit_after>use crate::errors::OscError;
use crate::regex::Regex;
use crate::types::Result;

fn validate_address(addr: &str) -> Result<()> {
    if !addr.is_ascii() {
        return Err(OscError::BadAddress(
            "Address must only contain ASCII characters",
        ));
    }
    if addr.is_empty() {
        return Err(OscError::BadAddress(
            "Address must be at least 1 character long",
        ));
    }

    // Check if address starts with '/'
    if !addr.starts_with('/') {
        return Err(OscError::BadAddress("Address must start with '/'"));
    }

    // Check if address ends with '/'
    if addr.ends_with('/') {
        return Err(OscError::BadAddress("Address must not end with '/'"));
    }

    Ok(())
}

/// Check if the address of an OSC method is valid
pub fn validate_method_address(addr: &str) -> Result<()> {
    match validate_address(addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }

    let chars = addr.chars();

    // Check if address contains illegal characters
    for char in chars {
        match char {
            _c if ((char as u8) < 0x20) | ((char as u8) > 0x7E) => {
                return Err(OscError::BadAddress(
                    "Address may only contain printable ASCII characters",
                ));
            }
            _c if " #*,?[]{}".chars().any(|x| char == x) => {
                return Err(OscError::BadAddress(
                    "Address may not contain any of the following characters: ' #*,?[]{}'",
                ));
            }
            _ => (),
        }
    }

    Ok(())
}

/// Check if the address of an OSC message is valid
pub fn validate_message_address(addr: &str) -> Result<()> {
    match validate_address(addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }

    let chars = addr.chars();

    // Validate rest of address
    let mut in_character_range = false;
    let mut in_string_list = false;
    for char in chars {
        if ((char as u8) < 0x20) | ((char as u8) > 0x7E) {
            return Err(OscError::BadAddress(
                "Address may only contain printable ASCII characters",
            ));
        }
        if " #".chars().any(|x| char == x) {
            return Err(OscError::BadAddress(
                "Address may not contain any of the following characters: ' #'",
            ));
        }
        if !in_string_list && char == ',' {
            return Err(OscError::BadAddress("Address may not contain any of the following characters outside of string lists: ','"));
        }
        if char == '[' {
            in_character_range = true;
            if in_string_list {
                return Err(OscError::BadAddress("Can not start a character range (square brackets) within string list (curly brackets)"));
            }
        }
        if (char == '/') & in_character_range {
            return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the next address part started"));
        }
        if char == ']' {
            in_character_range = false;
        }
        if char == '{' {
            in_string_list = true;
        }
        if (char == '/') & in_string_list {
            return Err(OscError::BadAddress("String list (curly brackets) was started but not closed before the next address part started"));
        }
        if char == '}' {
            in_string_list = false;
            if in_character_range {
                return Err(OscError::BadAddress("Can not start a string list (curly brackets) within character range (square brackets)"));
            }
        }
    }
    if in_character_range {
        return Err(OscError::BadAddress("Character range (square brackets) was started but not closed before the end of the address"));
    }
    if in_string_list {
        return Err(OscError::BadAddress(
            "String list (curly brackets) was started but not closed before the end of the address",
        ));
    }

    Ok(())
}

/// Match a single part of two OSC addresses
fn match_part(message_part: &str, method_part: &str) -> bool {
    // direct match
    if message_part == method_part {
        return true;
    }

    // Use regex for everything else
    let mut pattern = message_part
        .to_string()
        .replace("*", "\\w*")
        .replace("?", "\\w")
        .replace("[!", "[^")
        .replace(",", ")|(?:")
        .replace("{", "(?:")
        .replace("}", ")");

    // Anchor the pattern to beginning and end of string
    pattern = format!("^{}$", pattern);

    let re = Regex::new(pattern.as_str()).unwrap();

    re.is_match(method_part)
}

/// Check if a message address matches a method address
pub fn match_address(message_addr: &str, method_addr: &str) -> Result<bool> {
    match validate_message_address(message_addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }
    match validate_method_address(method_addr) {
        Ok(()) => {}
        Err(e) => return Err(e),
    }

    let message_addr_parts: Vec<&str> = message_addr.split('/').collect();
    let method_addr_parts: Vec<&str> = method_addr.split('/').collect();

    if message_addr_parts.len() != method_addr_parts.len() {
        // Both addresses must have the same amount of parts
        return Ok(false);
    }

    for i in 1..message_addr_parts.len() {
        //println!("Parts: '{}' '{}' {}", message_addr_parts[i], method_addr_parts[i], match_part(message_addr_parts[i], method_addr_parts[i]));
        if !match_part(message_addr_parts[i], method_addr_parts[i]) {
            return Ok(false);
        }
    }

    Ok(true)
}
<|endoftext|>


<commit_before>use super::routes::{call_route, Routes};
use super::types::{
    ClanData, ClanDataData, ClanDiscription, ClanRating, ClanRatingData, Response, TopClans,
};
use crate::db;
use crate::other::{remove_all_quotes, ConfAndFlags};
use futures::Future;
use std::collections::HashMap;
use std::pin::Pin;

pub async fn search_for_clan_ids(config: &ConfAndFlags) -> Result<(), String> {
    let mut clans = get_all_clan_ids(config).await?;
    println!("Fetched {} clan ids", clans.len());

    clans = filter_out_clans(config, clans).await;
    println!("Filtered clans, output = {} clans", clans.len());

    // // TODO: Removes blacklisted clans and add extra clans to the clans list
    clans = remove_duplicates(clans);
    println!("Removed duplicate clans, output = {} clans", clans.len());

    db::set_clan_ids(&clans);

    // when this is ran for the first time make sure to get clan list
    get_clan_data(config, Some(clans)).await?;

    Ok(())
}

// GetClanData returns all needed information about clans
// includedClans is not required
pub async fn get_clan_data(
    config: &ConfAndFlags,
    included_clans: Option<Vec<String>>,
) -> Result<(), String> {
    let mut clan_ids: HashMap<String, ()> = if let Some(ids) = included_clans {
        let mut res: HashMap<String, ()> = HashMap::new();
        for id in ids {
            res.insert(id, ());
        }
        res
    } else {
        db::get_clan_ids().clone()
    };

    let blocked_clans = db::get_blocked_clans_ids().clone();
    let mut extra_clans = db::get_extra_clans_ids().clone();

    for (clan_id, _) in &clan_ids {
        extra_clans.remove(clan_id);
    }
    for (id, _) in extra_clans {
        clan_ids.insert(id, ());
    }

    let mut to_save: Vec<db::ClanStats> = Vec::new();
    let to_fetch = split_map_to_chucks(clan_ids);

    for chunk in to_fetch {
        let res = get_clan_data_try(config.clone(), chunk, None).await;
        let (info, rating, clans_to_remove_from_ids) = match res {
            Ok(v) => v,
            Err(e) => {
                println!("get_clan_data error check get_clan_data_try: error: {}", e);
                continue;
            }
        };

        db::remove_clan_ids(clans_to_remove_from_ids);

        for (id, item) in info {
            let rating = if let Some(rating) = rating.get(&id) {
                rating
            } else {
                continue;
            };
            let is_blocked = blocked_clans.get(&id).is_some();

            if item.tag.len() < 2 {
                continue;
            }

            let emblems = db::ClanStatsEmblems {
                x256_wowp: item.emblems.x256.wowp,
                x195_portal: item.emblems.x195.portal,
                x64_portal: item.emblems.x64.portal,
                x64_wot: item.emblems.x64.wot,
                x32_portal: item.emblems.x32.portal,
                x24_portal: item.emblems.x24.portal,
            };

            let stats = db::HistoryClanStats {
                tag: item.tag.clone(),
                name: item.name.clone(),
                id: id.clone(),
                members: item.members_count,
                battles: rating.battles_count_avg.value,
                daily_battles: rating.battles_count_avg_daily.value,
                efficiency: rating.efficiency.value,
                fb_elo10: rating.fb_elo_rating_10.value,
                fb_elo8: rating.fb_elo_rating_8.value,
                fb_elo6: rating.fb_elo_rating_6.value,
                fb_elo: rating.fb_elo_rating.value,
                gm_elo10: rating.gm_elo_rating_10.value,
                gm_elo8: rating.gm_elo_rating_8.value,
                gm_elo6: rating.gm_elo_rating_6.value,
                gm_elo: rating.gm_elo_rating.value,
                glob_rating: rating.global_rating_avg.value,
                glob_rating_weighted: rating.global_rating_weighted_avg.value,
                win_ratio: rating.wins_ratio_avg.value,
                v10l: rating.v10l_avg.value,
            };

            to_save.push(db::ClanStats {
                blocked: is_blocked,
                tag: item.tag,
                name: item.name,
                color: item.color,
                members: item.members_count,
                description: item.description_html,
                motto: item.motto,
                id: id.clone(),
                emblems: emblems,
                stats: stats,
            });
        }
    }
    db::set_current_clans_data(to_save)?;

    Ok(())
}

fn get_clan_data_try_re(
    config: ConfAndFlags,
    chunk_input: Vec<String>,
    removed_ids: Option<Vec<String>>,
) -> Pin<
    Box<
        dyn Future<
                Output = Result<
                    (
                        HashMap<String, ClanDataData>,
                        HashMap<String, ClanRatingData>,
                        Vec<String>,
                    ),
                    String,
                >,
            > + Send,
    >,
> {
    Box::pin(
        async move { get_clan_data_try(config, chunk_input.clone(), removed_ids.clone()).await },
    )
}

// GetClanDataTry is a helper function that retries when the api reports on invalid clan IDs
async fn get_clan_data_try(
    config: ConfAndFlags,
    chunk_input: Vec<String>,
    removed_ids: Option<Vec<String>>,
) -> Result<
    (
        HashMap<String, ClanDataData>,
        HashMap<String, ClanRatingData>,
        Vec<String>,
    ),
    String,
> {
    let chunk = remove_all_quotes(chunk_input);

    let clan_data_route = Routes::ClanData(chunk.clone());
    let info: Response<ClanData> = call_route(clan_data_route, &config).await?;
    let info_data = match info.get_data() {
        Ok(v) => v,
        Err(e) => {
            if let Some(meta) = info.error {
                match (meta.field, meta.value) {
                    (Some(field), Some(value)) => {
                        if field == "clan_id" && meta.message == "INVALID_CLAN_ID" && value != "" {
                            let mut new_chunk: Vec<String> = Vec::new();
                            let mut to_exclude: Vec<String> =
                                value.split(",").map(|id_str| id_str.to_string()).collect();

                            'outer: for clan_id in chunk {
                                for item in &to_exclude {
                                    if item == &clan_id {
                                        continue 'outer;
                                    }
                                }
                                new_chunk.push(clan_id);
                            }
                            let mut new_removed_ids = removed_ids.unwrap_or(Vec::new());
                            new_removed_ids.append(&mut to_exclude);

                            return get_clan_data_try_re(config, new_chunk, Some(new_removed_ids))
                                .await;
                        }
                    }
                    _ => {}
                }
            }

            return Err(e);
        }
    };

    let clan_rating_route = Routes::ClanRating(chunk);
    let rating: Response<ClanRating> = call_route(clan_rating_route, &config).await?;
    let rating_data = rating.get_data()?;

    if info_data.len() != rating_data.len() {
        Err(String::from("No clans in response"))
    } else {
        Ok((info_data, rating_data, removed_ids.unwrap_or(Vec::new())))
    }
}

// removes duplicates from an array
fn remove_duplicates(input: Vec<String>) -> Vec<String> {
    let mut output: Vec<String> = Vec::new();
    for input_item in input {
        let mut exsists = false;
        for output_item in output.iter() {
            if &input_item == output_item {
                exsists = true;
                break;
            }
        }
        if !exsists {
            output.push(input_item);
        }
    }
    output
}

// FilterOutClans filters out all not dutch clans out of a input list
async fn filter_out_clans(config: &ConfAndFlags, clan_ids: Vec<String>) -> Vec<String> {
    let tofetch = split_to_chucks(clan_ids);
    let mut to_return: Vec<String> = Vec::new();
    for ids in tofetch {
        let route = Routes::ClanDiscription(ids);
        let out: Response<ClanDiscription> = match call_route(route, config).await {
            Ok(v) => v,
            Err(e) => {
                println!("filter_out_clans api call failed, error: {}", e);
                continue;
            }
        };
        let data = match out.get_data() {
            Ok(v) => v,
            Err(e) => {
                println!("filter_out_clans api call failed, error: {}", e);
                continue;
            }
        };

        for (clan_id, clan) in data {
            let (description, tag) = match (clan.description, clan.tag) {
                (Some(des), Some(tag)) => (des, tag),
                _ => continue,
            };
            if !is_spesified_lang(config, description) {
                continue;
            }

            to_return.push(clan_id);
            if config.is_dev() {
                println!("found clan: {}", tag);
            }
        }
    }
    to_return
}

// SplitToChucks splits up a input list in arrays of 100
// This makes it easy to request a lot of things at the same time from the wargaming api
fn split_to_chucks(list: Vec<String>) -> Vec<Vec<String>> {
    let mut res: Vec<Vec<String>> = vec![];
    for item in list {
        if let Some(out) = res.last_mut() {
            if out.len() < 100 {
                out.push(item);
                continue;
            }
        }
        res.push(vec![item]);
    }
    res
}

fn split_map_to_chucks<T>(list: HashMap<String, T>) -> Vec<Vec<String>> {
    let mut res: Vec<Vec<String>> = vec![];
    for (item, _) in list {
        if let Some(out) = res.last_mut() {
            if out.len() < 100 {
                out.push(item);
                continue;
            }
        }
        res.push(vec![item]);
    }
    res
}

// GetAllClanIds returns all clan ids
async fn get_all_clan_ids(config: &ConfAndFlags) -> Result<Vec<String>, String> {
    let mut ids: Vec<String> = Vec::new();
    let mut page = 0;

    loop {
        page += 1;
        if page > config.flags().get_max_index_pages() {
            break;
        }

        let out: Response<TopClans> = call_route(Routes::TopClans(page), config).await?;
        let data = out.get_data()?;

        if data.len() == 0 {
            break;
        }

        for clan in data {
            ids.push(clan.clan_id.to_string());
        }

        if page % 10 == 1 {
            if config.is_dev() {
                println!("Fetched {} clans", ids.len());
            }
        }
    }

    Ok(ids)
}

// IsSpesifiedLang checks a setence for allowed and disallowed words
fn is_spesified_lang(config: &ConfAndFlags, input: String) -> bool {
    if input.len() == 0 {
        return false;
    }
    let formatted_input = input
        .to_lowercase()
        .replace("\n", " ")
        .replace("\t", " ")
        .replace("\r", " ")
        .replace("  ", " ");
    let formatted_input_parts: Vec<&str> = formatted_input.split(" ").collect();

    let inner_conf = config.conf();

    let mut res = false;
    for mut word in formatted_input_parts {
        word = word.trim();
        if word == "" {
            continue;
        }

        for disallowed_word in &inner_conf.disallowed_words {
            if word.contains(disallowed_word.trim()) {
                return false;
            }
        }

        if !res {
            for allowed_word in &inner_conf.allowed_words {
                if word.contains(allowed_word.trim()) {
                    res = true;
                    break;
                }
            }
        }
    }

    return res;
}
<commit_msg>Even more logging in prod build<commit_after>use super::routes::{call_route, Routes};
use super::types::{
    ClanData, ClanDataData, ClanDiscription, ClanRating, ClanRatingData, Response, TopClans,
};
use crate::db;
use crate::other::{remove_all_quotes, ConfAndFlags};
use futures::Future;
use std::collections::HashMap;
use std::pin::Pin;

pub async fn search_for_clan_ids(config: &ConfAndFlags) -> Result<(), String> {
    let mut clans = get_all_clan_ids(config).await?;
    println!("Fetched {} clan ids", clans.len());

    clans = filter_out_clans(config, clans).await;
    println!("Filtered clans, output = {} clans", clans.len());

    // // TODO: Removes blacklisted clans and add extra clans to the clans list
    clans = remove_duplicates(clans);
    println!("Removed duplicate clans, output = {} clans", clans.len());

    db::set_clan_ids(&clans);

    // when this is ran for the first time make sure to get clan list
    get_clan_data(config, Some(clans)).await?;

    Ok(())
}

// GetClanData returns all needed information about clans
// includedClans is not required
pub async fn get_clan_data(
    config: &ConfAndFlags,
    included_clans: Option<Vec<String>>,
) -> Result<(), String> {
    let mut clan_ids: HashMap<String, ()> = if let Some(ids) = included_clans {
        let mut res: HashMap<String, ()> = HashMap::new();
        for id in ids {
            res.insert(id, ());
        }
        res
    } else {
        db::get_clan_ids().clone()
    };

    let blocked_clans = db::get_blocked_clans_ids().clone();
    let mut extra_clans = db::get_extra_clans_ids().clone();

    for (clan_id, _) in &clan_ids {
        extra_clans.remove(clan_id);
    }
    for (id, _) in extra_clans {
        clan_ids.insert(id, ());
    }

    let mut to_save: Vec<db::ClanStats> = Vec::new();
    let to_fetch = split_map_to_chucks(clan_ids);

    for chunk in to_fetch {
        let res = get_clan_data_try(config.clone(), chunk, None).await;
        let (info, rating, clans_to_remove_from_ids) = match res {
            Ok(v) => v,
            Err(e) => {
                println!("get_clan_data error check get_clan_data_try: error: {}", e);
                continue;
            }
        };

        db::remove_clan_ids(clans_to_remove_from_ids);

        for (id, item) in info {
            let rating = if let Some(rating) = rating.get(&id) {
                rating
            } else {
                continue;
            };
            let is_blocked = blocked_clans.get(&id).is_some();

            if item.tag.len() < 2 {
                continue;
            }

            let emblems = db::ClanStatsEmblems {
                x256_wowp: item.emblems.x256.wowp,
                x195_portal: item.emblems.x195.portal,
                x64_portal: item.emblems.x64.portal,
                x64_wot: item.emblems.x64.wot,
                x32_portal: item.emblems.x32.portal,
                x24_portal: item.emblems.x24.portal,
            };

            let stats = db::HistoryClanStats {
                tag: item.tag.clone(),
                name: item.name.clone(),
                id: id.clone(),
                members: item.members_count,
                battles: rating.battles_count_avg.value,
                daily_battles: rating.battles_count_avg_daily.value,
                efficiency: rating.efficiency.value,
                fb_elo10: rating.fb_elo_rating_10.value,
                fb_elo8: rating.fb_elo_rating_8.value,
                fb_elo6: rating.fb_elo_rating_6.value,
                fb_elo: rating.fb_elo_rating.value,
                gm_elo10: rating.gm_elo_rating_10.value,
                gm_elo8: rating.gm_elo_rating_8.value,
                gm_elo6: rating.gm_elo_rating_6.value,
                gm_elo: rating.gm_elo_rating.value,
                glob_rating: rating.global_rating_avg.value,
                glob_rating_weighted: rating.global_rating_weighted_avg.value,
                win_ratio: rating.wins_ratio_avg.value,
                v10l: rating.v10l_avg.value,
            };

            to_save.push(db::ClanStats {
                blocked: is_blocked,
                tag: item.tag,
                name: item.name,
                color: item.color,
                members: item.members_count,
                description: item.description_html,
                motto: item.motto,
                id: id.clone(),
                emblems: emblems,
                stats: stats,
            });
        }
    }
    db::set_current_clans_data(to_save)?;

    Ok(())
}

fn get_clan_data_try_re(
    config: ConfAndFlags,
    chunk_input: Vec<String>,
    removed_ids: Option<Vec<String>>,
) -> Pin<
    Box<
        dyn Future<
                Output = Result<
                    (
                        HashMap<String, ClanDataData>,
                        HashMap<String, ClanRatingData>,
                        Vec<String>,
                    ),
                    String,
                >,
            > + Send,
    >,
> {
    Box::pin(
        async move { get_clan_data_try(config, chunk_input.clone(), removed_ids.clone()).await },
    )
}

// GetClanDataTry is a helper function that retries when the api reports on invalid clan IDs
async fn get_clan_data_try(
    config: ConfAndFlags,
    chunk_input: Vec<String>,
    removed_ids: Option<Vec<String>>,
) -> Result<
    (
        HashMap<String, ClanDataData>,
        HashMap<String, ClanRatingData>,
        Vec<String>,
    ),
    String,
> {
    let chunk = remove_all_quotes(chunk_input);

    let clan_data_route = Routes::ClanData(chunk.clone());
    let info: Response<ClanData> = call_route(clan_data_route, &config).await?;
    let info_data = match info.get_data() {
        Ok(v) => v,
        Err(e) => {
            if let Some(meta) = info.error {
                match (meta.field, meta.value) {
                    (Some(field), Some(value)) => {
                        if field == "clan_id" && meta.message == "INVALID_CLAN_ID" && value != "" {
                            let mut new_chunk: Vec<String> = Vec::new();
                            let mut to_exclude: Vec<String> =
                                value.split(",").map(|id_str| id_str.to_string()).collect();

                            'outer: for clan_id in chunk {
                                for item in &to_exclude {
                                    if item == &clan_id {
                                        continue 'outer;
                                    }
                                }
                                new_chunk.push(clan_id);
                            }
                            let mut new_removed_ids = removed_ids.unwrap_or(Vec::new());
                            new_removed_ids.append(&mut to_exclude);

                            return get_clan_data_try_re(config, new_chunk, Some(new_removed_ids))
                                .await;
                        }
                    }
                    _ => {}
                }
            }

            return Err(e);
        }
    };

    let clan_rating_route = Routes::ClanRating(chunk);
    let rating: Response<ClanRating> = call_route(clan_rating_route, &config).await?;
    let rating_data = rating.get_data()?;

    if info_data.len() != rating_data.len() {
        Err(String::from("No clans in response"))
    } else {
        Ok((info_data, rating_data, removed_ids.unwrap_or(Vec::new())))
    }
}

// removes duplicates from an array
fn remove_duplicates(input: Vec<String>) -> Vec<String> {
    let mut output: Vec<String> = Vec::new();
    for input_item in input {
        let mut exsists = false;
        for output_item in output.iter() {
            if &input_item == output_item {
                exsists = true;
                break;
            }
        }
        if !exsists {
            output.push(input_item);
        }
    }
    output
}

// FilterOutClans filters out all not dutch clans out of a input list
async fn filter_out_clans(config: &ConfAndFlags, clan_ids: Vec<String>) -> Vec<String> {
    let tofetch = split_to_chucks(clan_ids);
    let mut to_return: Vec<String> = Vec::new();
    for ids in tofetch {
        let route = Routes::ClanDiscription(ids);
        let out: Response<ClanDiscription> = match call_route(route, config).await {
            Ok(v) => v,
            Err(e) => {
                println!("filter_out_clans api call failed, error: {}", e);
                continue;
            }
        };
        let data = match out.get_data() {
            Ok(v) => v,
            Err(e) => {
                println!("filter_out_clans api call failed, error: {}", e);
                continue;
            }
        };

        for (clan_id, clan) in data {
            let (description, tag) = match (clan.description, clan.tag) {
                (Some(des), Some(tag)) => (des, tag),
                _ => continue,
            };
            if !is_spesified_lang(config, description) {
                continue;
            }

            to_return.push(clan_id);
            if config.is_dev() {
                println!("found clan: {}", tag);
            }
        }
    }
    to_return
}

// SplitToChucks splits up a input list in arrays of 100
// This makes it easy to request a lot of things at the same time from the wargaming api
fn split_to_chucks(list: Vec<String>) -> Vec<Vec<String>> {
    let mut res: Vec<Vec<String>> = vec![];
    for item in list {
        if let Some(out) = res.last_mut() {
            if out.len() < 100 {
                out.push(item);
                continue;
            }
        }
        res.push(vec![item]);
    }
    res
}

fn split_map_to_chucks<T>(list: HashMap<String, T>) -> Vec<Vec<String>> {
    let mut res: Vec<Vec<String>> = vec![];
    for (item, _) in list {
        if let Some(out) = res.last_mut() {
            if out.len() < 100 {
                out.push(item);
                continue;
            }
        }
        res.push(vec![item]);
    }
    res
}

// GetAllClanIds returns all clan ids
async fn get_all_clan_ids(config: &ConfAndFlags) -> Result<Vec<String>, String> {
    let mut ids: Vec<String> = Vec::new();
    let mut page = 0;

    loop {
        page += 1;
        if page > config.flags().get_max_index_pages() {
            break;
        }

        let out: Response<TopClans> = call_route(Routes::TopClans(page), config).await?;
        let data = out.get_data()?;

        if data.len() == 0 {
            break;
        }

        for clan in data {
            ids.push(clan.clan_id.to_string());
        }

        if config.is_dev() {
            if page % 10 == 1 {
                println!("Fetched {} clans", ids.len());
            }
        } else {
            if page % 50 == 1 {
                println!("Fetched {} clans", ids.len());
            }
        }
    }

    Ok(ids)
}

// IsSpesifiedLang checks a setence for allowed and disallowed words
fn is_spesified_lang(config: &ConfAndFlags, input: String) -> bool {
    if input.len() == 0 {
        return false;
    }
    let formatted_input = input
        .to_lowercase()
        .replace("\n", " ")
        .replace("\t", " ")
        .replace("\r", " ")
        .replace("  ", " ");
    let formatted_input_parts: Vec<&str> = formatted_input.split(" ").collect();

    let inner_conf = config.conf();

    let mut res = false;
    for mut word in formatted_input_parts {
        word = word.trim();
        if word == "" {
            continue;
        }

        for disallowed_word in &inner_conf.disallowed_words {
            if word.contains(disallowed_word.trim()) {
                return false;
            }
        }

        if !res {
            for allowed_word in &inner_conf.allowed_words {
                if word.contains(allowed_word.trim()) {
                    res = true;
                    break;
                }
            }
        }
    }

    return res;
}
<|endoftext|>


<commit_before>extern crate bf;
extern crate clap;

use std::io::{self, Read};
use std::fs::File;
use bf::*;
use clap::{Arg, App};

fn main() {
    let program = parser::parse_program(&get_program()).unwrap();
    let mut state = state::State::new();
    interpreter::interpret(&program, &mut state, &mut io::stdin(), &mut io::stdout());
}

fn get_program() -> Vec<u8> {
    let matches = App::new("bfi")
        .author("Jesse A. Tov <jesse.tov@gmail.com>")
        .about("A Brainfuck interpreter")
        .arg(Arg::with_name("expr")
            .short("e")
            .long("expr")
            .value_name("CODE")
            .help("BF code to execute")
            .multiple(true)
            .takes_value(true)
            .conflicts_with("INPUT"))
        .arg(Arg::with_name("INPUT")
            .help("The source file to interpret")
            .multiple(true)
            .conflicts_with("expr")
            .index(1))
        .get_matches();

    let mut input = Vec::new();
    if let Some(exprs) = matches.values_of("expr") {
        for e in exprs {
            input.extend(e.as_bytes());
        }
    } else if let Some(files) = matches.values_of("INPUT") {
        for f in files {
            let mut file = File::open(f).unwrap();
            file.read_to_end(&mut input).unwrap();
        }
    }

    input
}
<commit_msg>Factored out build_clap_app.<commit_after>extern crate bf;
extern crate clap;

use std::io::{self, Read};
use std::fs::File;
use bf::*;
use clap::{Arg, App};

fn main() {
    let program = parser::parse_program(&get_program()).unwrap();
    let mut state = state::State::new();
    interpreter::interpret(&program, &mut state, &mut io::stdin(), &mut io::stdout());
}

fn get_program() -> Vec<u8> {
    let matches = build_clap_app().get_matches();

    let mut input = Vec::new();
    if let Some(exprs) = matches.values_of("expr") {
        for e in exprs {
            input.extend(e.as_bytes());
        }
    } else if let Some(files) = matches.values_of("INPUT") {
        for f in files {
            let mut file = File::open(f).unwrap();
            file.read_to_end(&mut input).unwrap();
        }
    }

    input
}

fn build_clap_app() -> App<'static, 'static> {
    App::new("bfi")
        .author("Jesse A. Tov <jesse.tov@gmail.com>")
        .about("A Brainfuck interpreter")
        .arg(Arg::with_name("expr")
            .short("e")
            .long("expr")
            .value_name("CODE")
            .help("BF code to execute")
            .multiple(true)
            .takes_value(true)
            .conflicts_with("INPUT"))
        .arg(Arg::with_name("INPUT")
            .help("The source file to interpret")
            .multiple(true)
            .conflicts_with("expr")
            .index(1))
}

<|endoftext|>


<commit_before>use std::rc::Rc;

use num::{Complex, Zero, FromPrimitive};
use common::FFTnum;

use math_utils;
use twiddles;
use super::FFTAlgorithm;

pub struct RadersAlgorithm<T> {
    len: usize,

    primitive_root: u64,
    root_inverse: u64,

    inner_fft_data: Vec<Complex<T>>,
    inner_fft: Rc<FFTAlgorithm<T>>,
}

impl<T: FFTnum> RadersAlgorithm<T> {
    pub fn new(len: usize, inner_fft: Rc<FFTAlgorithm<T>>, inverse: bool) -> Self {
        assert_eq!(len - 1, inner_fft.len(), "For raders algorithm, inner_fft.len() must be self.len() - 1. Expected {}, got {}", len - 1, inner_fft.len());

        let inner_fft_len = len - 1;

        // compute the primitive root and its inverse for this size
        let primitive_root = math_utils::primitive_root(len as u64).unwrap();
        let root_inverse = math_utils::multiplicative_inverse(primitive_root, len as u64);

        // precompute the coefficients to use inside the process method
        let unity_scale: T = FromPrimitive::from_f64(1f64 / inner_fft_len as f64).unwrap();
        let mut inner_fft_input: Vec<Complex<T>> = (0..inner_fft_len)
            .map(|i| math_utils::modular_exponent(root_inverse, i as u64, len as u64) as usize)
            .map(|i| twiddles::single_twiddle(i, len, inverse))
            .map(|c| c * unity_scale)
            .collect();

        //precompute a FFT of our reordered twiddle factors
        let mut inner_fft_output = vec![Zero::zero(); inner_fft_len];
        inner_fft.process(&mut inner_fft_input, &mut inner_fft_output);

        Self {
            len: len,
            primitive_root: primitive_root,
            root_inverse: root_inverse,

            inner_fft_data: inner_fft_output,
            inner_fft: inner_fft,
        }
    }

    unsafe fn copy_from_input(&self, input: &[Complex<T>], output: &mut [Complex<T>]) {
        // it's not just a straight copy from the input to the scratch, we have
        // to compute the input index based on the scratch index and primitive root
        for (index, output_element) in output.iter_mut().enumerate() {
            let input_index = math_utils::modular_exponent(self.primitive_root, index as u64 + 1, self.len as u64) as usize - 1;

            *output_element = *input.get_unchecked(input_index);
        }
    }

    unsafe fn copy_to_output(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        // copy the data back into the input vector, but again it's not just a straight copy
        for (index, &input_element) in input.iter().enumerate() {
            let output_index = math_utils::modular_exponent(
                self.root_inverse,
                index as u64 + 1,
                self.len as u64) as usize - 1;

            *output.get_unchecked_mut(output_index) = input_element;
        }
    }

    fn perform_fft(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {

        //the input and output buffers are one element too large, split off the first and do the processing for the first now
        let (first_input, input) = input.split_first_mut().unwrap();
        let first_input_val = *first_input;

        let (first_output, output) = output.split_first_mut().unwrap();

        //the first element of the output will be the sum of all the inputs
        let input_sum: Complex<T> = input.iter().fold(Zero::zero(), |acc, &e| acc + e);
        *first_output = first_input_val + input_sum;

        // redorder the input as we copy it to the output buffer
        unsafe { self.copy_from_input(input, output) };

        // perform the first of two inner FFTs
        self.inner_fft.process(output, input);

        // multiply the inner result with our cached setup data
        // also conjugate every entry. this sets us up to do an inverse FFT
        // (because an inverse FFT is equivalent to a normal FFT where you conjugate both the inputs and outputs)
        for ((&input_cell, output_cell), &multiple) in input.iter().zip(output.iter_mut()).zip(self.inner_fft_data.iter()) {
            *output_cell = (input_cell * multiple).conj();
        }

        // execute the second FFT
        self.inner_fft.process(output, input);

        // to finalize the inverse, compute the conjugate of every element
        for element in input.iter_mut() {
            *element = element.conj() + first_input_val;
        }

        unsafe { self.copy_to_output(input, output) };
    }
}

impl<T: FFTnum> FFTAlgorithm<T> for RadersAlgorithm<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
    fn len(&self) -> usize {
        self.len
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use std::rc::Rc;
    use num::Zero;
    use test_utils::{random_signal, compare_vectors};
    use dft;
    use algorithm::{butterflies, DFTAlgorithm};

    #[test]
    fn test_raders() {
        for &len in &[3,5,7,11,13] {
            let dft = DFTAlgorithm::new(len, false);
            let inner_fft = Rc::new(DFTAlgorithm::new(len - 1, false));

            let raders_fft = RadersAlgorithm::new(len, inner_fft, false);

            let mut dft_input = random_signal(len);
            let mut raders_input = dft_input.clone();

            let mut expected = vec![Zero::zero(); len];
            dft.process(&mut dft_input, &mut expected);

            let mut actual = vec![Zero::zero(); len];
            raders_fft.process(&mut raders_input, &mut actual);

            println!("Expected: {:?}", expected);
            println!("Actual:   {:?}", actual);

            assert!(compare_vectors(&actual, &expected), "len = {}", len);
        }
    }
}
<commit_msg>Removed unused imports<commit_after>use std::rc::Rc;

use num::{Complex, Zero, FromPrimitive};
use common::FFTnum;

use math_utils;
use twiddles;
use super::FFTAlgorithm;

pub struct RadersAlgorithm<T> {
    len: usize,

    primitive_root: u64,
    root_inverse: u64,

    inner_fft_data: Vec<Complex<T>>,
    inner_fft: Rc<FFTAlgorithm<T>>,
}

impl<T: FFTnum> RadersAlgorithm<T> {
    pub fn new(len: usize, inner_fft: Rc<FFTAlgorithm<T>>, inverse: bool) -> Self {
        assert_eq!(len - 1, inner_fft.len(), "For raders algorithm, inner_fft.len() must be self.len() - 1. Expected {}, got {}", len - 1, inner_fft.len());

        let inner_fft_len = len - 1;

        // compute the primitive root and its inverse for this size
        let primitive_root = math_utils::primitive_root(len as u64).unwrap();
        let root_inverse = math_utils::multiplicative_inverse(primitive_root, len as u64);

        // precompute the coefficients to use inside the process method
        let unity_scale: T = FromPrimitive::from_f64(1f64 / inner_fft_len as f64).unwrap();
        let mut inner_fft_input: Vec<Complex<T>> = (0..inner_fft_len)
            .map(|i| math_utils::modular_exponent(root_inverse, i as u64, len as u64) as usize)
            .map(|i| twiddles::single_twiddle(i, len, inverse))
            .map(|c| c * unity_scale)
            .collect();

        //precompute a FFT of our reordered twiddle factors
        let mut inner_fft_output = vec![Zero::zero(); inner_fft_len];
        inner_fft.process(&mut inner_fft_input, &mut inner_fft_output);

        Self {
            len: len,
            primitive_root: primitive_root,
            root_inverse: root_inverse,

            inner_fft_data: inner_fft_output,
            inner_fft: inner_fft,
        }
    }

    unsafe fn copy_from_input(&self, input: &[Complex<T>], output: &mut [Complex<T>]) {
        // it's not just a straight copy from the input to the scratch, we have
        // to compute the input index based on the scratch index and primitive root
        for (index, output_element) in output.iter_mut().enumerate() {
            let input_index = math_utils::modular_exponent(self.primitive_root, index as u64 + 1, self.len as u64) as usize - 1;

            *output_element = *input.get_unchecked(input_index);
        }
    }

    unsafe fn copy_to_output(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        // copy the data back into the input vector, but again it's not just a straight copy
        for (index, &input_element) in input.iter().enumerate() {
            let output_index = math_utils::modular_exponent(
                self.root_inverse,
                index as u64 + 1,
                self.len as u64) as usize - 1;

            *output.get_unchecked_mut(output_index) = input_element;
        }
    }

    fn perform_fft(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {

        //the input and output buffers are one element too large, split off the first and do the processing for the first now
        let (first_input, input) = input.split_first_mut().unwrap();
        let first_input_val = *first_input;

        let (first_output, output) = output.split_first_mut().unwrap();

        //the first element of the output will be the sum of all the inputs
        let input_sum: Complex<T> = input.iter().fold(Zero::zero(), |acc, &e| acc + e);
        *first_output = first_input_val + input_sum;

        // redorder the input as we copy it to the output buffer
        unsafe { self.copy_from_input(input, output) };

        // perform the first of two inner FFTs
        self.inner_fft.process(output, input);

        // multiply the inner result with our cached setup data
        // also conjugate every entry. this sets us up to do an inverse FFT
        // (because an inverse FFT is equivalent to a normal FFT where you conjugate both the inputs and outputs)
        for ((&input_cell, output_cell), &multiple) in input.iter().zip(output.iter_mut()).zip(self.inner_fft_data.iter()) {
            *output_cell = (input_cell * multiple).conj();
        }

        // execute the second FFT
        self.inner_fft.process(output, input);

        // to finalize the inverse, compute the conjugate of every element
        for element in input.iter_mut() {
            *element = element.conj() + first_input_val;
        }

        unsafe { self.copy_to_output(input, output) };
    }
}

impl<T: FFTnum> FFTAlgorithm<T> for RadersAlgorithm<T> {
    fn process(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        self.perform_fft(input, output);
    }
    fn process_multi(&self, input: &mut [Complex<T>], output: &mut [Complex<T>]) {
        for (in_chunk, out_chunk) in input.chunks_mut(self.len()).zip(output.chunks_mut(self.len())) {
            self.perform_fft(in_chunk, out_chunk);
        }
    }
    fn len(&self) -> usize {
        self.len
    }
}

#[cfg(test)]
mod unit_tests {
    use super::*;
    use std::rc::Rc;
    use num::Zero;
    use test_utils::{random_signal, compare_vectors};
    use algorithm::DFTAlgorithm;

    #[test]
    fn test_raders() {
        for &len in &[3,5,7,11,13] {
            let dft = DFTAlgorithm::new(len, false);
            let inner_fft = Rc::new(DFTAlgorithm::new(len - 1, false));

            let raders_fft = RadersAlgorithm::new(len, inner_fft, false);

            let mut dft_input = random_signal(len);
            let mut raders_input = dft_input.clone();

            let mut expected = vec![Zero::zero(); len];
            dft.process(&mut dft_input, &mut expected);

            let mut actual = vec![Zero::zero(); len];
            raders_fft.process(&mut raders_input, &mut actual);

            println!("Expected: {:?}", expected);
            println!("Actual:   {:?}", actual);

            assert!(compare_vectors(&actual, &expected), "len = {}", len);
        }
    }
}
<|endoftext|>


<commit_before>//! This module serves as the entry point into Xtensis's main binary.

// This file is part of Xtensis.

// This is the Xtensis text editor; it edits text.
// Copyright (C) 2016-2017  The Xtensis Developers

// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see
// <http://www.gnu.org/licenses/>.

extern crate clap;
extern crate xtensis;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_scope;

use slog_scope::logger;
use clap::{App, Arg, ArgMatches, SubCommand};
use xtensis::logging::init_logger;
use xtensis::utils::get_version;

fn retrieve_arguments() -> ArgMatches<'static> {
    App::new("xtensis-core")
        .version(get_version())
        .author("Dom Rodriguez <shymega@shymega.org.uk>")
        .about("Extensible editing: screw limits.")
        .arg(Arg::with_name("verbose")
            .short("v")
            .multiple(true)
            .required(false)
            .help("Set the level of logging verbosity"))
        .subcommand(SubCommand::with_name("spawn")
            .about("Spawn a new instance of xtensis-core.")
            .version(get_version())
            .author("Dom Rodriguez <shymega@shymega.org.uk>"))
        .get_matches()
}

fn main() {
    let cargs = retrieve_arguments();

    init_logger(cargs.clone());

    println!("Hello, XTENSIS!");
}
<commit_msg>Add conditional to test for the "spawn" subcommand<commit_after>//! This module serves as the entry point into Xtensis's main binary.

// This file is part of Xtensis.

// This is the Xtensis text editor; it edits text.
// Copyright (C) 2016-2017  The Xtensis Developers

// This program is free software: you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.

// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see
// <http://www.gnu.org/licenses/>.

extern crate clap;
extern crate xtensis;

#[macro_use]
extern crate slog;
extern crate slog_term;
extern crate slog_scope;

use slog_scope::logger;
use clap::{App, Arg, ArgMatches, SubCommand};
use xtensis::logging::init_logger;
use xtensis::utils::get_version;

fn retrieve_arguments() -> ArgMatches<'static> {
    App::new("xtensis-core")
        .version(get_version())
        .author("Dom Rodriguez <shymega@shymega.org.uk>")
        .about("Extensible editing: screw limits.")
        .arg(Arg::with_name("verbose")
            .short("v")
            .multiple(true)
            .required(false)
            .help("Set the level of logging verbosity"))
        .subcommand(SubCommand::with_name("spawn")
            .about("Spawn a new instance of xtensis-core.")
            .version(get_version())
            .author("Dom Rodriguez <shymega@shymega.org.uk>"))
        .get_matches()
}

fn main() {
    let cargs = retrieve_arguments();

    init_logger(cargs.clone());

    if cargs.subcommand_matches("spawn").is_some() {
        info!(logger(), "xtensis-core starting.. initialising ")
    }

    println!("Hello, XTENSIS!");
}
<|endoftext|>


<commit_before>extern crate coreutils;

use std::env;
use std::io::{Write, stdout};

use coreutils::extra::{OptionalExt};

fn main() {
    let mut stdout = stdout();

    let answer = env::args().skip(1).next();
    if let Some(x) = answer {
        let print = x.as_bytes();
        loop {
            stdout.write(print).try();
        }
    } else {
        loop {
            stdout.write(b"y").try();
        }
    }; // Dafuq, borrowck
}
<commit_msg>Fix yes on Redox<commit_after>extern crate coreutils;

use std::env;
use std::io::{Write, stdout};
use std::thread;

use coreutils::extra::{OptionalExt};

fn main() {
    let mut stdout = stdout();

    let answer = env::args().skip(1).next();
    if let Some(mut x) = answer {
        x.push('\n');
        let print = x.as_bytes();
        loop {
            stdout.write(print).try();
            thread::yield_now();
        }
    } else {
        loop {
            stdout.write(b"y\n").try();
            thread::yield_now();
        }
    }; // Dafuq, borrowck
}
<|endoftext|>