<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};
use nom::character::complete::{alphanumeric1, char, satisfy};
use nom::combinator::{all_consuming, complete, map_parser, verify};
use nom::multi::{many1, separated_list0};
use nom::sequence::{delimited, 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> {
        let pattern_parts = match all_consuming(many1(get_address_pattern_components))(pattern) {
            Ok((_, parts)) => { parts }
            Err(_) => panic!("Address 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> {
        let mut remainder: &str = address;
        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 Err(OscError::Unimplemented) // TODO
            };
        }
        return if remainder.len() == 0 {
            Ok(true)
        } else {
            Err(OscError::Unimplemented)     // TODO
        };
    }
}

fn map_alternative(s: &str) -> String {
    wrap_with(&s.replace(',', "|"), "(", ")")
}

fn wrap_with(s: &str, pre: &str, post: &str) -> String {
    pre.to_string() + s + post
}

fn map_wildcard(_: &str) -> String {
    r"\w*".into()
}

fn map_question_mark(_: &str) -> String {
    r"\w?".into()
}

fn parse_address_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \t\r\n#*,/?[]{}".contains(c)))(input)
}

fn parse_address_pattern_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \n\t\r/".contains(c)))(input)
}

// Translate OSC pattern rules into an regular expression.
// A pattern part can contain more than one rule, e.g. `{voice,synth}-[1-9]` contains two rules, an alternative and a number range.
fn parse_pattern_part(input: &str) -> IResult<&str, String> {
    many1(alt((
        delimited(char('{'), is_not("}"), char('}')).map(map_alternative),
        delimited(tag("[!"), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[^", "]")),
        delimited(char('['), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[", "]")),
        tag("*").map(map_wildcard),
        tag("?").map(map_question_mark),
        is_not("[{").map(|s: &str| s.to_owned()),
    )))(input)
    .map(|(input, parts)| (input, parts.concat()))
}

fn parse_address_pattern(input: &str) -> Result<Vec<Regex>, OscError> {
    let (_, patterns) = all_consuming(many1(map_parser(
        parse_address_pattern_part,
        parse_pattern_part,
    )))(input)
    .map_err(|_| OscError::BadAddressPattern("bad address pattern".to_string()))?;
    patterns
        .iter()
        .map(|p| Regex::new(p))
        .collect::<Result<Vec<Regex>, regex::Error>>()
        .map_err(|err| OscError::RegexError(err.to_string()))
}

#[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'
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 {
        out.push(c as char);
    }
    // TODO: for funky but legal ranges like '0-a' we need to clean up the ranges
    out
}

fn is_alphanumeric_char(x: char) -> bool {
    x.is_alphanumeric()
}

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_alphanumeric_char), char('-'), satisfy(is_alphanumeric_char)).map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_alphanumeric_char).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| { String::from("-") })
        ))))(input);

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

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

fn get_address_pattern_components(input: &str) -> IResult<&str, AddressPatternComponent>
{
    alt((
        // Anything that's alphanumeric gets matched literally
        alphanumeric1.map(|s: &str| { AddressPatternComponent::Tag(String::from(s)) }), // TODO: this should be all printable ascii characters, not just alphanum!
        // 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()) }),
        map_parser(
            delimited(char('{'), is_not("}"), char('}')),
            separated_list0(char(','.into()), alphanumeric1),
        ).map(|alternatives: Vec<&str>| { AddressPatternComponent::Choice(alternatives.iter().map(|x| x.to_string()).collect()) }),
        delimited(char('['), is_not("]") /*TODO: Only allowed: alphanum and '!-' */, char(']')).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(char::is_alphanumeric))(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 alphanumeric characters until end or next '/'
        None => verify(alphanumeric1, |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 get_address_pattern_part
                    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)
        }
    }
}<commit_msg>Improve naming<commit_after>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};
use nom::character::complete::{alphanumeric1, char, satisfy};
use nom::combinator::{all_consuming, complete, map_parser, verify};
use nom::multi::{many1, separated_list0};
use nom::sequence::{delimited, 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> {
        let pattern_parts = match all_consuming(many1(map_address_pattern_component))(pattern) {
            Ok((_, parts)) => { parts }
            Err(_) => panic!("Address 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> {
        let mut remainder: &str = address;
        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 Err(OscError::Unimplemented) // TODO
            };
        }
        return if remainder.len() == 0 {
            Ok(true)
        } else {
            Err(OscError::Unimplemented)     // TODO
        };
    }
}

fn map_alternative(s: &str) -> String {
    wrap_with(&s.replace(',', "|"), "(", ")")
}

fn wrap_with(s: &str, pre: &str, post: &str) -> String {
    pre.to_string() + s + post
}

fn map_wildcard(_: &str) -> String {
    r"\w*".into()
}

fn map_question_mark(_: &str) -> String {
    r"\w?".into()
}

fn parse_address_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \t\r\n#*,/?[]{}".contains(c)))(input)
}

fn parse_address_pattern_part(input: &str) -> IResult<&str, &str> {
    preceded(char('/'), take_till1(|c| " \n\t\r/".contains(c)))(input)
}

// Translate OSC pattern rules into an regular expression.
// A pattern part can contain more than one rule, e.g. `{voice,synth}-[1-9]` contains two rules, an alternative and a number range.
fn parse_pattern_part(input: &str) -> IResult<&str, String> {
    many1(alt((
        delimited(char('{'), is_not("}"), char('}')).map(map_alternative),
        delimited(tag("[!"), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[^", "]")),
        delimited(char('['), is_not("]"), char(']')).map(|s: &str| wrap_with(s, "[", "]")),
        tag("*").map(map_wildcard),
        tag("?").map(map_question_mark),
        is_not("[{").map(|s: &str| s.to_owned()),
    )))(input)
    .map(|(input, parts)| (input, parts.concat()))
}

fn parse_address_pattern(input: &str) -> Result<Vec<Regex>, OscError> {
    let (_, patterns) = all_consuming(many1(map_parser(
        parse_address_pattern_part,
        parse_pattern_part,
    )))(input)
    .map_err(|_| OscError::BadAddressPattern("bad address pattern".to_string()))?;
    patterns
        .iter()
        .map(|p| Regex::new(p))
        .collect::<Result<Vec<Regex>, regex::Error>>()
        .map_err(|err| OscError::RegexError(err.to_string()))
}

#[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'
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 {
        out.push(c as char);
    }
    // TODO: for funky but legal ranges like '0-a' we need to clean up the ranges
    out
}

fn is_alphanumeric_char(x: char) -> bool {
    x.is_alphanumeric()
}

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_alphanumeric_char), char('-'), satisfy(is_alphanumeric_char)).map(|(first, second)| expand_character_range(first, second)),
            // Match characters literally
            satisfy(is_alphanumeric_char).map(|x| x.to_string()),
            // Trailing dash
            char('-').map(|_| { String::from("-") })
        ))))(input);

        match characters {
            // TODO: Deduplicate?
            Ok((_, o)) => CharacterClass { negated, 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
        alphanumeric1.map(|s: &str| { AddressPatternComponent::Tag(String::from(s)) }), // TODO: this should be all printable ascii characters, not just alphanum!
        // 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()) }),
        map_parser(
            delimited(char('{'), is_not("}"), char('}')),
            separated_list0(char(','.into()), alphanumeric1),
        ).map(|alternatives: Vec<&str>| { AddressPatternComponent::Choice(alternatives.iter().map(|x| x.to_string()).collect()) }),
        delimited(char('['), is_not("]") /*TODO: Only allowed: alphanum and '!-' */, char(']')).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(char::is_alphanumeric))(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 alphanumeric characters until end or next '/'
        None => verify(alphanumeric1, |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)
        }
    }
}<|endoftext|>


<commit_before>//! Module for storing and manipulating the application state. This state is globally shared
//! via smart pointers.

use std::rc::Rc;
use std::cell::RefCell;

use sounding_base::Sounding;

use errors::*;
use gui::sonde_widgets::SondeWidgets;
use gui::sounding::{DeviceCoords, ScreenCoords, TPCoords, XYCoords};

/// Smart pointer for globally shareable data
pub type AppContextPointer = Rc<RefCell<AppContext>>;

/// Holds the application state. This is a singleton (not enforced) that is shared globally.
pub struct AppContext {
    list: Vec<Sounding>,
    // Lower left and  upper right corners of the bounding box that bounds all the soundings in
    // the list.
    lower_left: XYCoords,
    upper_right: XYCoords,

    currently_displayed: usize,
    widgets: Option<SondeWidgets>,

    // Standard x-y coords
    pub zoom_factor: f32, // Multiply by this after translating
    pub translate_x: f32, // subtract this from x before converting to screen coords.
    pub translate_y: f32, // subtract this from y before converting to screen coords.

    // device dimensions
    pub device_height: i32,
    pub device_width: i32,

    // state of input for left button press and panning.
    pub left_button_press_start: DeviceCoords,
    pub left_button_pressed: bool,
}

impl AppContext {
    /// Create a new instance of AppContext and return a smart pointer to it.
    ///
    /// TODO: add example code that also sets the widgets.
    pub fn new() -> AppContextPointer {
        Rc::new(RefCell::new(AppContext {
            // Data state
            list: vec![],
            lower_left: (0.0, 0.0),
            upper_right: (1.0, 1.0),
            currently_displayed: 0,
            widgets: None,

            // Sounding Area GUI state
            zoom_factor: 1.0,
            translate_x: 0.0,
            translate_y: 0.0,
            device_height: 100,
            device_width: 100,
            left_button_press_start: (0.0, 0.0),
            left_button_pressed: false,
        }))
    }

    pub fn set_widgets(&mut self, widgets: SondeWidgets) {
        self.widgets = Some(widgets);
    }

    pub fn load_data(&mut self, src: &mut Iterator<Item = Sounding>) -> Result<()> {
        use config;

        self.list = src.into_iter().collect();
        self.currently_displayed = 0;

        self.lower_left = (0.45, 0.45);
        self.upper_right = (0.55, 0.55);

        for snd in &self.list {
            for pair in snd.pressure.iter().zip(&snd.temperature).filter_map(|p| {
                if let (Some(p), Some(t)) = (p.0.as_option(), p.1.as_option()) {
                    if p < config::MINP { None } else { Some((t, p)) }
                } else {
                    None
                }
            })
            {
                let (x, y) = Self::convert_tp_to_xy(pair);
                if x < self.lower_left.0 {
                    self.lower_left.0 = x;
                }
                if y < self.lower_left.1 {
                    self.lower_left.1 = y;
                }
                if x > self.upper_right.0 {
                    self.upper_right.0 = x;
                }
                if y > self.upper_right.1 {
                    self.upper_right.1 = y;
                }
            }

            // TODO: Once you get surface temperature, make a station pressure-surface temp
            // point and throw that in the mix.
            // if let Some(p) = snd.station_pres.as_option() {

            // }

            for pair in snd.pressure.iter().zip(&snd.dew_point).filter_map(|p| {
                if let (Some(p), Some(t)) = (p.0.as_option(), p.1.as_option()) {
                    if p < config::MINP { None } else { Some((t, p)) }
                } else {
                    None
                }
            })
            {
                let (x, y) = Self::convert_tp_to_xy(pair);
                if x < self.lower_left.0 {
                    self.lower_left.0 = x;
                }
                if y < self.lower_left.1 {
                    self.lower_left.1 = y;
                }
                if x > self.upper_right.0 {
                    self.upper_right.0 = x;
                }
                if y > self.upper_right.1 {
                    self.upper_right.1 = y;
                }
            }
        }

        if let Some(ref wdgs) = self.widgets {
            wdgs.draw_all();
        }

        Ok(())
    }

    /// Is there any data to plot?
    pub fn plottable(&self) -> bool {
        !self.list.is_empty()
    }

    /// Set the next one as the one to display, or wrap to the beginning.
    pub fn display_next(&mut self) {
        if self.plottable() {
            if self.currently_displayed < self.list.len() - 1 {
                self.currently_displayed += 1;
            } else {
                self.currently_displayed = 0;
            }
        }

        if let Some(ref wdgs) = self.widgets {
            wdgs.draw_all();
        }
    }

    /// Set the previous one as the one to display, or wrap to the end.
    pub fn display_previous(&mut self) {
        if self.plottable() {
            if self.currently_displayed > 0 {
                self.currently_displayed -= 1;
            } else {
                self.currently_displayed = self.list.len() - 1;
            }
        }

        if let Some(ref wdgs) = self.widgets {
            wdgs.draw_all();
        }
    }

    /// Get the sounding to draw.
    pub fn get_sounding_for_display(&self) -> Option<&Sounding> {
        if self.plottable() {
            Some(&self.list[self.currently_displayed])
        } else {
            None
        }
    }

    /// A scale factor to use when converting from XY to Screen Coordinates.
    /// NOT the same as the zoom factor.
    #[inline]
    pub fn scale_factor(&self) -> f64 {
        ::std::cmp::min(self.device_height, self.device_width) as f64
    }

    /// Conversion from temperature (t) and pressure (p) to (x,y) coords
    #[inline]
    pub fn convert_tp_to_xy(coords: TPCoords) -> XYCoords {
        use config;
        use std::f32;

        let y = (f32::log10(config::MAXP) - f32::log10(coords.1)) /
            (f32::log10(config::MAXP) - f32::log10(config::MINP));

        let x = (coords.0 - config::MINT) / (config::MAXT - config::MINT);

        // do the skew
        let x = x + y;
        (x, y)
    }

    /// Convert device to screen coords
    #[inline]
    pub fn convert_device_to_screen(&self, coords: DeviceCoords) -> ScreenCoords {
        let scale_factor = self.scale_factor();
        (
            coords.0 / scale_factor,
            // Flip y coordinate vertically and translate so origin is upper left corner.
            -(coords.1 / scale_factor) + self.device_height as f64 / scale_factor,
        )
    }

    /// Convert device coords to (x,y) coords
    #[inline]
    pub fn convert_device_to_xy(&self, coords: DeviceCoords) -> XYCoords {
        let screen_coords = self.convert_device_to_screen(coords);
        self.convert_screen_to_xy(screen_coords)
    }

    /// Conversion from  (x,y) coords to temperature and pressure.
    #[inline]
    pub fn convert_xy_to_tp(coords: XYCoords) -> TPCoords {
        use config;
        use std::f32;

        // undo the skew
        let x = coords.0 - coords.1;
        let y = coords.1;

        let t = x * (config::MAXT - config::MINT) + config::MINT;
        let p = 10.0f32.powf(
            f32::log10(config::MAXP) -
                y * (f32::log10(config::MAXP) - f32::log10(config::MINP)),
        );

        (t, p)
    }

    /// Conversion from (x,y) coords to screen coords
    #[inline]
    pub fn convert_xy_to_screen(&self, coords: XYCoords) -> ScreenCoords {

        // Apply translation first
        let x = coords.0 - self.translate_x;
        let y = coords.1 - self.translate_y;

        // Apply scaling
        let x = (self.zoom_factor * x) as f64;
        let y = (self.zoom_factor * y) as f64;
        (x, y)
    }

    /// Conversion from (x,y) coords to screen coords
    #[inline]
    pub fn convert_screen_to_xy(&self, coords: ScreenCoords) -> XYCoords {
        // Screen coords go 0 -> 1 down the y axis and 0 -> aspect_ratio right along the x axis.

        let x = coords.0 as f32 / self.zoom_factor + self.translate_x;
        let y = coords.1 as f32 / self.zoom_factor + self.translate_y;
        (x, y)
    }

    /// Conversion from temperature/pressure to screen coordinates.
    #[inline]
    pub fn convert_tp_to_screen(&self, coords: TPCoords) -> ScreenCoords {
        let xy = Self::convert_tp_to_xy(coords);
        self.convert_xy_to_screen(xy)
    }

    /// Conversion from screen coordinates to temperature, pressure.
    #[inline]
    pub fn convert_screen_to_tp(&self, coords: ScreenCoords) -> TPCoords {
        let xy = self.convert_screen_to_xy(coords);
        Self::convert_xy_to_tp(xy)
    }

    /// Fit to the given x-y max coords.
    #[inline]
    pub fn fit_to_data(&mut self) {
        use std::f32;

        self.translate_x = self.lower_left.0;
        self.translate_y = self.lower_left.1;

        let width = self.upper_right.0 - self.lower_left.0;
        let height = self.upper_right.1 - self.lower_left.1;

        let width_scale = 1.0 / width;
        let height_scale = 1.0 / height;

        // println!("lower_left: {:?}, upper_right: {:?}, width_scale: {}, height_scale: {}",
        // lower_left, upper_right, width_scale, height_scale);

        self.zoom_factor = f32::min(width_scale, height_scale);

        self.bound_view();
    }

    /// Center the skew-t in the view if zoomed out, and if zoomed in don't let it view beyond the
    /// edges of the skew-t.
    pub fn bound_view(&mut self) {

        let bounds = (self.device_width as f64, self.device_height as f64);
        let lower_right = self.convert_device_to_xy(bounds);
        let upper_left = self.convert_device_to_xy((0.0, 0.0));
        let width = lower_right.0 - upper_left.0;
        let height = upper_left.1 - lower_right.1;

        if width <= 1.0 {
            if self.translate_x < 0.0 {
                self.translate_x = 0.0;
            }
            let max_x = 1.0 - width;
            if self.translate_x > max_x {
                self.translate_x = max_x;
            }
        } else {
            self.translate_x = -(width - 1.0) / 2.0;
        }
        if height < 1.0 {
            if self.translate_y < 0.0 {
                self.translate_y = 0.0;
            }
            let max_y = 1.0 - height;
            if self.translate_y > max_y {
                self.translate_y = max_y;
            }
        } else {
            self.translate_y = -(height - 1.0) / 2.0;
        }
    }
}
<commit_msg>Update  comments.<commit_after>//! Module for storing and manipulating the application state. This state is globally shared
//! via smart pointers.

use std::rc::Rc;
use std::cell::RefCell;

use sounding_base::Sounding;

use errors::*;
use gui::sonde_widgets::SondeWidgets;
use gui::sounding::{DeviceCoords, ScreenCoords, TPCoords, XYCoords};

/// Smart pointer for globally shareable data
pub type AppContextPointer = Rc<RefCell<AppContext>>;

/// Holds the application state. This is a singleton (not enforced) that is shared globally.
pub struct AppContext {
    list: Vec<Sounding>,
    // Lower left and  upper right corners of the bounding box that bounds all the soundings in
    // the list.
    lower_left: XYCoords,
    upper_right: XYCoords,

    currently_displayed: usize,
    widgets: Option<SondeWidgets>,

    // Standard x-y coords
    pub zoom_factor: f32, // Multiply by this after translating
    pub translate_x: f32, // subtract this from x before converting to screen coords.
    pub translate_y: f32, // subtract this from y before converting to screen coords.

    // device dimensions
    pub device_height: i32,
    pub device_width: i32,

    // state of input for left button press and panning.
    pub left_button_press_start: DeviceCoords,
    pub left_button_pressed: bool,
}

impl AppContext {
    /// Create a new instance of AppContext and return a smart pointer to it.
    ///
    /// Note: It is important at a later time to call set_widgets, otherwise nothing will ever be
    /// drawn on the GUI.
    pub fn new() -> AppContextPointer {
        Rc::new(RefCell::new(AppContext {
            // Data state
            list: vec![],
            lower_left: (0.0, 0.0),
            upper_right: (1.0, 1.0),
            currently_displayed: 0,
            widgets: None,

            // Sounding Area GUI state
            zoom_factor: 1.0,
            translate_x: 0.0,
            translate_y: 0.0,
            device_height: 100,
            device_width: 100,
            left_button_press_start: (0.0, 0.0),
            left_button_pressed: false,
        }))
    }

    pub fn set_widgets(&mut self, widgets: SondeWidgets) {
        self.widgets = Some(widgets);
    }

    pub fn load_data(&mut self, src: &mut Iterator<Item = Sounding>) -> Result<()> {
        use config;

        self.list = src.into_iter().collect();
        self.currently_displayed = 0;

        self.lower_left = (0.45, 0.45);
        self.upper_right = (0.55, 0.55);

        for snd in &self.list {
            for pair in snd.pressure.iter().zip(&snd.temperature).filter_map(|p| {
                if let (Some(p), Some(t)) = (p.0.as_option(), p.1.as_option()) {
                    if p < config::MINP { None } else { Some((t, p)) }
                } else {
                    None
                }
            })
            {
                let (x, y) = Self::convert_tp_to_xy(pair);
                if x < self.lower_left.0 {
                    self.lower_left.0 = x;
                }
                if y < self.lower_left.1 {
                    self.lower_left.1 = y;
                }
                if x > self.upper_right.0 {
                    self.upper_right.0 = x;
                }
                if y > self.upper_right.1 {
                    self.upper_right.1 = y;
                }
            }

            // TODO: Once you get surface temperature, make a station pressure-surface temp
            // point and throw that in the mix.
            // if let Some(p) = snd.station_pres.as_option() {

            // }

            for pair in snd.pressure.iter().zip(&snd.dew_point).filter_map(|p| {
                if let (Some(p), Some(t)) = (p.0.as_option(), p.1.as_option()) {
                    if p < config::MINP { None } else { Some((t, p)) }
                } else {
                    None
                }
            })
            {
                let (x, y) = Self::convert_tp_to_xy(pair);
                if x < self.lower_left.0 {
                    self.lower_left.0 = x;
                }
                if y < self.lower_left.1 {
                    self.lower_left.1 = y;
                }
                if x > self.upper_right.0 {
                    self.upper_right.0 = x;
                }
                if y > self.upper_right.1 {
                    self.upper_right.1 = y;
                }
            }
        }

        if let Some(ref wdgs) = self.widgets {
            wdgs.draw_all();
        }

        Ok(())
    }

    /// Is there any data to plot?
    pub fn plottable(&self) -> bool {
        !self.list.is_empty()
    }

    /// Set the next one as the one to display, or wrap to the beginning.
    pub fn display_next(&mut self) {
        if self.plottable() {
            if self.currently_displayed < self.list.len() - 1 {
                self.currently_displayed += 1;
            } else {
                self.currently_displayed = 0;
            }
        }

        if let Some(ref wdgs) = self.widgets {
            wdgs.draw_all();
        }
    }

    /// Set the previous one as the one to display, or wrap to the end.
    pub fn display_previous(&mut self) {
        if self.plottable() {
            if self.currently_displayed > 0 {
                self.currently_displayed -= 1;
            } else {
                self.currently_displayed = self.list.len() - 1;
            }
        }

        if let Some(ref wdgs) = self.widgets {
            wdgs.draw_all();
        }
    }

    /// Get the sounding to draw.
    pub fn get_sounding_for_display(&self) -> Option<&Sounding> {
        if self.plottable() {
            Some(&self.list[self.currently_displayed])
        } else {
            None
        }
    }

    /// A scale factor to use when converting from XY to Screen Coordinates.
    /// NOT the same as the zoom factor.
    #[inline]
    pub fn scale_factor(&self) -> f64 {
        ::std::cmp::min(self.device_height, self.device_width) as f64
    }

    /// Conversion from temperature (t) and pressure (p) to (x,y) coords
    #[inline]
    pub fn convert_tp_to_xy(coords: TPCoords) -> XYCoords {
        use config;
        use std::f32;

        let y = (f32::log10(config::MAXP) - f32::log10(coords.1)) /
            (f32::log10(config::MAXP) - f32::log10(config::MINP));

        let x = (coords.0 - config::MINT) / (config::MAXT - config::MINT);

        // do the skew
        let x = x + y;
        (x, y)
    }

    /// Convert device to screen coords
    #[inline]
    pub fn convert_device_to_screen(&self, coords: DeviceCoords) -> ScreenCoords {
        let scale_factor = self.scale_factor();
        (
            coords.0 / scale_factor,
            // Flip y coordinate vertically and translate so origin is upper left corner.
            -(coords.1 / scale_factor) + self.device_height as f64 / scale_factor,
        )
    }

    /// Convert device coords to (x,y) coords
    #[inline]
    pub fn convert_device_to_xy(&self, coords: DeviceCoords) -> XYCoords {
        let screen_coords = self.convert_device_to_screen(coords);
        self.convert_screen_to_xy(screen_coords)
    }

    /// Conversion from  (x,y) coords to temperature and pressure.
    #[inline]
    pub fn convert_xy_to_tp(coords: XYCoords) -> TPCoords {
        use config;
        use std::f32;

        // undo the skew
        let x = coords.0 - coords.1;
        let y = coords.1;

        let t = x * (config::MAXT - config::MINT) + config::MINT;
        let p = 10.0f32.powf(
            f32::log10(config::MAXP) -
                y * (f32::log10(config::MAXP) - f32::log10(config::MINP)),
        );

        (t, p)
    }

    /// Conversion from (x,y) coords to screen coords
    #[inline]
    pub fn convert_xy_to_screen(&self, coords: XYCoords) -> ScreenCoords {

        // Apply translation first
        let x = coords.0 - self.translate_x;
        let y = coords.1 - self.translate_y;

        // Apply scaling
        let x = (self.zoom_factor * x) as f64;
        let y = (self.zoom_factor * y) as f64;
        (x, y)
    }

    /// Conversion from (x,y) coords to screen coords
    #[inline]
    pub fn convert_screen_to_xy(&self, coords: ScreenCoords) -> XYCoords {
        // Screen coords go 0 -> 1 down the y axis and 0 -> aspect_ratio right along the x axis.

        let x = coords.0 as f32 / self.zoom_factor + self.translate_x;
        let y = coords.1 as f32 / self.zoom_factor + self.translate_y;
        (x, y)
    }

    /// Conversion from temperature/pressure to screen coordinates.
    #[inline]
    pub fn convert_tp_to_screen(&self, coords: TPCoords) -> ScreenCoords {
        let xy = Self::convert_tp_to_xy(coords);
        self.convert_xy_to_screen(xy)
    }

    /// Conversion from screen coordinates to temperature, pressure.
    #[inline]
    pub fn convert_screen_to_tp(&self, coords: ScreenCoords) -> TPCoords {
        let xy = self.convert_screen_to_xy(coords);
        Self::convert_xy_to_tp(xy)
    }

    /// Fit to the given x-y max coords.
    #[inline]
    pub fn fit_to_data(&mut self) {
        use std::f32;

        self.translate_x = self.lower_left.0;
        self.translate_y = self.lower_left.1;

        let width = self.upper_right.0 - self.lower_left.0;
        let height = self.upper_right.1 - self.lower_left.1;

        let width_scale = 1.0 / width;
        let height_scale = 1.0 / height;

        // println!("lower_left: {:?}, upper_right: {:?}, width_scale: {}, height_scale: {}",
        // lower_left, upper_right, width_scale, height_scale);

        self.zoom_factor = f32::min(width_scale, height_scale);

        self.bound_view();
    }

    /// Center the skew-t in the view if zoomed out, and if zoomed in don't let it view beyond the
    /// edges of the skew-t.
    pub fn bound_view(&mut self) {

        let bounds = (self.device_width as f64, self.device_height as f64);
        let lower_right = self.convert_device_to_xy(bounds);
        let upper_left = self.convert_device_to_xy((0.0, 0.0));
        let width = lower_right.0 - upper_left.0;
        let height = upper_left.1 - lower_right.1;

        if width <= 1.0 {
            if self.translate_x < 0.0 {
                self.translate_x = 0.0;
            }
            let max_x = 1.0 - width;
            if self.translate_x > max_x {
                self.translate_x = max_x;
            }
        } else {
            self.translate_x = -(width - 1.0) / 2.0;
        }
        if height < 1.0 {
            if self.translate_y < 0.0 {
                self.translate_y = 0.0;
            }
            let max_y = 1.0 - height;
            if self.translate_y > max_y {
                self.translate_y = max_y;
            }
        } else {
            self.translate_y = -(height - 1.0) / 2.0;
        }
    }
}
<|endoftext|>


<commit_before>//! A TCP socket implementation of Hamelin.
#![feature(io, net)]
extern crate hamelin;
extern crate mio;

use std::collections::HashMap;
use std::env::args;
use std::io::ErrorKind::TimedOut;
use hamelin::{AsyncBufStream, Hamelin, HamelinGuard};
use mio::*;
use mio::net::tcp::{v4, TcpStream, TcpListener};

const SERVER: Token = Token(0);

struct Client {
    stream: AsyncBufStream<NonBlock<TcpStream>>,
    guard: HamelinGuard,
}

impl Client {
    fn new(sock: NonBlock<TcpStream>, guard: HamelinGuard) -> Client {
        Client {
            stream: AsyncBufStream::new(sock),
            guard: guard
        }
    }

    fn wait(&mut self) -> Result<(), ()> {
        self.guard.wait().map_err(|_| ())
    }

    fn read(&mut self) -> Result<(), ()> {
        match self.stream.read_line() {
            Ok(line) => match self.guard.write_line(&line) {
                Ok(_) => Ok(()),
                Err(ref e) if e.kind() == TimedOut => Ok(()),
                Err(_) => Err(()),
            },
            Err(ref e) if e.kind() == TimedOut => Ok(()),
            Err(_) => Err(()),
        }
    }

    fn write(&mut self) -> Result<(), ()> {
        match self.guard.read_line() {
            Ok(line) => match self.stream.write_line(&line) {
                Ok(_) => {
                    Ok(())
                },
                Err(ref e) if e.kind() == TimedOut => Ok(()),
                Err(_) => Err(()),
            },
            Err(ref e) if e.kind() == TimedOut => Ok(()),
            Err(_) => Err(()),
        }
    }
}

struct HamelinHandler {
    server: NonBlock<TcpListener>,
    hamelin: Hamelin,
    token_index: usize,
    clients: HashMap<usize, Client>,
}

impl HamelinHandler {
    fn new(server: NonBlock<TcpListener>, hamelin: Hamelin) -> HamelinHandler {
        HamelinHandler {
            server: server,
            hamelin: hamelin,
            token_index: 1,
            clients: HashMap::new(),
        }
    }

    fn accept(&mut self, eloop: &mut EventLoop<HamelinHandler>) {
        if let Ok(Some(client)) = self.server.accept() {
            let token = mio::Token(self.token_index);
            self.token_index += 1;
            eloop.register_opt(&client, token, Interest::all(), PollOpt::level())
                 .ok().expect("Failed to accept new client.");
            self.clients.insert(
                token.as_usize(), Client::new(client, self.hamelin.spawn().unwrap())
            );
            println!("Client connected.");
        }
    }

    fn read(&mut self, token: usize) -> Result<(), ()> {
        let client = &mut self.clients[token];
        let res = client.read();
        res
    }

    fn write(&mut self, token: usize) -> Result<(), ()> {
        if let Some(ref mut client) = self.clients.get_mut(&token) {
            let res = client.write();
            res
        } else {
            Err(())
        }
    }
}

impl Handler for HamelinHandler {
    type Timeout = ();
    type Message = ();

    fn readable(&mut self, eloop: &mut EventLoop<HamelinHandler>, token: Token, _: ReadHint) {
        match token {
            SERVER => self.accept(eloop),
            Token(x) => {
                if let Err(_) = self.read(x) {
                    eloop.deregister(&self.clients[x].stream.stream).unwrap();
                    let _ = self.clients[x].wait();
                    self.clients.remove(&x);
                }
            },
        }
    }

    fn writable(&mut self, _: &mut EventLoop<HamelinHandler>, token: Token) {
        match token {
            SERVER => (),
            Token(x) => {
                let _ = self.write(x);
            }
        }
    }
}

fn main() {
    let args: Vec<_> = args().skip(1).collect();
    if args.len() < 3 {
        println!("Usage: hamelin host port command [args]");
        return;
    }
    let hamelin = Hamelin::new(&args[2], if args.len() > 3 {
        Some(&args[3..])
    } else {
        None
    });
    let addr = format!("{}:{}", if args[0] == "localhost" { "127.0.0.1" } else { &*args[0] },
                       args[1]).parse().unwrap();
    let socket = v4().unwrap();
    socket.bind(&addr).unwrap();
    let server = socket.listen(256).unwrap();
    println!("Server bound on {:?}.", addr);
    let mut eloop = EventLoop::<HamelinHandler>::new().unwrap();
    eloop.register(&server, SERVER).unwrap();
    let mut handler = HamelinHandler::new(server, hamelin);
    eloop.run(&mut handler).ok().expect("Failed to execute event loop.");
}
<commit_msg>Updated for changes in mio.<commit_after>//! A TCP socket implementation of Hamelin.
#![feature(io, net)]
extern crate hamelin;
extern crate mio;

use std::collections::HashMap;
use std::env::args;
use std::io::ErrorKind::TimedOut;
use std::net::{TcpStream, TcpListener};
use hamelin::{AsyncBufStream, Hamelin, HamelinGuard};
use mio::*;
use mio::tcp::v4;

const SERVER: Token = Token(0);

struct Client {
    stream: AsyncBufStream<NonBlock<TcpStream>>,
    guard: HamelinGuard,
}

impl Client {
    fn new(sock: NonBlock<TcpStream>, guard: HamelinGuard) -> Client {
        Client {
            stream: AsyncBufStream::new(sock),
            guard: guard
        }
    }

    fn wait(&mut self) -> Result<(), ()> {
        self.guard.wait().map_err(|_| ())
    }

    fn read(&mut self) -> Result<(), ()> {
        match self.stream.read_line() {
            Ok(line) => match self.guard.write_line(&line) {
                Ok(_) => Ok(()),
                Err(ref e) if e.kind() == TimedOut => Ok(()),
                Err(_) => Err(()),
            },
            Err(ref e) if e.kind() == TimedOut => Ok(()),
            Err(_) => Err(()),
        }
    }

    fn write(&mut self) -> Result<(), ()> {
        match self.guard.read_line() {
            Ok(line) => match self.stream.write_line(&line) {
                Ok(_) => {
                    Ok(())
                },
                Err(ref e) if e.kind() == TimedOut => Ok(()),
                Err(_) => Err(()),
            },
            Err(ref e) if e.kind() == TimedOut => Ok(()),
            Err(_) => Err(()),
        }
    }
}

struct HamelinHandler {
    server: NonBlock<TcpListener>,
    hamelin: Hamelin,
    token_index: usize,
    clients: HashMap<usize, Client>,
}

impl HamelinHandler {
    fn new(server: NonBlock<TcpListener>, hamelin: Hamelin) -> HamelinHandler {
        HamelinHandler {
            server: server,
            hamelin: hamelin,
            token_index: 1,
            clients: HashMap::new(),
        }
    }

    fn accept(&mut self, eloop: &mut EventLoop<HamelinHandler>) {
        if let Ok(Some(client)) = self.server.accept() {
            let token = mio::Token(self.token_index);
            self.token_index += 1;
            eloop.register_opt(&client, token, Interest::all(), PollOpt::level())
                 .ok().expect("Failed to accept new client.");
            self.clients.insert(
                token.as_usize(), Client::new(client, self.hamelin.spawn().unwrap())
            );
            println!("Client connected.");
        }
    }

    fn read(&mut self, token: usize) -> Result<(), ()> {
        let client = &mut self.clients[token];
        let res = client.read();
        res
    }

    fn write(&mut self, token: usize) -> Result<(), ()> {
        if let Some(ref mut client) = self.clients.get_mut(&token) {
            let res = client.write();
            res
        } else {
            Err(())
        }
    }
}

impl Handler for HamelinHandler {
    type Timeout = ();
    type Message = ();

    fn readable(&mut self, eloop: &mut EventLoop<HamelinHandler>, token: Token, _: ReadHint) {
        match token {
            SERVER => self.accept(eloop),
            Token(x) => {
                if let Err(_) = self.read(x) {
                    eloop.deregister(&self.clients[x].stream.stream).unwrap();
                    let _ = self.clients[x].wait();
                    self.clients.remove(&x);
                }
            },
        }
    }

    fn writable(&mut self, _: &mut EventLoop<HamelinHandler>, token: Token) {
        match token {
            SERVER => (),
            Token(x) => {
                let _ = self.write(x);
            }
        }
    }
}

fn main() {
    let args: Vec<_> = args().skip(1).collect();
    if args.len() < 3 {
        println!("Usage: hamelin host port command [args]");
        return;
    }
    let hamelin = Hamelin::new(&args[2], if args.len() > 3 {
        Some(&args[3..])
    } else {
        None
    });
    let addr = format!("{}:{}", if args[0] == "localhost" { "127.0.0.1" } else { &*args[0] },
                       args[1]).parse().unwrap();
    let socket = v4().unwrap();
    socket.bind(&addr).unwrap();
    let server = socket.listen(256).unwrap();
    println!("Server bound on {:?}.", addr);
    let mut eloop = EventLoop::<HamelinHandler>::new().unwrap();
    eloop.register(&server, SERVER).unwrap();
    let mut handler = HamelinHandler::new(server, hamelin);
    eloop.run(&mut handler).ok().expect("Failed to execute event loop.");
}
<|endoftext|>


<commit_before>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

#![cfg_attr(not(feature = "v3_10"), allow(unused_imports))]

use libc::{c_char, ssize_t};

use glib::Object;
use glib::object::{Downcast, IsA};
use glib::translate::*;
use ffi;

glib_wrapper! {
    pub struct Builder(Object<ffi::GtkBuilder>);

    match fn {
        get_type => || ffi::gtk_builder_get_type(),
    }
}

impl Builder {
    pub fn new() -> Builder {
        assert_initialized_main_thread!();
        unsafe { from_glib_full(ffi::gtk_builder_new()) }
    }

    #[cfg(feature = "v3_10")]
    pub fn new_from_file(file_name: &str) -> Builder {
        assert_initialized_main_thread!();
        unsafe { from_glib_full(ffi::gtk_builder_new_from_file(file_name.to_glib_none().0)) }
    }

    #[cfg(feature = "v3_10")]
    pub fn new_from_resource(resource_path: &str) -> Builder {
        assert_initialized_main_thread!();
        unsafe {
            from_glib_full(ffi::gtk_builder_new_from_resource(resource_path.to_glib_none().0))
        }
    }

    #[cfg(feature = "v3_10")]
    pub fn new_from_string(string: &str) -> Builder {
        assert_initialized_main_thread!();
        unsafe {
            // Don't need a null-terminated string here
            from_glib_full(
                ffi::gtk_builder_new_from_string(string.as_ptr() as *const c_char,
                    string.len() as ssize_t))
        }
    }

    pub fn get_object<T: IsA<Object>>(&self, name: &str) -> Option<T> {
        unsafe {
            Option::<Object>::from_glib_none(
                ffi::gtk_builder_get_object(self.to_glib_none().0, name.to_glib_none().0))
                .and_then(|obj| obj.downcast().ok())
        }
    }

    pub fn add_from_file(&self, file_name: &str) -> bool {
        unsafe {
            let mut ptr = ::std::ptr::null_mut();
            ffi::gtk_builder_add_from_file(self.to_glib_none().0,
                                           file_name.to_glib_none().0,
                                           &mut ptr) > 0
        }
    }

    pub fn add_from_resource(&self, resource_path: &str) -> bool {
        unsafe {
            let mut ptr = ::std::ptr::null_mut();
            ffi::gtk_builder_add_from_resource(self.to_glib_none().0,
                                               resource_path.to_glib_none().0,
                                               &mut ptr) > 0
        }
    }

    pub fn add_from_string(&self, string: &str) -> bool {
        unsafe {
            let mut ptr = ::std::ptr::null_mut();
            ffi::gtk_builder_add_from_string(self.to_glib_none().0,
                                             string.as_ptr() as *const c_char,
                                             string.len(), &mut ptr) > 0
        }
    }
}
<commit_msg>Change file_path arguments from &str to &Path<commit_after>// Copyright 2013-2015, The Gtk-rs Project Developers.
// See the COPYRIGHT file at the top-level directory of this distribution.
// Licensed under the MIT license, see the LICENSE file or <http://opensource.org/licenses/MIT>

#![cfg_attr(not(feature = "v3_10"), allow(unused_imports))]

use libc::{c_char, ssize_t};

use glib::Object;
use glib::object::{Downcast, IsA};
use glib::translate::*;
use ffi;

use std::path::Path;

use Error;

glib_wrapper! {
    pub struct Builder(Object<ffi::GtkBuilder>);

    match fn {
        get_type => || ffi::gtk_builder_get_type(),
    }
}

impl Builder {
    pub fn new() -> Builder {
        assert_initialized_main_thread!();
        unsafe { from_glib_full(ffi::gtk_builder_new()) }
    }

    #[cfg(feature = "v3_10")]
    pub fn new_from_file<T: AsRef<Path>>(file_path: T) -> Builder {
        assert_initialized_main_thread!();
        unsafe { from_glib_full(ffi::gtk_builder_new_from_file(file_path.as_ref().to_glib_none().0)) }
    }

    #[cfg(feature = "v3_10")]
    pub fn new_from_resource(resource_path: &str) -> Builder {
        assert_initialized_main_thread!();
        unsafe {
            from_glib_full(ffi::gtk_builder_new_from_resource(resource_path.to_glib_none().0))
        }
    }

    #[cfg(feature = "v3_10")]
    pub fn new_from_string(string: &str) -> Builder {
        assert_initialized_main_thread!();
        unsafe {
            // Don't need a null-terminated string here
            from_glib_full(
                ffi::gtk_builder_new_from_string(string.as_ptr() as *const c_char,
                    string.len() as ssize_t))
        }
    }

    pub fn get_object<T: IsA<Object>>(&self, name: &str) -> Option<T> {
        unsafe {
            Option::<Object>::from_glib_none(
                ffi::gtk_builder_get_object(self.to_glib_none().0, name.to_glib_none().0))
                .and_then(|obj| obj.downcast().ok())
        }
    }

    pub fn add_from_file<T: AsRef<Path>>(&self, file_path: T) -> Result<(), Error> {
        unsafe {
            let mut error = ::std::ptr::null_mut();
            ffi::gtk_builder_add_from_file(self.to_glib_none().0,
                                           file_path.as_ref().to_glib_none().0,
                                           &mut error);
            if error.is_null() { Ok(()) } else { Err(from_glib_full(error)) }
        }
    }

    pub fn add_from_resource(&self, resource_path: &str) -> Result<(), Error> {
        unsafe {
            let mut error = ::std::ptr::null_mut();
            ffi::gtk_builder_add_from_resource(self.to_glib_none().0,
                                               resource_path.to_glib_none().0,
                                               &mut error);
            if error.is_null() { Ok(()) } else { Err(from_glib_full(error)) }
        }
    }

    pub fn add_from_string(&self, string: &str) -> Result<(), Error> {
        unsafe {
            let mut error = ::std::ptr::null_mut();
            ffi::gtk_builder_add_from_string(self.to_glib_none().0,
                                             string.as_ptr() as *const c_char,
                                             string.len(), &mut error);
            if error.is_null() { Ok(()) } else { Err(from_glib_full(error)) }
        }
    }
}
<|endoftext|>


<commit_before>use lua::{State, Index, ToLua, FromLua, Function, REGISTRYINDEX};
use types;
use std::ptr;
use error;

/// A wrapper around a Lua State.
///
/// Contains its own section of a Lua Stack; when the context goes out of scope, any value pushed
/// using this context is popped.
pub struct Context<'a> {
    state: &'a mut State,
    target_pos: Index,
}

/// __gc metamethod used to clean up Rust types that implements the Drop trait.
pub fn userdata_drop<T>(state: &mut State) -> i32 {
    use std::ptr;
    let mut context = Context::new(state);
    unsafe {
        // should be safe as long as types match
        let userdata = context.get_arg_typed::<types::LuaUserdata>(1).unwrap();
        let entity = userdata.get_value::<T>(&mut context).unwrap();
        ptr::drop_in_place(entity as *mut T);
    };
    0
}

impl<'a> Context<'a> {
    /// Creates a new Context using an existing state.
    pub fn new(state: &mut State) -> Context {
        let pos = state.get_top();
        Context {
            state: state,
            target_pos: pos,
        }
    }

    /// Get this context's contained state.
    pub fn get_state(&mut self) -> &mut State {
        self.state
    }

    /// Push a floating point number onto the stack.
    pub fn push_number(&mut self, value: f64) -> types::LuaNumber {
        self.state.push_number(value);
        let i = self.state.get_top();
        types::LuaNumber::new(i)
    }

    /// Push a string onto the stack.
    pub fn push_string(&mut self, value: &str) -> types::LuaString {
        self.state.push_string(value);
        let i = self.state.get_top();
        types::LuaString::new(i)
    }

    /// Create a new table and push it into the stack.
    pub fn push_table(&mut self) -> types::LuaTable {
        self.state.new_table();
        let i = self.state.get_top();
        types::LuaTable::new(i)
    }

    /// Push a boolean value onto the stack.
    pub fn push_bool(&mut self, value: bool) -> types::LuaBool {
        self.state.push_bool(value);
        let i = self.state.get_top();
        types::LuaBool::new(i)
    }

    /// Push a C function onto the stack.
    pub fn push_function(&mut self, func: Function) -> types::LuaFunction {
        self.state.push_fn(func);
        let i = self.state.get_top();
        types::LuaFunction::new(i)
    }

    /// Push an integer onto the stack.
    pub fn push_integer(&mut self, value: i64) -> types::LuaInteger {
        self.state.push_integer(value);
        let i = self.state.get_top();
        types::LuaInteger::new(i)
    }

    /// Push a nil value onto the stack.
    pub fn push_nil(&mut self) -> types::LuaNil {
        self.state.push_nil();
        let i = self.state.get_top();
        types::LuaNil::new(i)
    }

    /// Push a user-defined value onto the stack.
    pub fn push_userdata<T>(&mut self, value: T) -> types::LuaUserdata {
        unsafe { ptr::write(self.state.new_userdata_typed(), value); }
        let i = self.state.get_top();
        types::LuaUserdata::new(i)
    }

    /// Push a user-defined value onto the stack, and give it the metatable named 'name.'
    pub fn push_userdata_named<T>(&mut self, value: T, name: &str) -> types::LuaUserdata {
        let entity_object = self.push_userdata(value);
        let entity_object_meta = self.metatable_get(&name).unwrap();
        entity_object.set_metatable(self, &entity_object_meta);
        entity_object
    }

    pub fn push_thread(&mut self) -> State {
        self.state.new_thread()
    }

    /// Create a library using an array of Functions, and push the library table onto the stack.,
    pub fn create_lib(&mut self, lib: &[(&str, Function)]) -> types::LuaTable {
        self.state.new_lib(lib);
        let i = self.state.get_top();
        types::LuaTable::new(i)
    }

    /// Push a global value onto the stack.
    pub fn push_global(&mut self, key: &str) -> types::LuaGeneric {
        self.state.get_global(key);
        let i = self.state.get_top();
        types::LuaGeneric::new(i)
    }

    /// Set a value in the global Lua namespace.
    pub fn set_global(&mut self, key: &str, value: &ToLua) {
        value.to_lua(self.state);
        self.state.set_global(key);
    }

    /// Get a value from the Lua registry.
    pub fn get_from_registry(&mut self, key: &ToLua) -> types::LuaGeneric {
        key.to_lua(self.state);
        self.state.get_table(REGISTRYINDEX);
        let i = self.state.get_top();
        types::LuaGeneric::new(i)
    }

    /// Get a value from the Lua registry using a type.
    pub fn get_from_registry_typed<T: FromLua>(&mut self, key: &ToLua) -> Option<T> {
        key.to_lua(self.state);
        self.state.get_table(REGISTRYINDEX);
        let i = self.state.get_top();
        T::from_lua(&mut self.state, i)
    }

    /// Set a value in the Lua registry.
    pub fn set_in_registry(&mut self, key: &ToLua, value: &ToLua) {
        key.to_lua(self.state);
        value.to_lua(self.state);
        self.state.set_table(REGISTRYINDEX);
    }

    /// Get an argument from this context.
    pub fn get_arg(&mut self, arg: Index) -> Option<types::LuaGeneric> {
        if self.state.is_none(arg) {
            None
        } else {
            Some(types::LuaGeneric::new(arg))
        }
    }

    /// Get an argument from this context.
    pub fn get_arg_typed<T: FromLua>(&mut self, arg: Index) -> Option<T> {
        T::from_lua(&mut self.state, arg)
    }

    /// Get an argument from this context.
    ///
    /// If the argument does not exist, return a default value.
    pub fn get_arg_typed_or<T: FromLua>(&mut self, arg: Index, value: T) -> Option<T> {
        if self.state.is_none_or_nil(arg) {
            Some(value)
        } else {
            T::from_lua(&mut self.state, arg)
        }
    }

    /// Execute valid Lua code.
    ///
    /// # Errors
    /// Returns an error if the string is not valid Lua,
    /// or a runtime error occurs during execution.
    pub fn do_string(&mut self, s: &str) -> error::Result<()> {
        let threadstatus = self.state.do_string(&s);
        match error::get_status_from_threadstatus(threadstatus) {
            Ok(_) => {
                error::new_luaresult_ok(())
            },
            Err(status) => {
                error::new_luaresult_err(status, error::pop_error_from_state(&mut self.state))
            }
        }
    }

    /// Push a new context on top of the current context.
    ///
    /// New values can not be pushed onto the old context until the new context goes out of scope;
    /// however, values pushed by the old context can still be used by the new context.
    pub fn push_context(&mut self) -> Context {
        Context::new(&mut self.state)
    }

    /// Returns a list of values to Lua.
    pub fn return_context(mut self, args: &[&ToLua]) -> Index {
        // push elements in reverse order
        for arg in args.iter().rev() {
            arg.to_lua(&mut self.state);
        }
        for i in 0..args.len() {
            self.state.replace(i as Index + self.target_pos);
        }
        self.target_pos += args.len() as Index;
        args.len() as Index
    }

    /// Register a new metatable in the registry.
    ///
    /// Returns a tuple; the first value is if this metatable should be initialized, and the
    /// second value is the metatable itself.
    pub fn metatable_register(&mut self, name: &str) -> (bool, types::LuaTable) {
        let ret = self.state.new_metatable(name);
        let i = self.state.get_top();
        let table = types::LuaTable::new(i);
        (ret, table)
    }

    /// Get a metatable from the registry.
    pub fn metatable_get(&mut self, name: &str) -> Option<types::LuaTable> {
        self.state.get_metatable_from_registry(name);
        match self.state.is_table(-1) {
            true => {
                let i = self.state.get_top();
                let table = types::LuaTable::new(i);
                Some(table)
            },
            false => {
                self.state.pop(1);
                None
            }
        }
    }

    /// Register a named metatable into the Lua registry.
    ///
    /// Takes a list of member functions, a list of metamethods, and a unique name for this
    /// metatable.
    pub fn metatable_register_named<T>(&mut self, lib: &[(&str, Function)], metamethods: &[(&str, Function)], unique_name: &str)
            -> Option<types::LuaTable> {
        let entity_library_members = self.create_lib(lib);
        let (should_set, entity_metatable) = self.metatable_register(unique_name);
        if should_set {
            for &(ref name, ref value) in metamethods.iter() {
                entity_metatable.set_raw(self, name, value);
            }
            entity_metatable.set_raw(self, &"__index", &entity_library_members);
            entity_metatable.set_raw(self, &"__gc", &lua_func!(userdata_drop<T>));
            // disable getting of metatables
            entity_metatable.set_raw(self, &"__metatable", &0);
        }
        Some(entity_metatable)
    }
}

impl<'a> Drop for Context<'a> {
    fn drop(&mut self) {
        self.state.set_top(self.target_pos);
    }
}
<commit_msg>Changed Context::push_thread to return a LuaThread<commit_after>use lua::{State, Index, ToLua, FromLua, Function, REGISTRYINDEX};
use types;
use std::ptr;
use error;

/// A wrapper around a Lua State.
///
/// Contains its own section of a Lua Stack; when the context goes out of scope, any value pushed
/// using this context is popped.
pub struct Context<'a> {
    state: &'a mut State,
    target_pos: Index,
}

/// __gc metamethod used to clean up Rust types that implements the Drop trait.
pub fn userdata_drop<T>(state: &mut State) -> i32 {
    use std::ptr;
    let mut context = Context::new(state);
    unsafe {
        // should be safe as long as types match
        let userdata = context.get_arg_typed::<types::LuaUserdata>(1).unwrap();
        let entity = userdata.get_value::<T>(&mut context).unwrap();
        ptr::drop_in_place(entity as *mut T);
    };
    0
}

impl<'a> Context<'a> {
    /// Creates a new Context using an existing state.
    pub fn new(state: &mut State) -> Context {
        let pos = state.get_top();
        Context {
            state: state,
            target_pos: pos,
        }
    }

    /// Get this context's contained state.
    pub fn get_state(&mut self) -> &mut State {
        self.state
    }

    /// Push a floating point number onto the stack.
    pub fn push_number(&mut self, value: f64) -> types::LuaNumber {
        self.state.push_number(value);
        let i = self.state.get_top();
        types::LuaNumber::new(i)
    }

    /// Push a string onto the stack.
    pub fn push_string(&mut self, value: &str) -> types::LuaString {
        self.state.push_string(value);
        let i = self.state.get_top();
        types::LuaString::new(i)
    }

    /// Create a new table and push it into the stack.
    pub fn push_table(&mut self) -> types::LuaTable {
        self.state.new_table();
        let i = self.state.get_top();
        types::LuaTable::new(i)
    }

    /// Push a boolean value onto the stack.
    pub fn push_bool(&mut self, value: bool) -> types::LuaBool {
        self.state.push_bool(value);
        let i = self.state.get_top();
        types::LuaBool::new(i)
    }

    /// Push a C function onto the stack.
    pub fn push_function(&mut self, func: Function) -> types::LuaFunction {
        self.state.push_fn(func);
        let i = self.state.get_top();
        types::LuaFunction::new(i)
    }

    /// Push an integer onto the stack.
    pub fn push_integer(&mut self, value: i64) -> types::LuaInteger {
        self.state.push_integer(value);
        let i = self.state.get_top();
        types::LuaInteger::new(i)
    }

    /// Push a nil value onto the stack.
    pub fn push_nil(&mut self) -> types::LuaNil {
        self.state.push_nil();
        let i = self.state.get_top();
        types::LuaNil::new(i)
    }

    /// Push a user-defined value onto the stack.
    pub fn push_userdata<T>(&mut self, value: T) -> types::LuaUserdata {
        unsafe { ptr::write(self.state.new_userdata_typed(), value); }
        let i = self.state.get_top();
        types::LuaUserdata::new(i)
    }

    /// Push a user-defined value onto the stack, and give it the metatable named 'name.'
    pub fn push_userdata_named<T>(&mut self, value: T, name: &str) -> types::LuaUserdata {
        let entity_object = self.push_userdata(value);
        let entity_object_meta = self.metatable_get(&name).unwrap();
        entity_object.set_metatable(self, &entity_object_meta);
        entity_object
    }

    /// Push a Lua thread onto the stack
    ///
    /// You will need to do stuff with the Lua thread in a separate thread if you want two Lua
    /// threads to run concurrently, Lua doesn't do any actual multithreading itself.
    pub fn push_thread(&mut self) -> types::LuaThread {
        self.state.new_thread();
        let i = self.state.get_top();
        types::LuaThread::new(i)
    }

    /// Create a library using an array of Functions, and push the library table onto the stack.,
    pub fn create_lib(&mut self, lib: &[(&str, Function)]) -> types::LuaTable {
        self.state.new_lib(lib);
        let i = self.state.get_top();
        types::LuaTable::new(i)
    }

    /// Push a global value onto the stack.
    pub fn push_global(&mut self, key: &str) -> types::LuaGeneric {
        self.state.get_global(key);
        let i = self.state.get_top();
        types::LuaGeneric::new(i)
    }

    /// Set a value in the global Lua namespace.
    pub fn set_global(&mut self, key: &str, value: &ToLua) {
        value.to_lua(self.state);
        self.state.set_global(key);
    }

    /// Get a value from the Lua registry.
    pub fn get_from_registry(&mut self, key: &ToLua) -> types::LuaGeneric {
        key.to_lua(self.state);
        self.state.get_table(REGISTRYINDEX);
        let i = self.state.get_top();
        types::LuaGeneric::new(i)
    }

    /// Get a value from the Lua registry using a type.
    pub fn get_from_registry_typed<T: FromLua>(&mut self, key: &ToLua) -> Option<T> {
        key.to_lua(self.state);
        self.state.get_table(REGISTRYINDEX);
        let i = self.state.get_top();
        T::from_lua(&mut self.state, i)
    }

    /// Set a value in the Lua registry.
    pub fn set_in_registry(&mut self, key: &ToLua, value: &ToLua) {
        key.to_lua(self.state);
        value.to_lua(self.state);
        self.state.set_table(REGISTRYINDEX);
    }

    /// Get an argument from this context.
    pub fn get_arg(&mut self, arg: Index) -> Option<types::LuaGeneric> {
        if self.state.is_none(arg) {
            None
        } else {
            Some(types::LuaGeneric::new(arg))
        }
    }

    /// Get an argument from this context.
    pub fn get_arg_typed<T: FromLua>(&mut self, arg: Index) -> Option<T> {
        T::from_lua(&mut self.state, arg)
    }

    /// Get an argument from this context.
    ///
    /// If the argument does not exist, return a default value.
    pub fn get_arg_typed_or<T: FromLua>(&mut self, arg: Index, value: T) -> Option<T> {
        if self.state.is_none_or_nil(arg) {
            Some(value)
        } else {
            T::from_lua(&mut self.state, arg)
        }
    }

    /// Execute valid Lua code.
    ///
    /// # Errors
    /// Returns an error if the string is not valid Lua,
    /// or a runtime error occurs during execution.
    pub fn do_string(&mut self, s: &str) -> error::Result<()> {
        let threadstatus = self.state.do_string(&s);
        match error::get_status_from_threadstatus(threadstatus) {
            Ok(_) => {
                error::new_luaresult_ok(())
            },
            Err(status) => {
                error::new_luaresult_err(status, error::pop_error_from_state(&mut self.state))
            }
        }
    }

    /// Push a new context on top of the current context.
    ///
    /// New values can not be pushed onto the old context until the new context goes out of scope;
    /// however, values pushed by the old context can still be used by the new context.
    pub fn push_context(&mut self) -> Context {
        Context::new(&mut self.state)
    }

    /// Returns a list of values to Lua.
    pub fn return_context(mut self, args: &[&ToLua]) -> Index {
        // push elements in reverse order
        for arg in args.iter().rev() {
            arg.to_lua(&mut self.state);
        }
        for i in 0..args.len() {
            self.state.replace(i as Index + self.target_pos);
        }
        self.target_pos += args.len() as Index;
        args.len() as Index
    }

    /// Register a new metatable in the registry.
    ///
    /// Returns a tuple; the first value is if this metatable should be initialized, and the
    /// second value is the metatable itself.
    pub fn metatable_register(&mut self, name: &str) -> (bool, types::LuaTable) {
        let ret = self.state.new_metatable(name);
        let i = self.state.get_top();
        let table = types::LuaTable::new(i);
        (ret, table)
    }

    /// Get a metatable from the registry.
    pub fn metatable_get(&mut self, name: &str) -> Option<types::LuaTable> {
        self.state.get_metatable_from_registry(name);
        match self.state.is_table(-1) {
            true => {
                let i = self.state.get_top();
                let table = types::LuaTable::new(i);
                Some(table)
            },
            false => {
                self.state.pop(1);
                None
            }
        }
    }

    /// Register a named metatable into the Lua registry.
    ///
    /// Takes a list of member functions, a list of metamethods, and a unique name for this
    /// metatable.
    pub fn metatable_register_named<T>(&mut self, lib: &[(&str, Function)], metamethods: &[(&str, Function)], unique_name: &str)
            -> Option<types::LuaTable> {
        let entity_library_members = self.create_lib(lib);
        let (should_set, entity_metatable) = self.metatable_register(unique_name);
        if should_set {
            for &(ref name, ref value) in metamethods.iter() {
                entity_metatable.set_raw(self, name, value);
            }
            entity_metatable.set_raw(self, &"__index", &entity_library_members);
            entity_metatable.set_raw(self, &"__gc", &lua_func!(userdata_drop<T>));
            // disable getting of metatables
            entity_metatable.set_raw(self, &"__metatable", &0);
        }
        Some(entity_metatable)
    }
}

impl<'a> Drop for Context<'a> {
    fn drop(&mut self) {
        self.state.set_top(self.target_pos);
    }
}
<|endoftext|>