<commit_before>use crypto::digest::Digest;
use crypto::sha1::Sha1;
use num;

pub const LENGTH: usize = 160;

#[derive(Clone,Copy,Debug,Eq,Hash,PartialEq)]
pub struct Address {
    data: [u8; 20]
}

impl Address {
    pub fn for_content(content: &str) -> Address {
        let mut hasher = Sha1::new();
        hasher.input_str(content);
        let mut data = [0; 20];
        hasher.result(&mut data);
        Address {
            data: data
        }
    }

    pub fn from_str(string: &str) -> Address {
        use rustc_serialize::hex::FromHex;
        let bytes = string.from_hex().unwrap();
        let mut data = [0u8; 20];
        for (place, byte) in data.iter_mut().zip(bytes.iter()) {
            *place = *byte;
        }
        Address {
            data: data
        }
    }

    pub fn as_numeric(&self) -> num::BigUint {
        num::BigUint::from_bytes_be(&self.data)
    }

    pub fn distance_from(&self, other: &Self) -> num::BigUint {
        use std::ops::BitXor;
        self.as_numeric().bitxor(other.as_numeric())
    }

    pub fn to_str(&self) -> String {
        use rustc_serialize::hex::ToHex;
        self.data.to_hex()
    }
}

pub trait Addressable {
    fn get_address(&self) -> Address;
}

#[cfg(test)]
mod tests {
    use super::Address;

    #[test]
    fn test_for_content() {
        let address = Address::for_content("some string");
        assert_eq!(address.to_str(), "8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
    }

    #[test]
    fn test_from_str() {
        let address = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        assert_eq!(address.to_str(), "8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
    }

    #[test]
    fn test_as_numeric() {
        use num::bigint::ToBigUint;

        let address = Address::from_str("000000000000000000000000000000000000000f");
        assert_eq!(address.as_numeric(), 15u8.to_biguint().unwrap());
        let address = Address::from_str("00000000000000000000000000000000000000f0");
        assert_eq!(address.as_numeric(), 240u8.to_biguint().unwrap());
    }

    #[test]
    fn test_equal() {
        let a = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        let b = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        assert_eq!(a, b);
    }

    #[test]
    fn test_not_equal() {
        let a = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        let b = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3f");
        assert!(a != b);
    }
}
<commit_msg>Make debug repr of Address readable<commit_after>use crypto::digest::Digest;
use crypto::sha1::Sha1;
use num;
use std::fmt;

pub const LENGTH: usize = 160;

#[derive(Clone,Copy,Eq,Hash,PartialEq)]
pub struct Address {
    data: [u8; 20]
}

impl Address {
    pub fn for_content(content: &str) -> Address {
        let mut hasher = Sha1::new();
        hasher.input_str(content);
        let mut data = [0; 20];
        hasher.result(&mut data);
        Address {
            data: data
        }
    }

    pub fn from_str(string: &str) -> Address {
        use rustc_serialize::hex::FromHex;
        let bytes = string.from_hex().unwrap();
        let mut data = [0u8; 20];
        for (place, byte) in data.iter_mut().zip(bytes.iter()) {
            *place = *byte;
        }
        Address {
            data: data
        }
    }

    pub fn as_numeric(&self) -> num::BigUint {
        num::BigUint::from_bytes_be(&self.data)
    }

    pub fn distance_from(&self, other: &Self) -> num::BigUint {
        use std::ops::BitXor;
        self.as_numeric().bitxor(other.as_numeric())
    }

    pub fn to_str(&self) -> String {
        use rustc_serialize::hex::ToHex;
        self.data.to_hex()
    }
}

pub trait Addressable {
    fn get_address(&self) -> Address;
}

impl fmt::Debug for Address {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Address {{ {} }}", self.to_str())
    }
}

#[cfg(test)]
mod tests {
    use super::Address;

    #[test]
    fn test_for_content() {
        let address = Address::for_content("some string");
        assert_eq!(address.to_str(), "8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
    }

    #[test]
    fn test_from_str() {
        let address = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        assert_eq!(address.to_str(), "8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
    }

    #[test]
    fn test_as_numeric() {
        use num::bigint::ToBigUint;

        let address = Address::from_str("000000000000000000000000000000000000000f");
        assert_eq!(address.as_numeric(), 15u8.to_biguint().unwrap());
        let address = Address::from_str("00000000000000000000000000000000000000f0");
        assert_eq!(address.as_numeric(), 240u8.to_biguint().unwrap());
    }

    #[test]
    fn test_equal() {
        let a = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        let b = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        assert_eq!(a, b);
    }

    #[test]
    fn test_not_equal() {
        let a = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3e");
        let b = Address::from_str("8b45e4bd1c6acb88bebf6407d16205f567e62a3f");
        assert!(a != b);
    }
}
<|endoftext|>


<commit_before>use crate::errors::OscError;

use alloc::borrow::ToOwned;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag, take_till1, take, is_a, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, map_parser, opt, recognize, verify};
use nom::multi::{many1, separated_list0, separated_list1};
use nom::sequence::{delimited, pair, preceded, separated_pair};
use nom::{IResult, Parser};
use nom::error::{ErrorKind, ParseError};
use regex::Regex;

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => { parts }
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid")
        };

        Ok(Matcher { pattern: String::from(pattern), pattern_parts })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?;   // TODO: Create an address struct so we don't have to re-check addresses every time we match
        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => match_wildcard(remainder, l.clone(), None),
                            _ => match_wildcard(remainder, l.clone(), Some(next))
                        }
                    } else {
                        match_wildcard(remainder, l.clone(), None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false)  // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        return if remainder.len() == 0 {
            Ok(true)
        } else {
            Ok(false)
        };
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F'
    }
}

/// Parser to turn a choice like '{foo,bar}' into a vector containing the choices, like ["foo", "bar"]
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>>
{
    delimited(char('{'), separated_list1(tag(","), take_while1(is_address_character)), char('}'))(input)
}

/// Parser to recognize a character class like [!a-zA-Z] and return '!a-zA-Z'
fn pattern_character_class(input: &str) -> IResult<&str, &str>
{
    let inner = pair(
        // It is important to read the leading negating '!' to make sure the rest of the parsed
        // character class isn't empty. E.g. '[!]' is not a valid character class.
        recognize(opt(tag("!"))),
        take_while1(is_address_character)
    );

    delimited(char('['), recognize(inner) , char(']'))(input)
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range<'a>(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range;
    if start >= end {
        range = end..=start;
    } else {
        range = start..=end;
    }

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

/// Removes all duplicates from a string of characters
fn deduplicate_characters(input: String) -> String
{
    let mut out: String = String::from("");
    for char in input.chars()
    {
        if !out.contains(char) {
            out.push(char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(satisfy(is_address_character), char('-'), satisfy(is_address_character)).map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| { String::from("-") })
        ))))(input);

        match characters {
            Ok((_, o)) => CharacterClass { negated, characters: deduplicate_characters(o.concat()) },
            _ => { panic!("Invalid character class formatting {}", s) }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent>
{
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character).map(|s: &str| { AddressPatternComponent::Tag(String::from(s)) }),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| { AddressPatternComponent::Tag(c.to_string()) }),
        tag("?").map(|_| { AddressPatternComponent::WildcardSingle }),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| { AddressPatternComponent::Wildcard(x.matches("?").count()) }),
        pattern_choice.map(|choices: Vec<&str>| { AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect()) }),
        pattern_character_class.map(|s: &str| { AddressPatternComponent::CharacterClass(CharacterClass::new(s)) })
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str>
{
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str>
{
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(input: &'a str, character_class: &'a CharacterClass) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &Vec<String>) -> IResult<&'a str, &'a str> {
    for choice in choices
    {
        match tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            Ok((i, o)) => return Ok((i, o)),
            Err(_) => {}
        }
    }
    return Err(nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::Tag)));
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(input: &'a str, minimum_length: usize, next: Option<&AddressPatternComponent>) -> IResult<&'a str, &'a str>
{
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| s.len() >= minimum_length)(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once("/") {
                Some((p, _)) => p,
                None => input
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => match_character_class(substring, cc),
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => panic!("Single wildcard ('?') must not follow wildcard ('*')"),
                    AddressPatternComponent::Wildcard(_) => panic!("Double wildcards must be condensed into one"),
                };

                match result {
                    Ok(_) => longest = i,
                    _ => {}
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError>
{
    match all_consuming::<_,_,nom::error::Error<&str>,_>(
        many1(pair(tag("/"), take_while1(is_address_character)))
    )(input) {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string()))
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(
        alt((
            take_while1(is_address_character),
            tag("?"),
            tag("*"),
            recognize(pattern_choice),
            pattern_character_class,
        ))
    )(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError>
{
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(
            tag("/"),
            address_pattern_part_parser.map(|x| x.concat()),
        )
    ))(input)
    {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string()))
    }
}<commit_msg>Remove unused imports<commit_after>use crate::errors::OscError;

use alloc::string::{String, ToString};
use alloc::vec::Vec;
use nom::branch::alt;
use nom::bytes::complete::{is_not, tag, take, is_a, take_while1};
use nom::character::complete::{char, satisfy};
use nom::combinator::{all_consuming, complete, opt, recognize, verify};
use nom::multi::{many1, separated_list1};
use nom::sequence::{delimited, pair, separated_pair};
use nom::{IResult, Parser};
use nom::error::{ErrorKind, ParseError};

/// With a Matcher OSC method addresses can be [matched](Matcher::match_address) against an OSC address pattern.
/// Refer to the OSC specification for details about OSC address spaces: <http://opensoundcontrol.org/spec-1_0.html#osc-address-spaces-and-osc-addresses>
#[derive(Debug)]
pub struct Matcher {
    pub pattern: String,
    pattern_parts: Vec<AddressPatternComponent>,
}

impl Matcher {
    /// Instantiates a new `Matcher` with the given address pattern.
    /// An error will be returned if the given address pattern is invalid.
    ///
    /// Matcher should be instantiated once per pattern and reused because its construction requires parsing the address pattern which is computationally expensive.
    ///
    /// A valid address pattern begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// OSC defines a couple of rules that look like regular expression but are subtly different:
    ///
    /// - `?` matches a single character
    /// - `*` matches zero or more characters
    /// - `[a-z]` are basically regex [character classes](https://www.regular-expressions.info/charclass.html)
    /// - `{foo,bar}` is an alternative, matching either `foo` or `bar`
    /// - everything else is matched literally
    ///
    /// Refer to the OSC specification for details about address pattern matching: <osc-message-dispatching-and-pattern-matching>.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// Matcher::new("/tempo").expect("valid address");
    /// Matcher::new("").expect_err("address does not start with a slash");
    /// ```
    pub fn new(pattern: &str) -> Result<Self, OscError> {
        verify_address_pattern(pattern)?;
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => { parts }
            // This should never happen because pattern is verified above
            Err(_) => panic!("Address pattern must be valid")
        };

        Ok(Matcher { pattern: String::from(pattern), pattern_parts })
    }

    /// Match an OSC address against an address pattern.
    /// If the address matches the pattern the result will be `true`, otherwise `false`.
    /// An error is returned if the given OSC address is not valid.
    ///
    /// A valid OSC address begins with a `/` and contains at least a method name, e.g. `/tempo`.
    /// Despite OSC address patterns a plain address must not include any of the following characters `#*,/?[]{}`.
    ///
    /// # Examples
    ///
    /// ```
    /// use rosc::address::Matcher;
    ///
    /// let matcher = Matcher::new("/oscillator/[0-9]/{frequency,phase}").unwrap();
    /// assert!(matcher.match_address("/oscillator/1/frequency").unwrap());
    /// assert!(matcher.match_address("/oscillator/8/phase").unwrap());
    /// assert_eq!(matcher.match_address("/oscillator/4/detune").unwrap(), false);
    /// ```
    pub fn match_address(&self, address: &str) -> Result<bool, OscError> {
        verify_address(address)?;   // TODO: Create an address struct so we don't have to re-check addresses every time we match
        // Trivial case
        if address == self.pattern {
            return Ok(true);
        }
        let mut remainder: &str = address;
        // Match the the address component by component
        for (index, part) in self.pattern_parts.as_slice().iter().enumerate() {
            let result = match part {
                AddressPatternComponent::Tag(s) => match_literally(remainder, s.as_str()),
                AddressPatternComponent::WildcardSingle => match_wildcard_single(remainder),
                AddressPatternComponent::Wildcard(l) => {
                    // Check if this is the last pattern component
                    if index < self.pattern_parts.len() - 1 {
                        let next = &self.pattern_parts[index + 1];
                        match next {
                            // If the next component is a '/', there are no more components in the current part and it can be wholly consumed
                            AddressPatternComponent::Tag(s) if s == "/" => match_wildcard(remainder, l.clone(), None),
                            _ => match_wildcard(remainder, l.clone(), Some(next))
                        }
                    } else {
                        match_wildcard(remainder, l.clone(), None)
                    }
                }
                AddressPatternComponent::CharacterClass(cc) => match_character_class(remainder, cc),
                AddressPatternComponent::Choice(s) => match_choice(remainder, s),
            };

            match result {
                Ok((i, _)) => remainder = i,
                Err(_) => return Ok(false)  // Component didn't match, goodbye
            };
        }

        // Address is only matched if it was consumed entirely
        return if remainder.len() == 0 {
            Ok(true)
        } else {
            Ok(false)
        };
    }
}

/// Check whether a character is an allowed address character
/// All printable ASCII characters except for a few special characters are allowed
fn is_address_character(x: char) -> bool {
    match x {
        ' ' | '#' | '*' | ',' | '/' | '?' | '[' | ']' | '{' | '}' => false,
        c => c > '\x20' && c < '\x7F'
    }
}

/// Parser to turn a choice like '{foo,bar}' into a vector containing the choices, like ["foo", "bar"]
fn pattern_choice(input: &str) -> IResult<&str, Vec<&str>>
{
    delimited(char('{'), separated_list1(tag(","), take_while1(is_address_character)), char('}'))(input)
}

/// Parser to recognize a character class like [!a-zA-Z] and return '!a-zA-Z'
fn pattern_character_class(input: &str) -> IResult<&str, &str>
{
    let inner = pair(
        // It is important to read the leading negating '!' to make sure the rest of the parsed
        // character class isn't empty. E.g. '[!]' is not a valid character class.
        recognize(opt(tag("!"))),
        take_while1(is_address_character)
    );

    delimited(char('['), recognize(inner) , char(']'))(input)
}

/// A characters class is defined by a set or range of characters that it matches.
/// For example, [a-z] matches all lowercase alphabetic letters. It can also contain multiple
/// ranges, like [a-zA-Z]. Instead of a range you can also directly provide the characters to match,
/// e.g. [abc123]. You can also combine this with ranges, like [a-z123].
/// If the first characters is an exclamation point, the match is negated, e.g. [!0-9] will match
/// anything except numbers.
#[derive(Debug)]
struct CharacterClass {
    pub negated: bool,
    pub characters: String,
}

/// Expand a character range like 'a-d' to all the letters contained in the range, e.g. 'abcd'
/// This is done by converting the characters to their ASCII values and then getting every ASCII
/// in between.
fn expand_character_range<'a>(first: char, second: char) -> String {
    let start = first as u8;
    let end = second as u8;

    let range;
    if start >= end {
        range = end..=start;
    } else {
        range = start..=end;
    }

    let mut out = String::from("");
    for c in range {
        // For funky ranges like [0-a], some illegal characters are contained
        if is_address_character(c as char) {
            out.push(c as char);
        }
    }
    out
}

/// Removes all duplicates from a string of characters
fn deduplicate_characters(input: String) -> String
{
    let mut out: String = String::from("");
    for char in input.chars()
    {
        if !out.contains(char) {
            out.push(char);
        }
    }
    out
}

impl CharacterClass {
    pub fn new(s: &str) -> Self {
        let mut input = s;
        let negated;
        match char::<_, nom::error::Error<&str>>('!')(input) {
            Ok((i, _)) => {
                negated = true;
                input = i;
            }
            Err(_) => negated = false
        }

        let characters = complete(many1(alt((
            // '!' besides at beginning has no special meaning, but is legal
            char::<_, nom::error::Error<&str>>('!').map(|_| String::from("")),
            // attempt to match a range like a-z or 0-9
            separated_pair(satisfy(is_address_character), char('-'), satisfy(is_address_character)).map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_address_character).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| { String::from("-") })
        ))))(input);

        match characters {
            Ok((_, o)) => CharacterClass { negated, characters: deduplicate_characters(o.concat()) },
            _ => { panic!("Invalid character class formatting {}", s) }
        }
    }
}

#[derive(Debug)]
enum AddressPatternComponent {
    Tag(String),
    Wildcard(usize),
    WildcardSingle,
    CharacterClass(CharacterClass),
    Choice(Vec<String>),
}

fn map_address_pattern_component(input: &str) -> IResult<&str, AddressPatternComponent>
{
    alt((
        // Anything that's alphanumeric gets matched literally
        take_while1(is_address_character).map(|s: &str| { AddressPatternComponent::Tag(String::from(s)) }),
        // Slashes must be seperated into their own tag for the non-greedy implementation of wildcards
        char('/').map(|c: char| { AddressPatternComponent::Tag(c.to_string()) }),
        tag("?").map(|_| { AddressPatternComponent::WildcardSingle }),
        // Combinations of wildcards are a bit tricky.
        // Multiple '*' wildcards in a row are equal to a single '*'.
        // A '*' wildcard followed by any number of '?' wildcards is also equal to '*' but must
        // match at least the same amount of characters as there are '?' wildcards in the combination.
        // For example, '*??' must match at least 2 characters.
        is_a("*?").map(|x: &str| { AddressPatternComponent::Wildcard(x.matches("?").count()) }),
        pattern_choice.map(|choices: Vec<&str>| { AddressPatternComponent::Choice(choices.iter().map(|x| x.to_string()).collect()) }),
        pattern_character_class.map(|s: &str| { AddressPatternComponent::CharacterClass(CharacterClass::new(s)) })
    ))(input)
}

fn match_literally<'a>(input: &'a str, pattern: &str) -> IResult<&'a str, &'a str>
{
    tag(pattern)(input)
}

fn match_wildcard_single(input: &str) -> IResult<&str, &str>
{
    // TODO: this has to be possible with a simpler parser?
    verify(take(1usize), |s: &str| s.chars().all(is_address_character))(input)
}

fn match_character_class<'a>(input: &'a str, character_class: &'a CharacterClass) -> IResult<&'a str, &'a str> {
    if character_class.negated {
        is_not(character_class.characters.as_str())(input)
    } else {
        is_a(character_class.characters.as_str())(input)
    }
}

/// Try all the tags in a choice element
/// Example choice element: '{foo,bar}'
/// It will get parsed into a vector containing the strings "foo" and "bar", which are then matched
fn match_choice<'a>(input: &'a str, choices: &Vec<String>) -> IResult<&'a str, &'a str> {
    for choice in choices
    {
        match tag::<_, _, nom::error::Error<&str>>(choice.as_str())(input) {
            Ok((i, o)) => return Ok((i, o)),
            Err(_) => {}
        }
    }
    return Err(nom::Err::Error(nom::error::Error::from_error_kind(input, ErrorKind::Tag)));
}

/// Match Wildcard '*' by either consuming the rest of the part, or, if it's not the last component
/// in the part, by looking ahead and matching the next component
fn match_wildcard<'a>(input: &'a str, minimum_length: usize, next: Option<&AddressPatternComponent>) -> IResult<&'a str, &'a str>
{
    match next {
        // No next component, consume all allowed characters until end or next '/'
        None => verify(take_while1(is_address_character), |s: &str| s.len() >= minimum_length)(input),
        // There is another element in this part, so logic gets a bit more complicated
        Some(component) => {
            // Wildcards can only match within the current address part, discard the rest
            let address_part = match input.split_once("/") {
                Some((p, _)) => p,
                None => input
            };

            // Attempt to find the latest matching occurrence of the next pattern component
            // This is a greedy wildcard implementation
            let mut longest: usize = 0;
            for i in 0..address_part.len() {
                let (_, substring) = input.split_at(i);
                let result: IResult<_, _, nom::error::Error<&str>> = match component {
                    AddressPatternComponent::Tag(s) => match_literally(substring, s.as_str()),
                    AddressPatternComponent::CharacterClass(cc) => match_character_class(substring, cc),
                    AddressPatternComponent::Choice(s) => match_choice(substring, s),
                    // These two cases are prevented from happening by map_address_pattern_component
                    AddressPatternComponent::WildcardSingle => panic!("Single wildcard ('?') must not follow wildcard ('*')"),
                    AddressPatternComponent::Wildcard(_) => panic!("Double wildcards must be condensed into one"),
                };

                match result {
                    Ok(_) => longest = i,
                    _ => {}
                }
            }
            verify(take(longest), |s: &str| s.len() >= minimum_length)(input)
        }
    }
}

/// Verify that an address is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address;
///
/// match verify_address("/oscillator/1") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address(input: &str) -> Result<(), OscError>
{
    match all_consuming::<_,_,nom::error::Error<&str>,_>(
        many1(pair(tag("/"), take_while1(is_address_character)))
    )(input) {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address".to_string()))
    }
}

/// Parse an address pattern's part until the next '/' or the end
fn address_pattern_part_parser(input: &str) -> IResult<&str, Vec<&str>> {
    many1::<_, _, nom::error::Error<&str>, _>(
        alt((
            take_while1(is_address_character),
            tag("?"),
            tag("*"),
            recognize(pattern_choice),
            pattern_character_class,
        ))
    )(input)
}

/// Verify that an address pattern is valid
///
/// # Examples
/// ```
/// use rosc::address::verify_address_pattern;
///
/// match verify_address_pattern("/oscillator/[0-9]/*") {
///     Ok(()) => println!("Address is valid"),
///     Err(e) => println!("Address is not valid")
/// }
/// ```
pub fn verify_address_pattern(input: &str) -> Result<(), OscError>
{
    match all_consuming(many1(
        // Each part must start with a '/'. This automatically also prevents a trailing '/'
        pair(
            tag("/"),
            address_pattern_part_parser.map(|x| x.concat()),
        )
    ))(input)
    {
        Ok((_)) => Ok(()),
        Err(_) => Err(OscError::BadAddress("Invalid address pattern".to_string()))
    }
}<|endoftext|>


<commit_before>/*!
`ndarray` compability

This adds support for reading data into [`ndarray`][ndarray] array objects. This primarily
enables support for automatically reshaping the resulting arrays to have the dimensionality of
the image that was asked for.

Data is read into the [`ndarray::ArrayD`][arrayd] type. The following methods from
[`FitsHdu`][fits-hdu] are supported:

* [`read_image`][read-image]
* [`read_region`][read-region]
* [`read_row`][read-row]
* [`read_rows`][read-rows]
* [`read_section`][read-section]

## `read_image`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 100);
assert_eq!(dim[1], 100);
assert_eq!(data[[20, 5]], 152);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_region`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 10);
assert_eq!(dim[1], 30);
assert_eq!(data[[5, 10]], 160);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_row`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
assert_eq!(data.ndim(), 1);
assert_eq!(data[20], 156);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_rows`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[1, 52]], 184);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_section`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# use fitsio::errors::Error;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
    Err(Error::Message(msg)) => {
        assert_eq!(
            msg,
            "must request number of pixels exactly divisible by image width".to_string()
        );
    }
    _ => panic!("invalid result"),
}

let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[0, 10]], 160);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

[ndarray]: https://crates.io/crates/ndarray
[arrayd]: https://docs.rs/ndarray/0.11.2/ndarray/type.ArrayD.html
[fits-hdu]: hdu/struct.FitsHdu.html
[read-image]: images/struct.FitsHdu.html#method.read_image
[read-region]: images/struct.FitsHdu.html#method.read_region
[read-row]: images/struct.FitsHdu.html#method.read_row
[read-rows]: images/struct.FitsHdu.html#method.read_rows
[read-section]: images/struct.FitsHdu.html#method.read_section
*/

use std::ops::Range;
use super::{FitsFile, FitsHdu};
use super::fitsfile::ReadImage;
use super::errors::Result;
use super::types::HduInfo;
use ndarray::{Array, ArrayD};

impl<T> ReadImage for ArrayD<T>
where
    T: Clone,
    Vec<T>: ReadImage,
{
    fn read_section(fits_file: &mut FitsFile, hdu: &FitsHdu, range: Range<usize>) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                if shape.len() != 2 {
                    return Err("Only 2D images supported for now".into());
                }

                let width = shape[1];

                if range.start % width != 0 {
                    return Err("range must start on row boundary".into());
                }
                let start_pixel = range.start / width;

                let n_pixels_requested = range.end - range.start;
                if n_pixels_requested % width != 0 {
                    return Err(
                        "must request number of pixels exactly divisible by image width".into(),
                    );
                }

                let n_rows = n_pixels_requested / width;
                ReadImage::read_rows(fits_file, hdu, start_pixel, n_rows)
            }
            HduInfo::TableInfo { .. } => {
                return Err("Cannot read image data from a FITS table".into())
            }
            _ => unreachable!(),
        }
    }

    fn read_rows(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        start_row: usize,
        num_rows: usize,
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_rows(fits_file, hdu, start_row, num_rows)?;
        let arr = Array::from_vec(data);
        let row_length = arr.len() / num_rows;
        Ok(arr.into_shape(vec![num_rows, row_length]).unwrap())
    }

    fn read_row(fits_file: &mut FitsFile, hdu: &FitsHdu, row: usize) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_row(fits_file, hdu, row)?;
        let shape = vec![data.len()];
        Ok(Array::from_shape_vec(shape, data).unwrap())
    }

    fn read_region(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        ranges: &[&Range<usize>],
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_region(fits_file, hdu, ranges)?;
        let shape: Vec<usize> = (0..ranges.len())
            .map(|i| ranges[i].end - ranges[i].start)
            .collect();
        let arr = Array::from_shape_vec(shape, data).unwrap();
        Ok(arr)
    }

    fn read_image(fits_file: &mut FitsFile, hdu: &FitsHdu) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                let data: Vec<T> = ReadImage::read_image(fits_file, hdu)?;
                let shape: Vec<usize> = (0..2).map(|i| shape[i]).collect();
                let arr = Array::from_shape_vec(shape, data).unwrap();
                Ok(arr)
            }
            _ => unreachable!(),
        }
    }
}

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

    #[test]
    fn test_read_image() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 100);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[20, 5]], 152);
    }

    #[test]
    fn test_read_rows() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[1, 52]], 184);
    }

    #[test]
    fn test_read_row() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
        assert_eq!(data.ndim(), 1);
        assert_eq!(data[20], 156);
    }

    #[test]
    fn test_read_region() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 10);
        assert_eq!(dim[1], 30);
        assert_eq!(data[[5, 10]], 160);
    }

    #[test]
    fn test_read_section() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
            Err(Error::Message(msg)) => {
                assert_eq!(
                    msg,
                    "must request number of pixels exactly divisible by image width".to_string()
                );
            }
            _ => panic!("invalid result"),
        }

        let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[0, 10]], 160);
    }
}
<commit_msg>Fix ndarray compat<commit_after>/*!
`ndarray` compability

This adds support for reading data into [`ndarray`][ndarray] array objects. This primarily
enables support for automatically reshaping the resulting arrays to have the dimensionality of
the image that was asked for.

Data is read into the [`ndarray::ArrayD`][arrayd] type. The following methods from
[`FitsHdu`][fits-hdu] are supported:

* [`read_image`][read-image]
* [`read_region`][read-region]
* [`read_row`][read-row]
* [`read_rows`][read-rows]
* [`read_section`][read-section]

## `read_image`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 100);
assert_eq!(dim[1], 100);
assert_eq!(data[[20, 5]], 152);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_region`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 10);
assert_eq!(dim[1], 30);
assert_eq!(data[[5, 10]], 160);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_row`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
assert_eq!(data.ndim(), 1);
assert_eq!(data[20], 156);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_rows`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[1, 52]], 184);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

## `read_section`

```rust
# extern crate fitsio;
# #[cfg(feature = "array")]
# extern crate ndarray;
# use fitsio::FitsFile;
# use fitsio::errors::Error;
# #[cfg(feature = "array")]
# use ndarray::ArrayD;
#
# #[cfg(feature = "array")]
# fn main() {
let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
let hdu = f.primary_hdu().unwrap();

match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
    Err(Error::Message(msg)) => {
        assert_eq!(
            msg,
            "must request number of pixels exactly divisible by image width".to_string()
        );
    }
    _ => panic!("invalid result"),
}

let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
let dim = data.dim();
assert_eq!(data.ndim(), 2);
assert_eq!(dim[0], 2);
assert_eq!(dim[1], 100);
assert_eq!(data[[0, 10]], 160);
# }
#
# #[cfg(not(feature = "array"))]
# fn main() {}
```

[ndarray]: https://crates.io/crates/ndarray
[arrayd]: https://docs.rs/ndarray/0.11.2/ndarray/type.ArrayD.html
[fits-hdu]: hdu/struct.FitsHdu.html
[read-image]: images/struct.FitsHdu.html#method.read_image
[read-region]: images/struct.FitsHdu.html#method.read_region
[read-row]: images/struct.FitsHdu.html#method.read_row
[read-rows]: images/struct.FitsHdu.html#method.read_rows
[read-section]: images/struct.FitsHdu.html#method.read_section
*/

use std::ops::Range;
use fitsfile::FitsFile;
use hdu::{FitsHdu, HduInfo};
use images::ReadImage;
use errors::Result;
use ndarray::{Array, ArrayD};

impl<T> ReadImage for ArrayD<T>
where
    T: Clone,
    Vec<T>: ReadImage,
{
    fn read_section(fits_file: &mut FitsFile, hdu: &FitsHdu, range: Range<usize>) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                if shape.len() != 2 {
                    return Err("Only 2D images supported for now".into());
                }

                let width = shape[1];

                if range.start % width != 0 {
                    return Err("range must start on row boundary".into());
                }
                let start_pixel = range.start / width;

                let n_pixels_requested = range.end - range.start;
                if n_pixels_requested % width != 0 {
                    return Err(
                        "must request number of pixels exactly divisible by image width".into(),
                    );
                }

                let n_rows = n_pixels_requested / width;
                ReadImage::read_rows(fits_file, hdu, start_pixel, n_rows)
            }
            HduInfo::TableInfo { .. } => {
                return Err("Cannot read image data from a FITS table".into())
            }
            _ => unreachable!(),
        }
    }

    fn read_rows(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        start_row: usize,
        num_rows: usize,
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_rows(fits_file, hdu, start_row, num_rows)?;
        let arr = Array::from_vec(data);
        let row_length = arr.len() / num_rows;
        Ok(arr.into_shape(vec![num_rows, row_length]).unwrap())
    }

    fn read_row(fits_file: &mut FitsFile, hdu: &FitsHdu, row: usize) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_row(fits_file, hdu, row)?;
        let shape = vec![data.len()];
        Ok(Array::from_shape_vec(shape, data).unwrap())
    }

    fn read_region(
        fits_file: &mut FitsFile,
        hdu: &FitsHdu,
        ranges: &[&Range<usize>],
    ) -> Result<Self> {
        let data: Vec<T> = ReadImage::read_region(fits_file, hdu, ranges)?;
        let shape: Vec<usize> = (0..ranges.len())
            .map(|i| ranges[i].end - ranges[i].start)
            .collect();
        let arr = Array::from_shape_vec(shape, data).unwrap();
        Ok(arr)
    }

    fn read_image(fits_file: &mut FitsFile, hdu: &FitsHdu) -> Result<Self> {
        match hdu.info {
            HduInfo::ImageInfo { ref shape, .. } => {
                let data: Vec<T> = ReadImage::read_image(fits_file, hdu)?;
                let shape: Vec<usize> = (0..2).map(|i| shape[i]).collect();
                let arr = Array::from_shape_vec(shape, data).unwrap();
                Ok(arr)
            }
            _ => unreachable!(),
        }
    }
}

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

    #[test]
    fn test_read_image() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_image(&mut f).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 100);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[20, 5]], 152);
    }

    #[test]
    fn test_read_rows() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_rows(&mut f, 0, 2).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[1, 52]], 184);
    }

    #[test]
    fn test_read_row() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_row(&mut f, 49).unwrap();
        assert_eq!(data.ndim(), 1);
        assert_eq!(data[20], 156);
    }

    #[test]
    fn test_read_region() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        let data: ArrayD<u32> = hdu.read_region(&mut f, &[&(70..80), &(20..50)]).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 10);
        assert_eq!(dim[1], 30);
        assert_eq!(data[[5, 10]], 160);
    }

    #[test]
    fn test_read_section() {
        let mut f = FitsFile::open("../testdata/full_example.fits").unwrap();
        let hdu = f.primary_hdu().unwrap();

        match hdu.read_section::<ArrayD<u32>>(&mut f, 0, 250) {
            Err(Error::Message(msg)) => {
                assert_eq!(
                    msg,
                    "must request number of pixels exactly divisible by image width".to_string()
                );
            }
            _ => panic!("invalid result"),
        }

        let data: ArrayD<u32> = hdu.read_section(&mut f, 0, 200).unwrap();
        let dim = data.dim();
        assert_eq!(data.ndim(), 2);
        assert_eq!(dim[0], 2);
        assert_eq!(dim[1], 100);
        assert_eq!(data[[0, 10]], 160);
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::cell::RefCell;

use conf::{RemoteBranch};
use constants::{CHANGED_KEY,NEW_KEY,STAGED_KEY,CONFLICTS_KEY};

use git2::*;

struct Cache {
    current_branch_name: RefCell<Option<String>>,
    // TODO: Reference can't be cached (can't be cloned)
    //       implement via OID
    // head: RefCell<Option<Oid>>,
}

pub struct Backend {
    cache: Cache,
    pub repo: Repository,
    pub debug: bool,
}

pub struct BranchAheadBehind {
    pub local_branch_name: Option<String>,
    pub remote_branch_name: Option<String>,
    pub remote_name: Option<String>,
    pub ahead: usize,
    pub behind: usize
}

impl BranchAheadBehind {
    fn new(l: Option<String>) -> BranchAheadBehind {
        BranchAheadBehind{ local_branch_name: l, remote_branch_name: None, remote_name: None,
                           ahead: 0, behind: 0 }
    }
}

#[derive(Clone)]
struct RefPair {
    remote_name: String,
    branch_name: String,
    oid: Oid,
}

impl Cache {
    pub fn new() -> Cache {
        Cache{ current_branch_name: RefCell::new(None) }
    }

    fn set_current_branch_name(&self, n: Option<String>) {
        let mut c = self.current_branch_name.borrow_mut();
        *c = n;
    }

    fn is_current_branch_set(&self) -> bool {
        self.current_branch_name.borrow().is_some()
    }

    fn get_current_branch(&self) -> Option<String> {
        self.current_branch_name.borrow().clone()
    }
}

impl Backend {
    pub fn new(repo: Repository, debug: bool) -> Backend {
        Backend{ repo: repo, debug: debug, cache: Cache::new() }
    }

    fn get_head(&self) -> Option<Reference> {
        match self.repo.head() {
            Ok(head) => Some(head),
            Err(e2) => {
                log!(self, "Can't get HEAD: {}", e2);
                match self.repo.find_reference("HEAD") {
                    Ok(x) => {
                        log!(self, "Found HEAD directly: {:?}", x.name());
                        Some(x)
                    },
                    Err(e) => {
                        log!(self, "reference HEAD: {}", e);
                        return None
                    },
                }
            },
        }
    }

    fn get_current_branch_oid(&self) -> Option<Oid> {
        let head = self.get_head();
        match head {
            Some(v) => v.target(),
            None => {
                log!(self, "Failed to find Oid for HEAD.");
                return None
            },
        }
    }

    fn resolve_symbolic_reference<'a>(&self, reference: Option<Reference<'a>>) -> Option<Reference<'a>> {
        return reference;
        let s_reference = match reference {
            Some(v) => {
                match v.resolve() {
                    Ok(y) => {
                        log!(self, "Branch name is {:?}", y.name());
                        y
                    },
                    Err(e) => {
                        log!(self, "Branch name is {:?}, error: {:?}", v.name(), e);
                        v
                    }
                }
            }
            None => {
                log!(self, "No branch name found");
                return None;
            }
        };
        Some(s_reference)
    }

    fn get_branch_name_for_reference(&self, r: Reference) -> Option<String> {
        if let Some(ref_name_string) = r.shorthand() {
            if ref_name_string != "HEAD" {
                let s = ref_name_string.to_string();
                log!(self, "Shorthand for reference is: {}", s);
                return Some(s);
            } else {
                if let Some(ref_name) = r.symbolic_target() {
                    let ref_name_string = ref_name.to_string();
                    log!(self, "shorthand = HEAD, links to: {}", ref_name_string);
                    let mut path: Vec<&str> = ref_name_string.split('/').collect();
                    if let Some(branch_short) = path.pop() {
                        let s = branch_short.to_string();
                        log!(self, "Last part of full name is: {}", s);
                        return Some(s);
                    }
                }
            }
        }

        // we're getting the commit hash as name
        match r.target() {
            Some(v) => {
                let hash_str = v.to_string();
                if hash_str.len() >= 8 {
                    let (s, _) = hash_str.split_at(7);
                    Some(s.to_string())
                } else {
                    Some(hash_str)
                }
            },
            None => None,
        }
    }

    pub fn get_current_branch_name(&mut self) -> Option<String> {
        if self.cache.is_current_branch_set() {
            return self.cache.get_current_branch();
        }
        let mut head: Option<Reference> = None;
        if head.is_none() {
            head = self.get_head();
        }
        let h = match self.resolve_symbolic_reference(head) {
            Some(v) => v,
            None => return None,
        };
        let current_branch_name = self.get_branch_name_for_reference(h);
        self.cache.set_current_branch_name(current_branch_name.clone());
        current_branch_name
    }

    fn get_branch_remote(&self, reference: Reference) -> Option<RefPair> {
        let b = Branch::wrap(reference);
        let upstream = match b.upstream() {
            Ok(u) => u,
            Err(e) => {
                log!(self, "Can't get upstream branch for {:?}", b.name());
                return None;
            }
        };
        let branch_name = match upstream.name() {
            Ok(o_n) => match o_n {
                Some(n) => n,
                None => {
                    log!(self, "Invalid branch name");
                    return None;
                },
            },
            Err(e) => {
                log!(self, "Error while getting name for upstream branch");
                return None;
            }
        };
        let upstream_reference = upstream.get();
        let oid = match upstream_reference.target() {
            Some(o) => o,
            None => {
                log!(self, "Can't get oid of upstream branch");
                return None;
            }
        };
        let remote_name = match upstream_reference.name() {
            Some(n) => {
                let v: Vec<&str> = n.splitn(3, "/").collect();
                if v.len() >= 3 {
                    v[1]
                } else {
                    log!(self, "Can't figure out remote name: {:?}", v);
                    return None;
                }
            },
            None => {
                log!(self, "Can't get full name of upstream branch.");
                return None;
            }
        };
        Some(RefPair{ remote_name: remote_name.to_string(),
                      branch_name: branch_name.to_string(), oid: oid })
    }

    fn get_current_branch_remote_oid(&self) -> Option<RefPair> {
        match self.get_head() {
            Some(r) => self.get_branch_remote(r),
            None => None
        }
    }

    pub fn get_branch_ahead_behind(&mut self, remote_branch: Option<RemoteBranch>) -> Option<BranchAheadBehind> {
        let current_branch_name = self.get_current_branch_name();
        log!(self, "Current branch name = {:?}", current_branch_name);
        let mut ab = BranchAheadBehind::new(current_branch_name);
        let ref_pair_option = self.get_remote_branch(remote_branch);
        let ref_pair = match ref_pair_option {
            Some(u) => {
                u.clone()
            },
            None => return Some(ab),
        };
        ab.remote_branch_name = Some(ref_pair.branch_name.clone());
        ab.remote_name = Some(ref_pair.remote_name);

        let oid = match self.get_current_branch_oid() {
            Some(r) => r,
            None => return None
        };
        let res = self.repo.graph_ahead_behind(oid, ref_pair.oid);
        match res {
            Ok((a, b)) => {
                ab.ahead = a;
                ab.behind = b;
            },
            Err(e) => {
                log!(self, "Can't get ahead & behind stats for branch {}", ref_pair.branch_name);
            }
        };
        Some(ab)
    }

    // find remote branch if branch_name is specified
    // if not, get remote tracking branch for current branch
    fn get_remote_branch(&self, remote_branch: Option<RemoteBranch>) -> Option<RefPair> {
        match remote_branch {
            Some(b) => {
                match self.repo.find_branch(&b.remote_branch, BranchType::Remote) {
                    Ok(o) => Some(RefPair{ branch_name: b.remote_branch_name,
                                           remote_name: b.remote_name,
                                           oid: o.into_reference().target().unwrap() }),
                    Err(e) => {
                        // don't panic here - it doesn't exist, we don't care
                        log!(self, "No remote branch found for {}", b.remote_branch_name);
                        None
                    }
                }
            },
            None => {
                self.get_current_branch_remote_oid()
            }
        }
    }

    pub fn get_status(&self) -> Option<Statuses> {
        let mut so = StatusOptions::new();
        let mut opts = so.show(StatusShow::IndexAndWorkdir);
        opts.include_untracked(true);
        match self.repo.statuses(Some(&mut opts)) {
            Ok(s) => Some(s),
            Err(e) => {
                log!(self, "Unable to get status of repository: {:?}", e);
                None
            }
        }
    }

    pub fn get_repository_state(&self) -> String {
        let state = self.repo.state();
        match state {
            RepositoryState::Clean => String::from(""),  // XXX: this seems to be hardcoded
            RepositoryState::Merge => String::from("merge"),
            RepositoryState::Revert | RepositoryState::RevertSequence => String::from("revert"),
            RepositoryState::CherryPick | RepositoryState::CherryPickSequence => String::from("cherry-pick"),
            RepositoryState::Bisect => String::from("bisect"),
            RepositoryState::Rebase |
                RepositoryState::RebaseInteractive |
                RepositoryState::RebaseMerge => String::from("rebase"),
            RepositoryState::ApplyMailbox | RepositoryState::ApplyMailboxOrRebase => String::from("apply"),
        }
    }

    pub fn get_file_status(&self) -> Option<HashMap<&str, u32>> {
        let mut d = HashMap::new();

        let changed = STATUS_WT_MODIFIED | STATUS_WT_DELETED | STATUS_WT_TYPECHANGE | STATUS_WT_RENAMED;
        let staged = STATUS_INDEX_MODIFIED | STATUS_INDEX_DELETED | STATUS_INDEX_TYPECHANGE | STATUS_INDEX_RENAMED | STATUS_INDEX_NEW;

        let statuses = match self.get_status() {
            Some(x) => x,
            None => return None,
        };

        for s in statuses.iter() {
            let file_status = s.status();
            log!(self, "{}", s.path().unwrap());

            if file_status.intersects(changed) {
                let counter = d.entry(CHANGED_KEY).or_insert(0);
                *counter += 1;
            };
            if file_status.contains(STATUS_WT_NEW) {
                let counter = d.entry(NEW_KEY).or_insert(0);
                *counter += 1;
            };
            if file_status.intersects(staged) {
                let counter = d.entry(STAGED_KEY).or_insert(0);
                *counter += 1;
            };
            if file_status.intersects(STATUS_CONFLICTED) {
                let counter = d.entry(CONFLICTS_KEY).or_insert(0);
                *counter += 1;
            };
        }
        Some(d)
    }
}
<commit_msg>I don't even...<commit_after>use std::collections::HashMap;
use std::cell::RefCell;

use conf::{RemoteBranch};
use constants::{CHANGED_KEY,NEW_KEY,STAGED_KEY,CONFLICTS_KEY};

use git2::*;

struct Cache {
    current_branch_name: RefCell<Option<String>>,
    // TODO: Reference can't be cached (can't be cloned)
    //       implement via OID
    // head: RefCell<Option<Oid>>,
}

pub struct Backend {
    cache: Cache,
    pub repo: Repository,
    pub debug: bool,
}

pub struct BranchAheadBehind {
    pub local_branch_name: Option<String>,
    pub remote_branch_name: Option<String>,
    pub remote_name: Option<String>,
    pub ahead: usize,
    pub behind: usize
}

impl BranchAheadBehind {
    fn new(l: Option<String>) -> BranchAheadBehind {
        BranchAheadBehind{ local_branch_name: l, remote_branch_name: None, remote_name: None,
                           ahead: 0, behind: 0 }
    }
}

#[derive(Clone)]
struct RefPair {
    remote_name: String,
    branch_name: String,
    oid: Oid,
}

impl Cache {
    pub fn new() -> Cache {
        Cache{ current_branch_name: RefCell::new(None) }
    }

    fn set_current_branch_name(&self, n: Option<String>) {
        let mut c = self.current_branch_name.borrow_mut();
        *c = n;
    }

    fn is_current_branch_set(&self) -> bool {
        self.current_branch_name.borrow().is_some()
    }

    fn get_current_branch(&self) -> Option<String> {
        self.current_branch_name.borrow().clone()
    }
}

impl Backend {
    pub fn new(repo: Repository, debug: bool) -> Backend {
        Backend{ repo: repo, debug: debug, cache: Cache::new() }
    }

    fn get_head(&self) -> Option<Reference> {
        match self.repo.head() {
            Ok(head) => Some(head),
            Err(e2) => {
                log!(self, "Can't get HEAD: {}", e2);
                match self.repo.find_reference("HEAD") {
                    Ok(x) => {
                        log!(self, "Found HEAD directly: {:?}", x.name());
                        Some(x)
                    },
                    Err(e) => {
                        log!(self, "reference HEAD: {}", e);
                        return None
                    },
                }
            },
        }
    }

    fn get_current_branch_oid(&self) -> Option<Oid> {
        let head = self.get_head();
        match head {
            Some(v) => v.target(),
            None => {
                log!(self, "Failed to find Oid for HEAD.");
                return None
            },
        }
    }

    fn resolve_symbolic_reference<'a>(&self, reference: Option<Reference<'a>>) -> Option<Reference<'a>> {
        let s_reference = match reference {
            Some(v) => {
                match v.resolve() {
                    Ok(y) => {
                        log!(self, "Branch name is {:?}", y.name());
                        y
                    },
                    Err(e) => {
                        log!(self, "Branch name is {:?}, error: {:?}", v.name(), e);
                        v
                    }
                }
            }
            None => {
                log!(self, "No branch name found");
                return None;
            }
        };
        Some(s_reference)
    }

    fn get_branch_name_for_reference(&self, r: Reference) -> Option<String> {
        if let Some(ref_name_string) = r.shorthand() {
            if ref_name_string != "HEAD" {
                let s = ref_name_string.to_string();
                log!(self, "Shorthand for reference is: {}", s);
                return Some(s);
            } else {
                if let Some(ref_name) = r.symbolic_target() {
                    let ref_name_string = ref_name.to_string();
                    log!(self, "shorthand = HEAD, links to: {}", ref_name_string);
                    let mut path: Vec<&str> = ref_name_string.split('/').collect();
                    if let Some(branch_short) = path.pop() {
                        let s = branch_short.to_string();
                        log!(self, "Last part of full name is: {}", s);
                        return Some(s);
                    }
                }
            }
        }

        // we're getting the commit hash as name
        match r.target() {
            Some(v) => {
                let hash_str = v.to_string();
                if hash_str.len() >= 8 {
                    let (s, _) = hash_str.split_at(7);
                    Some(s.to_string())
                } else {
                    Some(hash_str)
                }
            },
            None => None,
        }
    }

    pub fn get_current_branch_name(&mut self) -> Option<String> {
        if self.cache.is_current_branch_set() {
            return self.cache.get_current_branch();
        }
        let mut head: Option<Reference> = None;
        if head.is_none() {
            head = self.get_head();
        }
        let h = match self.resolve_symbolic_reference(head) {
            Some(v) => v,
            None => return None,
        };
        let current_branch_name = self.get_branch_name_for_reference(h);
        self.cache.set_current_branch_name(current_branch_name.clone());
        current_branch_name
    }

    fn get_branch_remote(&self, reference: Reference) -> Option<RefPair> {
        let b = Branch::wrap(reference);
        let upstream = match b.upstream() {
            Ok(u) => u,
            Err(e) => {
                log!(self, "Can't get upstream branch for {:?}", b.name());
                return None;
            }
        };
        let branch_name = match upstream.name() {
            Ok(o_n) => match o_n {
                Some(n) => n,
                None => {
                    log!(self, "Invalid branch name");
                    return None;
                },
            },
            Err(e) => {
                log!(self, "Error while getting name for upstream branch");
                return None;
            }
        };
        let upstream_reference = upstream.get();
        let oid = match upstream_reference.target() {
            Some(o) => o,
            None => {
                log!(self, "Can't get oid of upstream branch");
                return None;
            }
        };
        let remote_name = match upstream_reference.name() {
            Some(n) => {
                let v: Vec<&str> = n.splitn(3, "/").collect();
                if v.len() >= 3 {
                    v[1]
                } else {
                    log!(self, "Can't figure out remote name: {:?}", v);
                    return None;
                }
            },
            None => {
                log!(self, "Can't get full name of upstream branch.");
                return None;
            }
        };
        Some(RefPair{ remote_name: remote_name.to_string(),
                      branch_name: branch_name.to_string(), oid: oid })
    }

    fn get_current_branch_remote_oid(&self) -> Option<RefPair> {
        match self.get_head() {
            Some(r) => self.get_branch_remote(r),
            None => None
        }
    }

    pub fn get_branch_ahead_behind(&mut self, remote_branch: Option<RemoteBranch>) -> Option<BranchAheadBehind> {
        let current_branch_name = self.get_current_branch_name();
        log!(self, "Current branch name = {:?}", current_branch_name);
        let mut ab = BranchAheadBehind::new(current_branch_name);
        let ref_pair_option = self.get_remote_branch(remote_branch);
        let ref_pair = match ref_pair_option {
            Some(u) => {
                u.clone()
            },
            None => return Some(ab),
        };
        ab.remote_branch_name = Some(ref_pair.branch_name.clone());
        ab.remote_name = Some(ref_pair.remote_name);

        let oid = match self.get_current_branch_oid() {
            Some(r) => r,
            None => return None
        };
        let res = self.repo.graph_ahead_behind(oid, ref_pair.oid);
        match res {
            Ok((a, b)) => {
                ab.ahead = a;
                ab.behind = b;
            },
            Err(e) => {
                log!(self, "Can't get ahead & behind stats for branch {}", ref_pair.branch_name);
            }
        };
        Some(ab)
    }

    // find remote branch if branch_name is specified
    // if not, get remote tracking branch for current branch
    fn get_remote_branch(&self, remote_branch: Option<RemoteBranch>) -> Option<RefPair> {
        match remote_branch {
            Some(b) => {
                match self.repo.find_branch(&b.remote_branch, BranchType::Remote) {
                    Ok(o) => Some(RefPair{ branch_name: b.remote_branch_name,
                                           remote_name: b.remote_name,
                                           oid: o.into_reference().target().unwrap() }),
                    Err(e) => {
                        // don't panic here - it doesn't exist, we don't care
                        log!(self, "No remote branch found for {}", b.remote_branch_name);
                        None
                    }
                }
            },
            None => {
                self.get_current_branch_remote_oid()
            }
        }
    }

    pub fn get_status(&self) -> Option<Statuses> {
        let mut so = StatusOptions::new();
        let mut opts = so.show(StatusShow::IndexAndWorkdir);
        opts.include_untracked(true);
        match self.repo.statuses(Some(&mut opts)) {
            Ok(s) => Some(s),
            Err(e) => {
                log!(self, "Unable to get status of repository: {:?}", e);
                None
            }
        }
    }

    pub fn get_repository_state(&self) -> String {
        let state = self.repo.state();
        match state {
            RepositoryState::Clean => String::from(""),  // XXX: this seems to be hardcoded
            RepositoryState::Merge => String::from("merge"),
            RepositoryState::Revert | RepositoryState::RevertSequence => String::from("revert"),
            RepositoryState::CherryPick | RepositoryState::CherryPickSequence => String::from("cherry-pick"),
            RepositoryState::Bisect => String::from("bisect"),
            RepositoryState::Rebase |
                RepositoryState::RebaseInteractive |
                RepositoryState::RebaseMerge => String::from("rebase"),
            RepositoryState::ApplyMailbox | RepositoryState::ApplyMailboxOrRebase => String::from("apply"),
        }
    }

    pub fn get_file_status(&self) -> Option<HashMap<&str, u32>> {
        let mut d = HashMap::new();

        let changed = STATUS_WT_MODIFIED | STATUS_WT_DELETED | STATUS_WT_TYPECHANGE | STATUS_WT_RENAMED;
        let staged = STATUS_INDEX_MODIFIED | STATUS_INDEX_DELETED | STATUS_INDEX_TYPECHANGE | STATUS_INDEX_RENAMED | STATUS_INDEX_NEW;

        let statuses = match self.get_status() {
            Some(x) => x,
            None => return None,
        };

        for s in statuses.iter() {
            let file_status = s.status();
            log!(self, "{}", s.path().unwrap());

            if file_status.intersects(changed) {
                let counter = d.entry(CHANGED_KEY).or_insert(0);
                *counter += 1;
            };
            if file_status.contains(STATUS_WT_NEW) {
                let counter = d.entry(NEW_KEY).or_insert(0);
                *counter += 1;
            };
            if file_status.intersects(staged) {
                let counter = d.entry(STAGED_KEY).or_insert(0);
                *counter += 1;
            };
            if file_status.intersects(STATUS_CONFLICTED) {
                let counter = d.entry(CONFLICTS_KEY).or_insert(0);
                *counter += 1;
            };
        }
        Some(d)
    }
}
<|endoftext|>


<commit_before>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) |
            Err(Ignored(s)) |
            Err(Mismatch(s)) |
            Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, ref_mode: RefMode) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => if ref_mode.is_ref() { ok("str") } else { ok("String") },
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    }
                    else {
                        ok("std::path::PathBuf")
                    }
                }
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode)
                .map_any(|_| alias.name.clone())
        }
        Enumeration(..) |
            Bitfield(..) => Ok(type_.get_name().to_owned()),
        Record(..) |
            Class(..) |
            Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            }
            else {
                Ok(name)
            }
        }
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) |
                    Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE
        && !implemented_in_main_namespace(&env.library, type_id) {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| format!("{}::{}",
            env.namespaces[type_id.ns_id].higher_crate_name, s));
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => rust_type = rust_type.map_any(|s| format!("&{}", s)),
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer
                | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
                           direction: library::ParameterDirection, nullable: Nullable,
                           ref_mode: RefMode) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
                }
            } else {
                rust_type.map_any(|s| format_parameter(s, direction))
            }
        }
        Alias(ref alias) => {
            rust_type.and_then(|s| {
                parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                    .map_any(|_| s)
            })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) |
            Bitfield(..) => rust_type.map_any(|s| format_parameter(s, direction)),

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Out |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) |
            SList(..) |
            CArray(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    if library.namespace(library::MAIN_NAMESPACE).name != "Gtk" {
        return false;
    }
    match &*type_id.full_name(library) {
        "Gdk.Rectangle" => true,
        "GLib.Error" => true,
        _ => false,
    }
}
<commit_msg>Assume (for now?) Error is reexported in all crates<commit_after>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) |
            Err(Ignored(s)) |
            Err(Mismatch(s)) |
            Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, ref_mode: RefMode) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => if ref_mode.is_ref() { ok("str") } else { ok("String") },
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    }
                    else {
                        ok("std::path::PathBuf")
                    }
                }
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode)
                .map_any(|_| alias.name.clone())
        }
        Enumeration(..) |
            Bitfield(..) => Ok(type_.get_name().to_owned()),
        Record(..) |
            Class(..) |
            Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            }
            else {
                Ok(name)
            }
        }
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) |
                    Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE
        && !implemented_in_main_namespace(&env.library, type_id) {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| format!("{}::{}",
            env.namespaces[type_id.ns_id].higher_crate_name, s));
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => rust_type = rust_type.map_any(|s| format!("&{}", s)),
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer
                | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
                           direction: library::ParameterDirection, nullable: Nullable,
                           ref_mode: RefMode) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
                }
            } else {
                rust_type.map_any(|s| format_parameter(s, direction))
            }
        }
        Alias(ref alias) => {
            rust_type.and_then(|s| {
                parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                    .map_any(|_| s)
            })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) |
            Bitfield(..) => rust_type.map_any(|s| format_parameter(s, direction)),

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Out |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) |
            SList(..) |
            CArray(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    match &*type_id.full_name(library) {
        "GLib.Error" => return true,
        _ => (),
    }
    if library.namespace(library::MAIN_NAMESPACE).name != "Gtk" {
        return false;
    }
    match &*type_id.full_name(library) {
        "Gdk.Rectangle" => true,
        _ => false,
    }
}
<|endoftext|>