<commit_before>/// Parser

use std::char;

use charclass::CharClass;
use charclass::ascii;

/// A regular expression AST.
#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, uint, Option<uint>, Greedy),
    Capture(~Expr),
    Assertion(AssertType, ~Expr)
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

#[deriving(ToStr)]
pub enum AssertType {
    Positive,
    Negative
}

/// Parse a regular expression into an AST.  Fails on invalid syntax.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.  This tracks the position in the input string.
#[deriving(Clone)]
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>  // See `State::retreat`
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `advance`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

/// Parse alternation, e.g. `ducks|geese|swans`.
///
/// An alternation consists of zero or more concatenations, separated by
/// vertical bars `|`.
///
fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Alternate(items)
    }
}

/// Parse concatenation, e.g. `abc`.
fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => push_ignore_empty(&mut items, p_group(s)),
                '.' => items.push(Range('\0', char::MAX)),
                '\\' => items.push(cc_to_expr(p_escape(s))),
                '?' => {
                    let e = pop_expr(&mut items);
                    items.push(match e {
                        Repeat(_, _, _, NonGreedy) => fail!("multiple repeat"),
                        Repeat(inner, min, max, Greedy) =>
                            Repeat(inner, min, max, NonGreedy),
                        _ => Repeat(~e, 0, Some(1), Greedy)
                    })
                },
                '+' => add_repeat(&mut items, 1, None),
                '*' => add_repeat(&mut items, 0, None),
                '{' => match p_repetition(s) {
                    Some((min, max)) => add_repeat(&mut items, min, max),
                    None => items.push(Range(c, c))
                },
                _ => items.push(Range(c, c))
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Concatenate(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => {},
        _ => items.push(e)
    }
}

#[inline]
fn pop_expr(items: &mut ~[Expr]) -> Expr {
    items.pop_opt().expect("nothing to repeat")
}

#[inline]
fn add_repeat(items: &mut ~[Expr], min: uint, max: Option<uint>) {
    let e = pop_expr(items);
    items.push(match e {
        Repeat(..) => fail!("multiple repeat"),
        _ => Repeat(~e, min, max, Greedy)
    })
}

/// Parse a counted repetition (e.g. `a{2,3}`), sans the opening brace.
///
/// The following syntaxes are accepted:
///
/// * `{N}` – match exactly N repetitions;
/// * `{M,}` – at least M;
/// * `{,N}` – at most N;
/// * `{M,N}` – from M to N inclusive;
/// * `{,}` – zero or more (synonymous with `*`).
///
/// If parsing fails, return `None` without consuming input.  This
/// matches Python behavior, where invalid repetitions are ignored.
///
fn p_repetition(s_outer: &mut State) -> Option<(uint, Option<uint>)> {
    // Clone the parser state, so we can backtrack on failure
    let mut s = s_outer.clone();

    let result = {
        let min = p_number(&mut s);
        match s.advance() {
            Some(',') => {
                let max = p_number(&mut s);
                match s.advance() {
                    // {} or {N,} or {,M} or {N,M}
                    Some('}') => Some((min.unwrap_or(0), max)),
                    _ => None
                }
            },
            Some('}') => match min {
                // {N}
                Some(n) => Some((n, Some(n))),
                _ => None
            },
            _ => None
        }
    };

    if result.is_some() {
        // Only consume input if parsing was successful
        s_outer.clone_from(&s);
    }

    result
}

/// Parse a non-negative integer, returning `None` on failure.
fn p_number(s: &mut State) -> Option<uint> {
    let mut acc = None;
    loop {
        match s.advance() {
            Some(c) if '0' <= c && c <= '9' => {
                let digit = c as uint - '0' as uint;
                acc = Some(match acc {
                    Some(n) => 10 * n + digit,
                    None => digit
                });
            },
            _ => { s.retreat(); break }
        }
    }
    acc
}

/// Parse a group (e.g. `(hello)`), sans the opening parenthesis.
fn p_group(s: &mut State) -> Expr {
    let result = match s.advance() {
        Some('?') => match s.advance() {
            Some(c) => match c {
                ':' => p_alternate(s),
                '#' => p_comment(s),
                '=' => Assertion(Positive, ~p_alternate(s)),
                '!' => Assertion(Negative, ~p_alternate(s)),
                _ => fail!(format!("unknown extension: ?{}", c))
            },
            None => fail!("unexpected end of pattern")
        },
        _ => { s.retreat(); Capture(~p_alternate(s)) }
    };

    // Match the closing paren
    match s.advance() {
        Some(')') => result,
        _ => fail!("mismatched parenthesis")
    }
}

/// Consume all input up to the first closing parenthesis, and return
/// `Empty`.
fn p_comment(s: &mut State) -> Expr {
    loop {
        match s.advance() {
            Some(c) if c != ')' => continue,
            _ => { s.retreat(); break }
        }
    }
    Empty
}

/// Parse an escape sequence (e.g. `\d`), sans the leading backslash.
fn p_escape(s: &mut State) -> CharClass {
    match s.advance() {
        Some(c) => match c {
            'n' => CharClass::from_char('\n'),
            'r' => CharClass::from_char('\r'),
            't' => CharClass::from_char('\t'),

            'd' => ascii::digit,
            's' => ascii::space,
            'w' => ascii::word,

            'x' => p_hex_escape(s, 2),
            'u' => p_hex_escape(s, 4),
            'U' => p_hex_escape(s, 8),

            _ => fail!("invalid escape")
        },
        None => fail!("invalid escape")
    }
}

fn p_hex_escape(s: &mut State, n_digits: uint) -> CharClass {
    let mut acc = 0u32;
    for _ in range(0, n_digits) {
        acc = 16 * acc + match s.advance() {
            Some(c) => c.to_digit(16).expect("invalid escape") as u32,
            _ => fail!("invalid escape")
        };
    }
    let c = char::from_u32(acc).expect("character out of range");
    CharClass::from_char(c)
}

/// Reify a character class as an `Expr`.
fn cc_to_expr(cc: CharClass) -> Expr {
    Alternate(cc.ranges().iter().map(|&(lo, hi)| Range(lo, hi)).collect())
}
<commit_msg>Reject invalid counted repeats<commit_after>/// Parser

use std::char;

use charclass::CharClass;
use charclass::ascii;

/// A regular expression AST.
#[deriving(ToStr)]
pub enum Expr {
    Empty,
    Range(char, char),
    Concatenate(~[Expr]),
    Alternate(~[Expr]),
    Repeat(~Expr, uint, Option<uint>, Greedy),
    Capture(~Expr),
    Assertion(AssertType, ~Expr)
}

#[deriving(ToStr)]
pub enum Greedy {
    NonGreedy,
    Greedy
}

#[deriving(ToStr)]
pub enum AssertType {
    Positive,
    Negative
}

/// Parse a regular expression into an AST.  Fails on invalid syntax.
pub fn parse(input: &str) -> Expr {
    let mut s = State::new(input);
    let e = p_alternate(&mut s);
    if s.has_input() {
        // p_alternate() only terminates on an empty string or an extra
        // paren.  Since the string isn't empty, we infer the latter.
        fail!("unbalanced parenthesis")
    } else {
        e
    }
}

/// The parser state.  This tracks the position in the input string.
#[deriving(Clone)]
struct State<'a> {
    input: &'a str,
    prev: Option<&'a str>  // See `State::retreat`
}

impl<'a> State<'a> {
    fn new<'a>(input: &'a str) -> State<'a> {
        State {
            input: input,
            prev: None
        }
    }

    /// Consume and return the next character in the input, returning
    /// `None` if empty.
    fn advance(&mut self) -> Option<char> {
        self.prev = Some(self.input);
        if self.has_input() {
            let (c, input_) = self.input.slice_shift_char();
            self.input = input_;
            Some(c)
        } else {
            None
        }
    }

    /// Push the previously read character back onto the input.  This
    /// can only be called immediately after `advance`.
    fn retreat(&mut self) {
        self.input = self.prev.expect("nowhere to retreat");
        self.prev = None;
    }

    /// Return `true` if there is input remaining.
    fn has_input(&self) -> bool {
        self.input.len() > 0
    }
}

/// Parse alternation, e.g. `ducks|geese|swans`.
///
/// An alternation consists of zero or more concatenations, separated by
/// vertical bars `|`.
///
fn p_alternate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        items.push(p_concatenate(s));
        match s.advance() {
            Some(c) => {
                match c {
                    ')' => { s.retreat(); break },
                    '|' => continue,
                    _ => fail!("something bad happened; it's really bad")
                }
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Alternate(items)
    }
}

/// Parse concatenation, e.g. `abc`.
fn p_concatenate(s: &mut State) -> Expr {
    let mut items: ~[Expr] = ~[];

    loop {
        match s.advance() {
            Some(c) => match c {
                '|' | ')' => { s.retreat(); break },
                '(' => push_ignore_empty(&mut items, p_group(s)),
                '.' => items.push(Range('\0', char::MAX)),
                '\\' => items.push(cc_to_expr(p_escape(s))),
                '?' => {
                    let e = pop_expr(&mut items);
                    items.push(match e {
                        Repeat(_, _, _, NonGreedy) => fail!("multiple repeat"),
                        Repeat(inner, min, max, Greedy) =>
                            Repeat(inner, min, max, NonGreedy),
                        _ => Repeat(~e, 0, Some(1), Greedy)
                    })
                },
                '+' => add_repeat(&mut items, 1, None),
                '*' => add_repeat(&mut items, 0, None),
                '{' => match p_repetition(s) {
                    Some((min, max)) => add_repeat(&mut items, min, max),
                    None => items.push(Range(c, c))
                },
                _ => items.push(Range(c, c))
            },
            None => break
        }
    }

    match items {
        [] => Empty,
        [e] => e,
        _ => Concatenate(items)
    }
}

#[inline]
fn push_ignore_empty(items: &mut ~[Expr], e: Expr) {
    match e {
        Empty => {},
        _ => items.push(e)
    }
}

#[inline]
fn pop_expr(items: &mut ~[Expr]) -> Expr {
    items.pop_opt().expect("nothing to repeat")
}

#[inline]
fn add_repeat(items: &mut ~[Expr], min: uint, max: Option<uint>) {
    let e = pop_expr(items);
    items.push(match e {
        Repeat(..) => fail!("multiple repeat"),
        _ => Repeat(~e, min, max, Greedy)
    })
}

/// Parse a counted repetition (e.g. `a{2,3}`), sans the opening brace.
///
/// The following syntaxes are accepted:
///
/// * `{N}` – match exactly N repetitions;
/// * `{M,}` – at least M;
/// * `{,N}` – at most N;
/// * `{M,N}` – from M to N inclusive;
/// * `{,}` – zero or more (synonymous with `*`).
///
/// If parsing fails, return `None` without consuming input.  This
/// matches Python behavior, where invalid repetitions are ignored.
///
fn p_repetition(s_outer: &mut State) -> Option<(uint, Option<uint>)> {
    // Clone the parser state, so we can backtrack on failure
    let mut s = s_outer.clone();

    let result = {
        let min = p_number(&mut s);
        match s.advance() {
            Some(',') => {
                let max = p_number(&mut s);
                match s.advance() {
                    // {} or {M,} or {,N} or {M,N}
                    Some('}') => {
                        let min_ = min.unwrap_or(0);
                        if check_repeat(min_, max) {
                            Some((min_, max))
                        } else {
                            fail!("bad repeat interval")
                        }
                    },
                    _ => None
                }
            },
            Some('}') => match min {
                // {N}
                Some(n) => Some((n, Some(n))),
                _ => None
            },
            _ => None
        }
    };

    if result.is_some() {
        // Only consume input if parsing was successful
        s_outer.clone_from(&s);
    }

    result
}

#[inline]
fn check_repeat(min: uint, max: Option<uint>) -> bool {
    match max {
        Some(max_) => min <= max_,
        None => true
    }
}

/// Parse a non-negative integer, returning `None` on failure.
fn p_number(s: &mut State) -> Option<uint> {
    let mut acc = None;
    loop {
        match s.advance() {
            Some(c) if '0' <= c && c <= '9' => {
                let digit = c as uint - '0' as uint;
                acc = Some(match acc {
                    Some(n) => 10 * n + digit,
                    None => digit
                });
            },
            _ => { s.retreat(); break }
        }
    }
    acc
}

/// Parse a group (e.g. `(hello)`), sans the opening parenthesis.
fn p_group(s: &mut State) -> Expr {
    let result = match s.advance() {
        Some('?') => match s.advance() {
            Some(c) => match c {
                ':' => p_alternate(s),
                '#' => p_comment(s),
                '=' => Assertion(Positive, ~p_alternate(s)),
                '!' => Assertion(Negative, ~p_alternate(s)),
                _ => fail!(format!("unknown extension: ?{}", c))
            },
            None => fail!("unexpected end of pattern")
        },
        _ => { s.retreat(); Capture(~p_alternate(s)) }
    };

    // Match the closing paren
    match s.advance() {
        Some(')') => result,
        _ => fail!("mismatched parenthesis")
    }
}

/// Consume all input up to the first closing parenthesis, and return
/// `Empty`.
fn p_comment(s: &mut State) -> Expr {
    loop {
        match s.advance() {
            Some(c) if c != ')' => continue,
            _ => { s.retreat(); break }
        }
    }
    Empty
}

/// Parse an escape sequence (e.g. `\d`), sans the leading backslash.
fn p_escape(s: &mut State) -> CharClass {
    match s.advance() {
        Some(c) => match c {
            'n' => CharClass::from_char('\n'),
            'r' => CharClass::from_char('\r'),
            't' => CharClass::from_char('\t'),

            'd' => ascii::digit,
            's' => ascii::space,
            'w' => ascii::word,

            'x' => p_hex_escape(s, 2),
            'u' => p_hex_escape(s, 4),
            'U' => p_hex_escape(s, 8),

            _ => fail!("invalid escape")
        },
        None => fail!("invalid escape")
    }
}

fn p_hex_escape(s: &mut State, n_digits: uint) -> CharClass {
    let mut acc = 0u32;
    for _ in range(0, n_digits) {
        acc = 16 * acc + match s.advance() {
            Some(c) => c.to_digit(16).expect("invalid escape") as u32,
            _ => fail!("invalid escape")
        };
    }
    let c = char::from_u32(acc).expect("character out of range");
    CharClass::from_char(c)
}

/// Reify a character class as an `Expr`.
fn cc_to_expr(cc: CharClass) -> Expr {
    Alternate(cc.ranges().iter().map(|&(lo, hi)| Range(lo, hi)).collect())
}
<|endoftext|>


<commit_before>use crate::{
    analysis::namespaces::{self, NsId},
    case::CaseExt,
    library::*,
};
use std::collections::HashMap;

#[derive(Clone, Debug, Default)]
pub struct Symbol {
    crate_name: Option<String>,
    module_name: Option<String>,
    owner_name: Option<String>,
    name: String,
    rust_prelude: bool,
}

impl Symbol {
    pub fn parent(&self) -> String {
        let mut ret = String::new();
        if Some("gobject") == self.crate_name() {
            ret.push_str("glib::object::");
        } else {
            if let Some(ref s) = self.crate_name {
                ret.push_str(s);
                ret.push_str("::");
            }
            if let Some(ref module) = self.module_name {
                ret.push_str(module);
                ret.push_str("::");
            }
        }
        if let Some(ref s) = self.owner_name {
            ret.push_str(s);
            ret.push_str("::");
        }
        ret
    }

    pub fn full_rust_name(&self) -> String {
        let mut ret = self.parent();
        ret.push_str(&self.name);
        ret
    }

    fn make_in_prelude(&mut self) {
        if self.module_name.replace("prelude".to_string()).is_some() {
            // .expect_none is not stabilized yet
            panic!("{:?} already had a module name set!", self)
        }
    }

    /// Convert this symbol into a trait
    pub fn make_trait(&mut self, trait_name: &str) {
        self.make_in_prelude();
        self.name = trait_name.into();
    }

    /// Convert this into a method of a trait
    pub fn make_trait_method(&mut self, trait_name: &str) {
        self.make_in_prelude();
        self.owner_name = Some(trait_name.into());
    }

    pub fn crate_name(&self) -> Option<&str> {
        self.crate_name.as_deref()
    }

    pub fn owner_name(&self) -> Option<&str> {
        self.owner_name.as_deref()
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn is_rust_prelude(&self) -> bool {
        self.rust_prelude
    }
}

#[derive(Debug)]
pub struct Info {
    symbols: Vec<Symbol>,
    c_name_index: HashMap<String, u32>,
    tid_index: HashMap<Option<TypeId>, u32>,
}

pub fn run(library: &Library, namespaces: &namespaces::Info) -> Info {
    let mut info = Info {
        symbols: Vec::new(),
        c_name_index: HashMap::new(),
        tid_index: HashMap::new(),
    };

    info.insert(
        "NULL",
        Symbol {
            name: "None".into(),
            rust_prelude: true,
            ..Default::default()
        },
        None,
    );
    info.insert(
        "FALSE",
        Symbol {
            name: "false".into(),
            rust_prelude: true,
            ..Default::default()
        },
        None,
    );
    info.insert(
        "TRUE",
        Symbol {
            name: "true".into(),
            rust_prelude: true,
            ..Default::default()
        },
        None,
    );

    for (ns_id, ns) in library.namespaces.iter().enumerate() {
        let ns_id = ns_id as NsId;
        if ns_id == namespaces::INTERNAL {
            continue;
        }

        let crate_name = if ns_id == namespaces::MAIN {
            None
        } else {
            Some(&namespaces[ns_id].crate_name)
        };

        for (pos, typ) in ns.types.iter().map(|t| t.as_ref().unwrap()).enumerate() {
            let symbol = Symbol {
                crate_name: crate_name.cloned(),
                name: typ.get_name(),
                ..Default::default()
            };
            let tid = TypeId {
                ns_id,
                id: pos as u32,
            };

            match typ {
                Type::Alias(Alias { c_identifier, .. }) => {
                    info.insert(c_identifier, symbol, Some(tid));
                }
                Type::Enumeration(Enumeration {
                    name,
                    c_type,
                    members,
                    functions,
                    ..
                })
                | Type::Bitfield(Bitfield {
                    name,
                    c_type,
                    members,
                    functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for member in members {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: member.name.to_camel(),
                            ..Default::default()
                        };
                        info.insert(&member.c_identifier, symbol, Some(tid));
                    }
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                            ..Default::default()
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                Type::Record(Record {
                    name,
                    c_type,
                    functions,
                    ..
                })
                | Type::Class(Class {
                    name,
                    c_type,
                    functions,
                    ..
                })
                | Type::Interface(Interface {
                    name,
                    c_type,
                    functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                            ..Default::default()
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                _ => {}
            }
        }
    }

    info
}

impl Info {
    pub fn by_c_name(&self, name: &str) -> Option<&Symbol> {
        self.c_name_index
            .get(name)
            .map(|&id| &self.symbols[id as usize])
    }

    pub fn by_c_name_mut(&mut self, name: &str) -> Option<&mut Symbol> {
        if let Some(&id) = self.c_name_index.get(name) {
            Some(&mut self.symbols[id as usize])
        } else {
            None
        }
    }

    pub fn by_tid(&self, tid: TypeId) -> Option<&Symbol> {
        self.tid_index
            .get(&Some(tid))
            .map(|&id| &self.symbols[id as usize])
    }

    fn insert(&mut self, name: &str, symbol: Symbol, tid: Option<TypeId>) {
        let id = self.symbols.len();
        self.symbols.push(symbol);
        self.c_name_index.insert(name.to_owned(), id as u32);
        if tid.is_some() {
            self.tid_index.insert(tid, id as u32);
        }
    }
}
<commit_msg>symbols: stop using glib::object::ObjectExt<commit_after>use crate::{
    analysis::namespaces::{self, NsId},
    case::CaseExt,
    library::*,
};
use std::collections::HashMap;

#[derive(Clone, Debug, Default)]
pub struct Symbol {
    crate_name: Option<String>,
    module_name: Option<String>,
    owner_name: Option<String>,
    name: String,
    rust_prelude: bool,
}

impl Symbol {
    pub fn parent(&self) -> String {
        let mut ret = String::new();
        if Some("gobject") == self.crate_name() {
            ret.push_str("glib::");
        } else {
            if let Some(ref s) = self.crate_name {
                ret.push_str(s);
                ret.push_str("::");
            }
            if let Some(ref module) = self.module_name {
                ret.push_str(module);
                ret.push_str("::");
            }
        }
        if let Some(ref s) = self.owner_name {
            ret.push_str(s);
            ret.push_str("::");
        }
        ret
    }

    pub fn full_rust_name(&self) -> String {
        let mut ret = self.parent();
        ret.push_str(&self.name);
        ret
    }

    fn make_in_prelude(&mut self) {
        if self.module_name.replace("prelude".to_string()).is_some() {
            // .expect_none is not stabilized yet
            panic!("{:?} already had a module name set!", self)
        }
    }

    /// Convert this symbol into a trait
    pub fn make_trait(&mut self, trait_name: &str) {
        self.make_in_prelude();
        self.name = trait_name.into();
    }

    /// Convert this into a method of a trait
    pub fn make_trait_method(&mut self, trait_name: &str) {
        self.make_in_prelude();
        self.owner_name = Some(trait_name.into());
    }

    pub fn crate_name(&self) -> Option<&str> {
        self.crate_name.as_deref()
    }

    pub fn owner_name(&self) -> Option<&str> {
        self.owner_name.as_deref()
    }

    pub fn name(&self) -> &str {
        &self.name
    }

    pub fn is_rust_prelude(&self) -> bool {
        self.rust_prelude
    }
}

#[derive(Debug)]
pub struct Info {
    symbols: Vec<Symbol>,
    c_name_index: HashMap<String, u32>,
    tid_index: HashMap<Option<TypeId>, u32>,
}

pub fn run(library: &Library, namespaces: &namespaces::Info) -> Info {
    let mut info = Info {
        symbols: Vec::new(),
        c_name_index: HashMap::new(),
        tid_index: HashMap::new(),
    };

    info.insert(
        "NULL",
        Symbol {
            name: "None".into(),
            rust_prelude: true,
            ..Default::default()
        },
        None,
    );
    info.insert(
        "FALSE",
        Symbol {
            name: "false".into(),
            rust_prelude: true,
            ..Default::default()
        },
        None,
    );
    info.insert(
        "TRUE",
        Symbol {
            name: "true".into(),
            rust_prelude: true,
            ..Default::default()
        },
        None,
    );

    for (ns_id, ns) in library.namespaces.iter().enumerate() {
        let ns_id = ns_id as NsId;
        if ns_id == namespaces::INTERNAL {
            continue;
        }

        let crate_name = if ns_id == namespaces::MAIN {
            None
        } else {
            Some(&namespaces[ns_id].crate_name)
        };

        for (pos, typ) in ns.types.iter().map(|t| t.as_ref().unwrap()).enumerate() {
            let symbol = Symbol {
                crate_name: crate_name.cloned(),
                name: typ.get_name(),
                ..Default::default()
            };
            let tid = TypeId {
                ns_id,
                id: pos as u32,
            };

            match typ {
                Type::Alias(Alias { c_identifier, .. }) => {
                    info.insert(c_identifier, symbol, Some(tid));
                }
                Type::Enumeration(Enumeration {
                    name,
                    c_type,
                    members,
                    functions,
                    ..
                })
                | Type::Bitfield(Bitfield {
                    name,
                    c_type,
                    members,
                    functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for member in members {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: member.name.to_camel(),
                            ..Default::default()
                        };
                        info.insert(&member.c_identifier, symbol, Some(tid));
                    }
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                            ..Default::default()
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                Type::Record(Record {
                    name,
                    c_type,
                    functions,
                    ..
                })
                | Type::Class(Class {
                    name,
                    c_type,
                    functions,
                    ..
                })
                | Type::Interface(Interface {
                    name,
                    c_type,
                    functions,
                    ..
                }) => {
                    info.insert(c_type, symbol, Some(tid));
                    for func in functions {
                        let symbol = Symbol {
                            crate_name: crate_name.cloned(),
                            owner_name: Some(name.clone()),
                            name: func.name.clone(),
                            ..Default::default()
                        };
                        info.insert(func.c_identifier.as_ref().unwrap(), symbol, None);
                    }
                }
                _ => {}
            }
        }
    }

    info
}

impl Info {
    pub fn by_c_name(&self, name: &str) -> Option<&Symbol> {
        self.c_name_index
            .get(name)
            .map(|&id| &self.symbols[id as usize])
    }

    pub fn by_c_name_mut(&mut self, name: &str) -> Option<&mut Symbol> {
        if let Some(&id) = self.c_name_index.get(name) {
            Some(&mut self.symbols[id as usize])
        } else {
            None
        }
    }

    pub fn by_tid(&self, tid: TypeId) -> Option<&Symbol> {
        self.tid_index
            .get(&Some(tid))
            .map(|&id| &self.symbols[id as usize])
    }

    fn insert(&mut self, name: &str, symbol: Symbol, tid: Option<TypeId>) {
        let id = self.symbols.len();
        self.symbols.push(symbol);
        self.c_name_index.insert(name.to_owned(), id as u32);
        if tid.is_some() {
            self.tid_index.insert(tid, id as u32);
        }
    }
}
<|endoftext|>


<commit_before>mod score;
pub mod consts;

use std::ascii::AsciiExt;
pub use self::score::*;

/// Compares two characters case-insensitively
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::eq('a', 'A'));
/// ```
#[inline(always)]
pub fn eq(a: char, b: char) -> bool {
    match a {
        a if a == b => true,
        a if a.is_ascii() && !b.is_ascii() => false,
        a if !a.is_ascii() && b.is_ascii() => false,
        a if a.is_ascii() && b.is_ascii() => {
            a.to_ascii_lowercase().eq(&b.to_ascii_lowercase())
        },
        a => a.to_lowercase().eq(b.to_lowercase())
    }
}

/// Searches for needle's chars in the haystack
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::matches("amo", "app/models/order"));
/// ```
pub fn matches(needle: &str, haystack: &str) -> bool {
    let mut hchars = haystack.chars();

    if needle == haystack {
        return true;
    }

    'outer: for n in needle.chars() {
        loop {
            match hchars.next() {
                Some(h) if eq(h, n) => continue 'outer,
                Some(_) => continue,
                None => return false
            }
        }
    }

    true
}

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

    #[test]
    fn test_eq() {
        assert!(eq('a', 'A'));
        assert!(eq('山', '山'));
        assert!(!eq('a', 'b'));
    }

    #[test]
    fn test_matches() {
        assert!(matches("a", "a"));
        assert!(matches("a", "abc"));
        assert!(matches("abc", "abc"));
        assert!(matches("ABC", "abc"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("test", "t/e/s/t"));
        assert!(matches("test", "t💣e💣s💣t"));
        assert!(matches("💣💣💣", "t💣e💣s💣t"));

        assert!(!matches("abc", "ab"));
        assert!(!matches("abc", "cab"));
        assert!(!matches("abc", ""));

        assert!(matches("", ""));
        assert!(matches("", "ab"));

        // UTF-8 case testing
        assert!(matches("a", "A"));
        assert!(matches("A", "a"));
        assert!(matches("山", "山"));
        assert!(matches("café", "CAFÉ"));
        assert!(matches("weiß", "WEIẞ"));
    }

    #[bench]
    fn bench_eq_same(b: &mut Bencher) {
        b.iter(|| eq('a', 'a'));
    }

    #[bench]
    fn bench_eq_ascii(b: &mut Bencher) {
        b.iter(|| eq('a', 'A'));
    }

    #[bench]
    fn bench_eq_utf8(b: &mut Bencher) {
        b.iter(|| eq('ø', 'Ø'));
    }

    #[bench]
    fn bench_eq_mixed(b: &mut Bencher) {
        b.iter(|| eq('a', 'Ø'));
    }

    #[bench]
    fn bench_matches(b: &mut Bencher) {
        b.iter(|| matches("amor", "app/models/order.rb"))
    }

    #[bench]
    fn bench_matches_mixed_case(b: &mut Bencher) {
        b.iter(|| matches("AMOr", "App/Models/Order.rb"))
    }

    #[bench]
    fn bench_matches_multiple(b: &mut Bencher) {
        b.iter(|| {
            matches("amor", "app/models/order.rb");
            matches("amor", "spec/models/order_spec.rb");
            matches("amor", "other_garbage.rb");
            matches("amor", "Gemfile");
            matches("amor", "node_modules/test/a/thing.js");
            matches("amor", "vendor/bundle/ruby/gem.rb")
        })
    }

    #[bench]
    fn bench_matches_eq(b: &mut Bencher) {
        b.iter(|| {
            matches("Gemfile", "Gemfile");
            matches("gemfile", "Gemfile")
        })
    }
}
<commit_msg>Simplify fuzzy::eq implementation<commit_after>mod score;
pub mod consts;

use std::ascii::AsciiExt;
pub use self::score::*;

/// Compares two characters case-insensitively
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::eq('a', 'A'));
/// ```
#[inline(always)]
pub fn eq(a: char, b: char) -> bool {
    match a {
        _ if a == b => true,
        _ if a.is_ascii() || b.is_ascii() => a.eq_ignore_ascii_case(&b),
        _ => a.to_lowercase().eq(b.to_lowercase())
    }
}

/// Searches for needle's chars in the haystack
///
/// # Examples
///
/// ```
/// assert!(rff::fuzzy::matches("amo", "app/models/order"));
/// ```
#[inline]
pub fn matches(needle: &str, haystack: &str) -> bool {
    if needle == haystack {
        return true;
    }

    let mut hchars = haystack.chars();

    'outer: for n in needle.chars() {
        loop {
            match hchars.next() {
                Some(h) if eq(h, n) => continue 'outer,
                Some(_) => continue,
                None => return false
            }
        }
    }

    true
}

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

    #[test]
    fn test_eq() {
        assert!(eq('a', 'A'));
        assert!(eq('山', '山'));
        assert!(!eq('a', 'b'));
    }

    #[test]
    fn test_matches() {
        assert!(matches("a", "a"));
        assert!(matches("a", "abc"));
        assert!(matches("abc", "abc"));
        assert!(matches("ABC", "abc"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("abc", "a1b2c3"));
        assert!(matches("test", "t/e/s/t"));
        assert!(matches("test", "t💣e💣s💣t"));
        assert!(matches("💣💣💣", "t💣e💣s💣t"));

        assert!(!matches("abc", "ab"));
        assert!(!matches("abc", "cab"));
        assert!(!matches("abc", ""));

        assert!(matches("", ""));
        assert!(matches("", "ab"));

        // UTF-8 case testing
        assert!(matches("a", "A"));
        assert!(matches("A", "a"));
        assert!(matches("山", "山"));
        assert!(matches("café", "CAFÉ"));
        assert!(matches("weiß", "WEIẞ"));
    }

    #[bench]
    fn bench_eq_same(b: &mut Bencher) {
        b.iter(|| eq('a', 'a'));
    }

    #[bench]
    fn bench_eq_ascii(b: &mut Bencher) {
        b.iter(|| eq('a', 'A'));
    }

    #[bench]
    fn bench_eq_utf8(b: &mut Bencher) {
        b.iter(|| eq('ø', 'Ø'));
    }

    #[bench]
    fn bench_eq_mixed(b: &mut Bencher) {
        b.iter(|| eq('a', 'Ø'));
    }

    #[bench]
    fn bench_matches(b: &mut Bencher) {
        b.iter(|| matches("amor", "app/models/order.rb"))
    }

    #[bench]
    fn bench_matches_mixed_case(b: &mut Bencher) {
        b.iter(|| matches("AMOr", "App/Models/Order.rb"))
    }

    #[bench]
    fn bench_matches_multiple(b: &mut Bencher) {
        b.iter(|| {
            matches("amor", "app/models/order.rb");
            matches("amor", "spec/models/order_spec.rb");
            matches("amor", "other_garbage.rb");
            matches("amor", "Gemfile");
            matches("amor", "node_modules/test/a/thing.js");
            matches("amor", "vendor/bundle/ruby/gem.rb")
        })
    }

    #[bench]
    fn bench_matches_eq(b: &mut Bencher) {
        b.iter(|| {
            matches("Gemfile", "Gemfile");
            matches("gemfile", "Gemfile")
        })
    }
}
<|endoftext|>


<commit_before>//! Half-edge graph representation of meshes.
//!
//! This module provides a flexible representation of meshes as a [half-edge
//! graph](https://en.wikipedia.org/wiki/doubly_connected_edge_list). Meshes
//! can store arbitrary geometric data associated with any topological
//! structure, including vertices, half-edges, and faces.
//!
//! # Representation
//!
//! `Mesh`es store topological data using associative collections. Keys are
//! exposed as strongly typed and opaque values, which can be used to refer to
//! a topological structure, such as `VertexKey`. Topology is typically
//! manipulated using a view.
//!
//! A `Mesh` is conceptually composed of vertices, half-edges, and faces.
//! Half-edges are directed and join vertices. Every half-edge is paired with
//! an opposite half-edge with the opposite direction.  Given a half-edge that
//! connects a vertex `a` to a vertex `b`, that half-edge will have an opposite
//! half-edge from `b` to `a`. Together, these half-edges form a composite
//! edge. When the term "edge" is used alone, it generally refers to a
//! half-edge.
//!
//! Half-edges are connected to their neighbors, known as "next" and "previous"
//! half-edges. When a face is present in the region formed by a perimeter of
//! vertices and their half-edges, the half-edges will refer to that face and
//! the face will refer to one of the half-edges in the interior circuit.
//!
//! Together with vertices and faces, the connectivity of half-edges allows for
//! effecient traversal of topology. For example, it becomes trivial to find
//! neighboring topologies, such as the faces that share a given vertex or the
//! neighboring faces of a given face.
//!
//! # Topological Views
//!
//! `Mesh`es expose views over their topological structures (vertices, edges,
//! and faces). Views are accessed via keys or iteration and behave similarly
//! to references. They provide the primary API for interacting with a mesh's
//! topology and geometry. There are three types summarized below:
//!
//! | Type      | Traversal | Exclusive | Geometry  | Topology  |
//! |-----------|-----------|-----------|-----------|-----------|
//! | Immutable | Yes       | No        | Immutable | Immutable |
//! | Mutable   | Yes       | Yes       | Mutable   | Mutable   |
//! | Orphan    | No        | No        | Mutable   | N/A       |
//!
//! Immutable and mutable views are much like references. Immutable views
//! cannot mutate a mesh in any way and it is possible to obtain multiple such
//! views at the same time. Mutable views are exclusive, but allow for
//! mutations.
//!
//! Orphan views are similar to mutable views, but they only have access to the
//! geometry of a specific topological structure in a mesh. Because they do not
//! know about other vertices, edges, or faces, an orphan view cannot traverse
//! the topology of a mesh in any way. These views are most useful for
//! modifying the geometry of a mesh and, unlike mutable views, multiple orphan
//! views can be obtained at the same time. Orphan views are mostly used by
//! circulators (iterators).
//!
//! Immutable and mutable views are represented by a single type constructor,
//! such as `FaceView`.  Orphan views are represented by their own type, such
//! as `OrphanFace`.
//!
//! # Circulators
//!
//! Topological views allow for traversals of a mesh's topology. One useful
//! type of traversal uses a circulator, which is a type of iterator that
//! examines the neighbors of a topological structure. For example, the face
//! circulator of a vertex yields all faces that share that vertex in order.
//!
//! Mutable circulators emit orphan views, not mutable views. This is because
//! it is not possible to instantiate more than one mutable view at a time. If
//! multiple mutable views are needed, it is possible to use an immutable
//! circulator to collect the keys of the target topology and then lookup each
//! mutable view using those keys.
//!
//! # Examples
//!
//! Generating a mesh from a primitive:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! use nalgebra::Point3;
//! use plexus::graph::Mesh;
//! use plexus::prelude::*;
//! use plexus::primitive::sphere::UvSphere;
//!
//! # fn main() {
//! let mut mesh = UvSphere::new(16, 16)
//!     .polygons_with_position()
//!     .collect::<Mesh<Point3<f32>>>();
//! # }
//! ```
//!
//! Manipulating a face in a mesh:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! use nalgebra::Point3;
//! use plexus::graph::Mesh;
//! use plexus::prelude::*;
//! use plexus::primitive::sphere::UvSphere;
//!
//! # fn main() {
//! let mut mesh = UvSphere::new(16, 16)
//!     .polygons_with_position()
//!     .collect::<Mesh<Point3<f32>>>();
//! let key = mesh.faces().nth(0).unwrap().key(); // Get the key of the first face.
//! mesh.face_mut(key).unwrap().extrude(1.0).unwrap(); // Extrude the face.
//! # }
//! ```
//!
//! Traversing and circulating over a mesh:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! use nalgebra::Point2;
//! use plexus::graph::Mesh;
//! use plexus::prelude::*;
//!
//! # fn main() {
//! let mut mesh = Mesh::<Point2<f32>>::from_raw_buffers(
//!     vec![0, 1, 2, 3],
//!     vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)],
//!     4,
//! ).unwrap();
//! mesh.triangulate().unwrap();
//!
//! // Traverse an edge and use a circulator to get the faces of a nearby vertex.
//! let key = mesh.edges().nth(0).unwrap().key();
//! let mut vertex = mesh
//!     .edge_mut(key)
//!     .unwrap()
//!     .into_opposite_edge()
//!     .into_next_edge()
//!     .into_destination_vertex();
//! for face in vertex.neighboring_orphan_faces() {
//!     // `face.geometry` is mutable here.
//! }
//! # }
//! ```

mod container;
mod geometry;
mod mesh;
mod mutation;
mod storage;
mod topology;
mod view;

use failure::Error;

pub use self::mesh::Mesh;
pub use self::storage::{EdgeKey, FaceKey, VertexKey};
// TODO: It's unclear how view types should be exposed to users. Type aliases
//       for mutable, immutable, and orphan views over a `Mesh` would be
//       simpler and help insulate users from the complexity of views, but it
//       is currently not possible to document such aliases. See:
//       https://github.com/rust-lang/rust/issues/39437
//
//       Moreover, in the future it may be tenable to expose the internal
//       mutation APIs, and exposing the underlying view types would then be
//       necessary. For now, use them directly.
pub use self::view::{
    EdgeKeyTopology, EdgeView, FaceKeyTopology, FaceView, OrphanEdgeView, OrphanFaceView,
    OrphanVertexView, VertexView,
};

#[derive(Debug, Fail)]
pub enum GraphError {
    #[fail(display = "required topology not found")]
    TopologyNotFound,
    #[fail(display = "conflicting topology found")]
    TopologyConflict,
    #[fail(display = "topology malformed")]
    TopologyMalformed,
    #[fail(
        display = "conflicting arity; expected {}, but got {}",
        expected,
        actual
    )]
    ArityConflict { expected: usize, actual: usize },
    #[fail(display = "face arity is non-constant")]
    ArityNonConstant,
}

// TODO: Using `Error` so broadly is a misuse case. Look at how complex
//       `ResultExt` is! Just use `GraphError` directly. Never use `Error`
//       along "happy" or "expected" paths (this happens often in the
//       `mutation` module.

pub trait ResultExt<T>: Sized {
    fn or_if_conflict<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Result<T, Error>;
}

impl<T> ResultExt<T> for Result<T, Error> {
    fn or_if_conflict<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Result<T, Error>,
    {
        self.map_err(|error| error.downcast::<GraphError>().unwrap())
            .or_if_conflict(f)
    }
}

impl<T> ResultExt<T> for Result<T, GraphError> {
    fn or_if_conflict<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Result<T, Error>,
    {
        self.or_else(|error| match error {
            GraphError::TopologyConflict => f(),
            error => Err(error.into()),
        })
    }
}

trait IteratorExt: Iterator + Sized {
    /// Provides an iterator over a window of duplets that includes the first
    /// value in the sequence at the beginning and end of the iteration.
    ///
    /// Given a collection with ordered elements `a`, `b`, and `c`, this
    /// iterator yeilds the ordered items `(a, b)`, `(b, c)`, `(c, a)`.
    fn perimeter(self) -> Perimeter<Self>
    where
        Self::Item: Copy;
}

impl<I> IteratorExt for I
where
    I: Iterator,
{
    fn perimeter(self) -> Perimeter<I>
    where
        I::Item: Copy,
    {
        Perimeter::new(self)
    }
}

struct Perimeter<I>
where
    I: Iterator,
    I::Item: Copy,
{
    input: I,
    first: Option<I::Item>,
    previous: Option<I::Item>,
}

impl<I> Perimeter<I>
where
    I: Iterator,
    I::Item: Copy,
{
    fn new(mut input: I) -> Self {
        let first = input.next();
        let previous = first;
        Perimeter {
            input,
            first,
            previous,
        }
    }
}

impl<I> Iterator for Perimeter<I>
where
    I: Iterator,
    I::Item: Copy,
{
    type Item = (I::Item, I::Item);

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.input.next();
        match (self.previous, next.or_else(|| self.first.take())) {
            (Some(a), Some(b)) => {
                self.previous = Some(b);
                Some((a, b))
            }
            _ => None,
        }
    }
}
<commit_msg>Do not re-export `ResultExt`.<commit_after>//! Half-edge graph representation of meshes.
//!
//! This module provides a flexible representation of meshes as a [half-edge
//! graph](https://en.wikipedia.org/wiki/doubly_connected_edge_list). Meshes
//! can store arbitrary geometric data associated with any topological
//! structure, including vertices, half-edges, and faces.
//!
//! # Representation
//!
//! `Mesh`es store topological data using associative collections. Keys are
//! exposed as strongly typed and opaque values, which can be used to refer to
//! a topological structure, such as `VertexKey`. Topology is typically
//! manipulated using a view.
//!
//! A `Mesh` is conceptually composed of vertices, half-edges, and faces.
//! Half-edges are directed and join vertices. Every half-edge is paired with
//! an opposite half-edge with the opposite direction.  Given a half-edge that
//! connects a vertex `a` to a vertex `b`, that half-edge will have an opposite
//! half-edge from `b` to `a`. Together, these half-edges form a composite
//! edge. When the term "edge" is used alone, it generally refers to a
//! half-edge.
//!
//! Half-edges are connected to their neighbors, known as "next" and "previous"
//! half-edges. When a face is present in the region formed by a perimeter of
//! vertices and their half-edges, the half-edges will refer to that face and
//! the face will refer to one of the half-edges in the interior circuit.
//!
//! Together with vertices and faces, the connectivity of half-edges allows for
//! effecient traversal of topology. For example, it becomes trivial to find
//! neighboring topologies, such as the faces that share a given vertex or the
//! neighboring faces of a given face.
//!
//! # Topological Views
//!
//! `Mesh`es expose views over their topological structures (vertices, edges,
//! and faces). Views are accessed via keys or iteration and behave similarly
//! to references. They provide the primary API for interacting with a mesh's
//! topology and geometry. There are three types summarized below:
//!
//! | Type      | Traversal | Exclusive | Geometry  | Topology  |
//! |-----------|-----------|-----------|-----------|-----------|
//! | Immutable | Yes       | No        | Immutable | Immutable |
//! | Mutable   | Yes       | Yes       | Mutable   | Mutable   |
//! | Orphan    | No        | No        | Mutable   | N/A       |
//!
//! Immutable and mutable views are much like references. Immutable views
//! cannot mutate a mesh in any way and it is possible to obtain multiple such
//! views at the same time. Mutable views are exclusive, but allow for
//! mutations.
//!
//! Orphan views are similar to mutable views, but they only have access to the
//! geometry of a specific topological structure in a mesh. Because they do not
//! know about other vertices, edges, or faces, an orphan view cannot traverse
//! the topology of a mesh in any way. These views are most useful for
//! modifying the geometry of a mesh and, unlike mutable views, multiple orphan
//! views can be obtained at the same time. Orphan views are mostly used by
//! circulators (iterators).
//!
//! Immutable and mutable views are represented by a single type constructor,
//! such as `FaceView`.  Orphan views are represented by their own type, such
//! as `OrphanFace`.
//!
//! # Circulators
//!
//! Topological views allow for traversals of a mesh's topology. One useful
//! type of traversal uses a circulator, which is a type of iterator that
//! examines the neighbors of a topological structure. For example, the face
//! circulator of a vertex yields all faces that share that vertex in order.
//!
//! Mutable circulators emit orphan views, not mutable views. This is because
//! it is not possible to instantiate more than one mutable view at a time. If
//! multiple mutable views are needed, it is possible to use an immutable
//! circulator to collect the keys of the target topology and then lookup each
//! mutable view using those keys.
//!
//! # Examples
//!
//! Generating a mesh from a primitive:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! use nalgebra::Point3;
//! use plexus::graph::Mesh;
//! use plexus::prelude::*;
//! use plexus::primitive::sphere::UvSphere;
//!
//! # fn main() {
//! let mut mesh = UvSphere::new(16, 16)
//!     .polygons_with_position()
//!     .collect::<Mesh<Point3<f32>>>();
//! # }
//! ```
//!
//! Manipulating a face in a mesh:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! use nalgebra::Point3;
//! use plexus::graph::Mesh;
//! use plexus::prelude::*;
//! use plexus::primitive::sphere::UvSphere;
//!
//! # fn main() {
//! let mut mesh = UvSphere::new(16, 16)
//!     .polygons_with_position()
//!     .collect::<Mesh<Point3<f32>>>();
//! let key = mesh.faces().nth(0).unwrap().key(); // Get the key of the first face.
//! mesh.face_mut(key).unwrap().extrude(1.0).unwrap(); // Extrude the face.
//! # }
//! ```
//!
//! Traversing and circulating over a mesh:
//!
//! ```rust
//! # extern crate nalgebra;
//! # extern crate plexus;
//! use nalgebra::Point2;
//! use plexus::graph::Mesh;
//! use plexus::prelude::*;
//!
//! # fn main() {
//! let mut mesh = Mesh::<Point2<f32>>::from_raw_buffers(
//!     vec![0, 1, 2, 3],
//!     vec![(0.0, 0.0), (1.0, 0.0), (1.0, 1.0), (0.0, 1.0)],
//!     4,
//! ).unwrap();
//! mesh.triangulate().unwrap();
//!
//! // Traverse an edge and use a circulator to get the faces of a nearby vertex.
//! let key = mesh.edges().nth(0).unwrap().key();
//! let mut vertex = mesh
//!     .edge_mut(key)
//!     .unwrap()
//!     .into_opposite_edge()
//!     .into_next_edge()
//!     .into_destination_vertex();
//! for face in vertex.neighboring_orphan_faces() {
//!     // `face.geometry` is mutable here.
//! }
//! # }
//! ```

mod container;
mod geometry;
mod mesh;
mod mutation;
mod storage;
mod topology;
mod view;

use failure::Error;

pub use self::mesh::Mesh;
pub use self::storage::{EdgeKey, FaceKey, VertexKey};
// TODO: It's unclear how view types should be exposed to users. Type aliases
//       for mutable, immutable, and orphan views over a `Mesh` would be
//       simpler and help insulate users from the complexity of views, but it
//       is currently not possible to document such aliases. See:
//       https://github.com/rust-lang/rust/issues/39437
//
//       Moreover, in the future it may be tenable to expose the internal
//       mutation APIs, and exposing the underlying view types would then be
//       necessary. For now, use them directly.
pub use self::view::{
    EdgeKeyTopology, EdgeView, FaceKeyTopology, FaceView, OrphanEdgeView, OrphanFaceView,
    OrphanVertexView, VertexView,
};

#[derive(Debug, Fail)]
pub enum GraphError {
    #[fail(display = "required topology not found")]
    TopologyNotFound,
    #[fail(display = "conflicting topology found")]
    TopologyConflict,
    #[fail(display = "topology malformed")]
    TopologyMalformed,
    #[fail(
        display = "conflicting arity; expected {}, but got {}",
        expected,
        actual
    )]
    ArityConflict { expected: usize, actual: usize },
    #[fail(display = "face arity is non-constant")]
    ArityNonConstant,
}

// TODO: Using `Error` so broadly is a misuse case. Look at how complex
//       `ResultExt` is! Just use `GraphError` directly. Never use `Error`
//       along "happy" or "expected" paths (this happens often in the
//       `mutation` module.

trait ResultExt<T>: Sized {
    /// If the `Result` is an error with the value
    /// `GraphError::TopologyConflict`, then the given function is executed and
    /// its `Result` is returned. Otherwise, the original `Result` is returned.
    ///
    /// Because this operates on the result of an operation, it does not cancel
    /// or negate any mutations that may have occurred.
    fn or_if_conflict<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Result<T, Error>;
}

impl<T> ResultExt<T> for Result<T, Error> {
    fn or_if_conflict<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Result<T, Error>,
    {
        self.map_err(|error| error.downcast::<GraphError>().unwrap())
            .or_if_conflict(f)
    }
}

impl<T> ResultExt<T> for Result<T, GraphError> {
    fn or_if_conflict<F>(self, f: F) -> Result<T, Error>
    where
        F: FnOnce() -> Result<T, Error>,
    {
        self.or_else(|error| match error {
            GraphError::TopologyConflict => f(),
            error => Err(error.into()),
        })
    }
}

trait IteratorExt: Iterator + Sized {
    /// Provides an iterator over a window of duplets that includes the first
    /// value in the sequence at the beginning and end of the iteration.
    ///
    /// Given a collection with ordered elements `a`, `b`, and `c`, this
    /// iterator yeilds the ordered items `(a, b)`, `(b, c)`, `(c, a)`.
    fn perimeter(self) -> Perimeter<Self>
    where
        Self::Item: Copy;
}

impl<I> IteratorExt for I
where
    I: Iterator,
{
    fn perimeter(self) -> Perimeter<I>
    where
        I::Item: Copy,
    {
        Perimeter::new(self)
    }
}

struct Perimeter<I>
where
    I: Iterator,
    I::Item: Copy,
{
    input: I,
    first: Option<I::Item>,
    previous: Option<I::Item>,
}

impl<I> Perimeter<I>
where
    I: Iterator,
    I::Item: Copy,
{
    fn new(mut input: I) -> Self {
        let first = input.next();
        let previous = first;
        Perimeter {
            input,
            first,
            previous,
        }
    }
}

impl<I> Iterator for Perimeter<I>
where
    I: Iterator,
    I::Item: Copy,
{
    type Item = (I::Item, I::Item);

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.input.next();
        match (self.previous, next.or_else(|| self.first.take())) {
            (Some(a), Some(b)) => {
                self.previous = Some(b);
                Some((a, b))
            }
            _ => None,
        }
    }
}
<|endoftext|>


<commit_before>use time::precise_time_ns;

use std::sync::mpsc::{channel, Sender};
use std::thread::Thread;

use search;
use timer;

use super::types::{Cmd, Response};
use super::state::{State, Mode};

mod go_param;

pub fn process(state: &mut State, cmd: Cmd, output: &Sender<Response>) {
    match cmd {
        Cmd::Debug(val) => {
            state.is_debug = val;
            state.search_tx.as_mut().map(|tx| { let _ = tx.send(search::Cmd::Stop); } );
        },
        Cmd::IsReady => {
            // TODO implement IsReady
            unimplemented!();
        },
        Cmd::Register(ref param) => {
            // TODO register
            unimplemented!();
        },
        cmd => {
            match state.mode {
                Mode::Init => {
                    if cmd == Cmd::Uci {
                        // TODO print id/option/uciok
                        state.mode = Mode::Wait;
                        unimplemented!();
                    }
                },
                Mode::Wait => {
                    match cmd {
                        Cmd::SetOption(ref name, ref val_opt) => {
                            // TODO maybe initialize
                            // TODO set options
                            unimplemented!();
                        },
                        Cmd::UciNewGame => {
                            // TODO note that ucinewgame supported
                            // TODO reset game status
                            state.mode = Mode::NewGame;
                            unimplemented!();
                        },
                        cmd @Cmd::SetupPosition(..) => {
                            if state.ucinewgame_support {
                                // TODO set up position for same game
                                state.mode = Mode::Ready;
                                unimplemented!();
                            } else {
                                process(state, Cmd::UciNewGame, output);
                                state.ucinewgame_support = false;
                                process(state, cmd, output);
                            }
                        },
                        _ => {},
                    }
                },
                Mode::NewGame => {
                    if let Cmd::SetupPosition(ref pos, ref from_to_vec) = cmd {
                        // TODO setup position for new game
                        state.mode = Mode::Ready;
                        unimplemented!();
                    }
                },
                Mode::Ready => {
                    assert!(state.search_state.is_some());
                    if let Cmd::Go(param) = cmd {
                        go_param::setup(state, param);
                        let (search_tx, search_rx) = channel::<search::Cmd>();
                        let search_state = state.search_state.as_ref().unwrap().clone();
                        let output = output.clone();
                        let temp = Thread::scoped(move ||
                                                  search::start(search_state, search_rx, output));

                        state.search_tx = Some(search_tx.clone());
                        state.search_guard = Some(temp);

                        let time_data = state.time_data.clone().unwrap();
                        let c = state.search_state.as_ref().unwrap().pos.side_to_move();

                        Thread::spawn(move || timer::start(time_data, c, search_tx));

                        state.mode = Mode::Search;
                    }
                },
                Mode::Search => {
                    assert!(state.search_state.is_some());
                    assert!(state.search_guard.is_some());
                    assert!(state.search_tx.is_some());
                    match cmd {
                        Cmd::PonderHit => {
                            state.search_tx.as_ref().map(|ref tx| tx.send(search::Cmd::PonderHit));
                            state.search_state.as_mut().map(|ref mut x| x.param.ponder = false);
                            state.start_move_time = Some(precise_time_ns());
                        },
                        Cmd::Stop => {
                            state.search_tx.as_mut().map(|ref tx| tx.send(search::Cmd::Stop));
                            state.search_guard.take().map(|x| x.join());
                            state.search_tx = None;
                            state.mode = Mode::Wait;
                            state.start_search_time = None;
                            state.start_move_time = None;
                            state.time_data = None;
                            // TODO what to do about search_state
                            // TODO what to do about the timer thread
                            // TODO what if time is out
                            unimplemented!();
                        },
                        _ => {},
                    }
                },
            }
        },
    }
}
<commit_msg>Change unnecessary as_mut() to as_ref()<commit_after>use time::precise_time_ns;

use std::sync::mpsc::{channel, Sender};
use std::thread::Thread;

use search;
use timer;

use super::types::{Cmd, Response};
use super::state::{State, Mode};

mod go_param;

pub fn process(state: &mut State, cmd: Cmd, output: &Sender<Response>) {
    match cmd {
        Cmd::Debug(val) => {
            state.is_debug = val;
            state.search_tx.as_ref().map(|tx| { let _ = tx.send(search::Cmd::Stop); } );
        },
        Cmd::IsReady => {
            // TODO implement IsReady
            unimplemented!();
        },
        Cmd::Register(ref param) => {
            // TODO register
            unimplemented!();
        },
        cmd => {
            match state.mode {
                Mode::Init => {
                    if cmd == Cmd::Uci {
                        // TODO print id/option/uciok
                        state.mode = Mode::Wait;
                        unimplemented!();
                    }
                },
                Mode::Wait => {
                    match cmd {
                        Cmd::SetOption(ref name, ref val_opt) => {
                            // TODO maybe initialize
                            // TODO set options
                            unimplemented!();
                        },
                        Cmd::UciNewGame => {
                            // TODO note that ucinewgame supported
                            // TODO reset game status
                            state.mode = Mode::NewGame;
                            unimplemented!();
                        },
                        cmd @Cmd::SetupPosition(..) => {
                            if state.ucinewgame_support {
                                // TODO set up position for same game
                                state.mode = Mode::Ready;
                                unimplemented!();
                            } else {
                                process(state, Cmd::UciNewGame, output);
                                state.ucinewgame_support = false;
                                process(state, cmd, output);
                            }
                        },
                        _ => {},
                    }
                },
                Mode::NewGame => {
                    if let Cmd::SetupPosition(ref pos, ref from_to_vec) = cmd {
                        // TODO setup position for new game
                        state.mode = Mode::Ready;
                        unimplemented!();
                    }
                },
                Mode::Ready => {
                    assert!(state.search_state.is_some());
                    if let Cmd::Go(param) = cmd {
                        go_param::setup(state, param);
                        let (search_tx, search_rx) = channel::<search::Cmd>();
                        let search_state = state.search_state.as_ref().unwrap().clone();
                        let output = output.clone();
                        let temp = Thread::scoped(move ||
                                                  search::start(search_state, search_rx, output));

                        state.search_tx = Some(search_tx.clone());
                        state.search_guard = Some(temp);

                        let time_data = state.time_data.clone().unwrap();
                        let c = state.search_state.as_ref().unwrap().pos.side_to_move();

                        Thread::spawn(move || timer::start(time_data, c, search_tx));

                        state.mode = Mode::Search;
                    }
                },
                Mode::Search => {
                    assert!(state.search_state.is_some());
                    assert!(state.search_guard.is_some());
                    assert!(state.search_tx.is_some());
                    match cmd {
                        Cmd::PonderHit => {
                            state.search_tx.as_ref().map(|ref tx| tx.send(search::Cmd::PonderHit));
                            state.search_state.as_mut().map(|ref mut x| x.param.ponder = false);
                            state.start_move_time = Some(precise_time_ns());
                        },
                        Cmd::Stop => {
                            state.search_tx.as_mut().map(|ref tx| tx.send(search::Cmd::Stop));
                            state.search_guard.take().map(|x| x.join());
                            state.search_tx = None;
                            state.mode = Mode::Wait;
                            state.start_search_time = None;
                            state.start_move_time = None;
                            state.time_data = None;
                            // TODO what to do about search_state
                            // TODO what to do about the timer thread
                            // TODO what if time is out
                            unimplemented!();
                        },
                        _ => {},
                    }
                },
            }
        },
    }
}
<|endoftext|>


<commit_before>extern crate ncurses;

use self::super::find_closest;
use backend;
use event::{Event, Key};
use std::cell::{RefCell, Cell};
use std::collections::HashMap;
use theme::{Color, ColorPair, Effect};
use utf8;

pub struct Concrete {
    current_style: Cell<ColorPair>,
    pairs: RefCell<HashMap<ColorPair, i16>>,
}

impl Concrete {
    /// Save a new color pair.
    fn insert_color(&self, pairs: &mut HashMap<ColorPair, i16>,
                    pair: ColorPair)
                    -> i16 {

        let n = 1 + pairs.len() as i16;
        let target = if ncurses::COLOR_PAIRS() > n as i32 {
            // We still have plenty of space for everyone.
            n
        } else {
            // The world is too small for both of us.
            let target = n - 1;
            // Remove the mapping to n-1
            pairs.retain(|_, &mut v| v != target);
            target
        };
        pairs.insert(pair, target);
        ncurses::init_pair(target,
                           find_closest(&pair.front),
                           find_closest(&pair.back));
        target
    }

    /// Checks the pair in the cache, or re-define a color if needed.
    fn get_or_create(&self, pair: ColorPair) -> i16 {

        let mut pairs = self.pairs.borrow_mut();

        // Find if we have this color in stock
        if pairs.contains_key(&pair) {
            // We got it!
            pairs[&pair]
        } else {
            self.insert_color(&mut *pairs, pair)
        }
    }

    fn set_colors(&self, pair: ColorPair) {

        let i = self.get_or_create(pair);

        self.current_style.set(pair);
        let style = ncurses::COLOR_PAIR(i);
        ncurses::attron(style);
    }
}

impl backend::Backend for Concrete {
    fn init() -> Self {
        // The delay is the time ncurses wait after pressing ESC
        // to see if it's an escape sequence.
        // Default delay is way too long. 25 is imperceptible yet works fine.
        ncurses::setlocale(ncurses::LcCategory::all, "");
        ::std::env::set_var("ESCDELAY", "25");
        ncurses::initscr();
        ncurses::keypad(ncurses::stdscr(), true);
        ncurses::noecho();
        ncurses::cbreak();
        ncurses::start_color();
        ncurses::use_default_colors();
        ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);

        Concrete {
            current_style: Cell::new(ColorPair::from_256colors(0, 0)),
            pairs: RefCell::new(HashMap::new()),
        }
    }

    fn screen_size(&self) -> (usize, usize) {
        let mut x: i32 = 0;
        let mut y: i32 = 0;
        ncurses::getmaxyx(ncurses::stdscr(), &mut y, &mut x);
        (x as usize, y as usize)
    }

    fn has_colors(&self) -> bool {
        ncurses::has_colors()
    }

    fn finish(&mut self) {
        ncurses::endwin();
    }

    fn with_color<F: FnOnce()>(&self, colors: ColorPair, f: F) {
        let current = self.current_style.get();
        if current != colors {
            self.set_colors(colors);
        }

        f();

        if current != colors {
            self.set_colors(current);
        }
    }

    fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
        let style = match effect {
            Effect::Reverse => ncurses::A_REVERSE(),
            Effect::Simple => ncurses::A_NORMAL(),
        };
        ncurses::attron(style);
        f();
        ncurses::attroff(style);
    }

    fn clear(&self, color: Color) {
        let id = self.get_or_create(ColorPair {
                                        front: color,
                                        back: color,
                                    });
        ncurses::wbkgd(ncurses::stdscr(), ncurses::COLOR_PAIR(id));

        ncurses::clear();
    }

    fn refresh(&mut self) {
        ncurses::refresh();
    }

    fn print_at(&self, (x, y): (usize, usize), text: &str) {
        ncurses::mvaddstr(y as i32, x as i32, text);
    }

    fn poll_event(&self) -> Event {
        let ch: i32 = ncurses::getch();

        // Is it a UTF-8 starting point?
        if 32 <= ch && ch <= 255 && ch != 127 {
            Event::Char(utf8::read_char(ch as u8,
                                        || Some(ncurses::getch() as u8))
                                .unwrap())
        } else {
            parse_ncurses_char(ch)
        }
    }

    fn set_refresh_rate(&mut self, fps: u32) {
        if fps == 0 {
            ncurses::timeout(-1);
        } else {
            ncurses::timeout(1000 / fps as i32);
        }
    }
}

/// Returns the Key enum corresponding to the given ncurses event.
fn parse_ncurses_char(ch: i32) -> Event {
    match ch {
        // Value sent by ncurses when nothing happens
        -1 => Event::Refresh,

        // Values under 256 are chars and control values
        //
        // Tab is '\t'
        9 => Event::Key(Key::Tab),
        // Treat '\n' and the numpad Enter the same
        10 |
        ncurses::KEY_ENTER => Event::Key(Key::Enter),
        // This is the escape key when pressed by itself.
        // When used for control sequences, it should have been caught earlier.
        27 => Event::Key(Key::Esc),
        // `Backspace` sends 127, but Ctrl-H sends `Backspace`
        127 |
        ncurses::KEY_BACKSPACE => Event::Key(Key::Backspace),

        410 => Event::WindowResize,

        // Values 512 and above are probably extensions
        // Those keys don't seem to be documented...
        519 => Event::Alt(Key::Del),
        520 => Event::AltShift(Key::Del),
        521 => Event::Ctrl(Key::Del),
        522 => Event::CtrlShift(Key::Del),
        // 523: CtrlAltDel?
        //
        // 524?
        525 => Event::Alt(Key::Down),
        526 => Event::AltShift(Key::Down),
        527 => Event::Ctrl(Key::Down),
        528 => Event::CtrlShift(Key::Down),
        529 => Event::CtrlAlt(Key::Down),

        530 => Event::Alt(Key::End),
        531 => Event::AltShift(Key::End),
        532 => Event::Ctrl(Key::End),
        533 => Event::CtrlShift(Key::End),
        534 => Event::CtrlAlt(Key::End),

        535 => Event::Alt(Key::Home),
        536 => Event::AltShift(Key::Home),
        537 => Event::Ctrl(Key::Home),
        538 => Event::CtrlShift(Key::Home),
        539 => Event::CtrlAlt(Key::Home),

        540 => Event::Alt(Key::Ins),
        // 541: AltShiftIns?
        542 => Event::Ctrl(Key::Ins),
        // 543: CtrlShiftIns?
        544 => Event::CtrlAlt(Key::Ins),

        545 => Event::Alt(Key::Left),
        546 => Event::AltShift(Key::Left),
        547 => Event::Ctrl(Key::Left),
        548 => Event::CtrlShift(Key::Left),
        549 => Event::CtrlAlt(Key::Left),

        550 => Event::Alt(Key::PageDown),
        551 => Event::AltShift(Key::PageDown),
        552 => Event::Ctrl(Key::PageDown),
        553 => Event::CtrlShift(Key::PageDown),
        554 => Event::CtrlAlt(Key::PageDown),

        555 => Event::Alt(Key::PageUp),
        556 => Event::AltShift(Key::PageUp),
        557 => Event::Ctrl(Key::PageUp),
        558 => Event::CtrlShift(Key::PageUp),
        559 => Event::CtrlAlt(Key::PageUp),

        560 => Event::Alt(Key::Right),
        561 => Event::AltShift(Key::Right),
        562 => Event::Ctrl(Key::Right),
        563 => Event::CtrlShift(Key::Right),
        564 => Event::CtrlAlt(Key::Right),
        // 565?
        566 => Event::Alt(Key::Up),
        567 => Event::AltShift(Key::Up),
        568 => Event::Ctrl(Key::Up),
        569 => Event::CtrlShift(Key::Up),
        570 => Event::CtrlAlt(Key::Up),

        ncurses::KEY_B2 => Event::Key(Key::NumpadCenter),
        ncurses::KEY_DC => Event::Key(Key::Del),
        ncurses::KEY_IC => Event::Key(Key::Ins),
        ncurses::KEY_BTAB => Event::Shift(Key::Tab),
        ncurses::KEY_SLEFT => Event::Shift(Key::Left),
        ncurses::KEY_SRIGHT => Event::Shift(Key::Right),
        ncurses::KEY_LEFT => Event::Key(Key::Left),
        ncurses::KEY_RIGHT => Event::Key(Key::Right),
        ncurses::KEY_UP => Event::Key(Key::Up),
        ncurses::KEY_DOWN => Event::Key(Key::Down),
        ncurses::KEY_SR => Event::Shift(Key::Up),
        ncurses::KEY_SF => Event::Shift(Key::Down),
        ncurses::KEY_PPAGE => Event::Key(Key::PageUp),
        ncurses::KEY_NPAGE => Event::Key(Key::PageDown),
        ncurses::KEY_HOME => Event::Key(Key::Home),
        ncurses::KEY_END => Event::Key(Key::End),
        ncurses::KEY_SHOME => Event::Shift(Key::Home),
        ncurses::KEY_SEND => Event::Shift(Key::End),
        ncurses::KEY_SDC => Event::Shift(Key::Del),
        ncurses::KEY_SNEXT => Event::Shift(Key::PageDown),
        ncurses::KEY_SPREVIOUS => Event::Shift(Key::PageUp),
        // All Fn keys use the same enum with associated number
        f @ ncurses::KEY_F1...ncurses::KEY_F12 => {
            Event::Key(Key::from_f((f - ncurses::KEY_F0) as u8))
        }
        f @ 277...288 => Event::Shift(Key::from_f((f - 276) as u8)),
        f @ 289...300 => Event::Ctrl(Key::from_f((f - 288) as u8)),
        f @ 301...312 => Event::CtrlShift(Key::from_f((f - 300) as u8)),
        f @ 313...324 => Event::Alt(Key::from_f((f - 312) as u8)),
        // Values 8-10 (H,I,J) are used by other commands,
        // so we probably won't receive them. Meh~
        c @ 1...25 => Event::CtrlChar((b'a' + (c - 1) as u8) as char),
        other => {
            // Split the i32 into 4 bytes
            Event::Unknown((0..4)
                               .map(|i| ((other >> (8 * i)) & 0xFF) as u8)
                               .collect())
        }
    }
}
<commit_msg>Fix ncurses input key codes<commit_after>extern crate ncurses;

use self::super::find_closest;
use backend;
use event::{Event, Key};
use std::cell::{RefCell, Cell};
use std::collections::HashMap;
use theme::{Color, ColorPair, Effect};
use utf8;

pub struct Concrete {
    current_style: Cell<ColorPair>,
    pairs: RefCell<HashMap<ColorPair, i16>>,
}

impl Concrete {
    /// Save a new color pair.
    fn insert_color(&self, pairs: &mut HashMap<ColorPair, i16>,
                    pair: ColorPair)
                    -> i16 {

        let n = 1 + pairs.len() as i16;
        let target = if ncurses::COLOR_PAIRS() > n as i32 {
            // We still have plenty of space for everyone.
            n
        } else {
            // The world is too small for both of us.
            let target = n - 1;
            // Remove the mapping to n-1
            pairs.retain(|_, &mut v| v != target);
            target
        };
        pairs.insert(pair, target);
        ncurses::init_pair(target,
                           find_closest(&pair.front),
                           find_closest(&pair.back));
        target
    }

    /// Checks the pair in the cache, or re-define a color if needed.
    fn get_or_create(&self, pair: ColorPair) -> i16 {

        let mut pairs = self.pairs.borrow_mut();

        // Find if we have this color in stock
        if pairs.contains_key(&pair) {
            // We got it!
            pairs[&pair]
        } else {
            self.insert_color(&mut *pairs, pair)
        }
    }

    fn set_colors(&self, pair: ColorPair) {

        let i = self.get_or_create(pair);

        self.current_style.set(pair);
        let style = ncurses::COLOR_PAIR(i);
        ncurses::attron(style);
    }
}

impl backend::Backend for Concrete {
    fn init() -> Self {
        // The delay is the time ncurses wait after pressing ESC
        // to see if it's an escape sequence.
        // Default delay is way too long. 25 is imperceptible yet works fine.
        ncurses::setlocale(ncurses::LcCategory::all, "");
        ::std::env::set_var("ESCDELAY", "25");
        ncurses::initscr();
        ncurses::keypad(ncurses::stdscr(), true);
        ncurses::noecho();
        ncurses::cbreak();
        ncurses::start_color();
        ncurses::use_default_colors();
        ncurses::curs_set(ncurses::CURSOR_VISIBILITY::CURSOR_INVISIBLE);

        Concrete {
            current_style: Cell::new(ColorPair::from_256colors(0, 0)),
            pairs: RefCell::new(HashMap::new()),
        }
    }

    fn screen_size(&self) -> (usize, usize) {
        let mut x: i32 = 0;
        let mut y: i32 = 0;
        ncurses::getmaxyx(ncurses::stdscr(), &mut y, &mut x);
        (x as usize, y as usize)
    }

    fn has_colors(&self) -> bool {
        ncurses::has_colors()
    }

    fn finish(&mut self) {
        ncurses::endwin();
    }

    fn with_color<F: FnOnce()>(&self, colors: ColorPair, f: F) {
        let current = self.current_style.get();
        if current != colors {
            self.set_colors(colors);
        }

        f();

        if current != colors {
            self.set_colors(current);
        }
    }

    fn with_effect<F: FnOnce()>(&self, effect: Effect, f: F) {
        let style = match effect {
            Effect::Reverse => ncurses::A_REVERSE(),
            Effect::Simple => ncurses::A_NORMAL(),
        };
        ncurses::attron(style);
        f();
        ncurses::attroff(style);
    }

    fn clear(&self, color: Color) {
        let id = self.get_or_create(ColorPair {
                                        front: color,
                                        back: color,
                                    });
        ncurses::wbkgd(ncurses::stdscr(), ncurses::COLOR_PAIR(id));

        ncurses::clear();
    }

    fn refresh(&mut self) {
        ncurses::refresh();
    }

    fn print_at(&self, (x, y): (usize, usize), text: &str) {
        ncurses::mvaddstr(y as i32, x as i32, text);
    }

    fn poll_event(&self) -> Event {
        let ch: i32 = ncurses::getch();

        // Is it a UTF-8 starting point?
        if 32 <= ch && ch <= 255 && ch != 127 {
            Event::Char(utf8::read_char(ch as u8,
                                        || Some(ncurses::getch() as u8))
                                .unwrap())
        } else {
            parse_ncurses_char(ch)
        }
    }

    fn set_refresh_rate(&mut self, fps: u32) {
        if fps == 0 {
            ncurses::timeout(-1);
        } else {
            ncurses::timeout(1000 / fps as i32);
        }
    }
}

/// Returns the Key enum corresponding to the given ncurses event.
fn parse_ncurses_char(ch: i32) -> Event {
    match ch {
        // Value sent by ncurses when nothing happens
        -1 => Event::Refresh,

        // Values under 256 are chars and control values
        //
        // Tab is '\t'
        9 => Event::Key(Key::Tab),
        // Treat '\n' and the numpad Enter the same
        10 |
        ncurses::KEY_ENTER => Event::Key(Key::Enter),
        // This is the escape key when pressed by itself.
        // When used for control sequences, it should have been caught earlier.
        27 => Event::Key(Key::Esc),
        // `Backspace` sends 127, but Ctrl-H sends `Backspace`
        127 |
        ncurses::KEY_BACKSPACE => Event::Key(Key::Backspace),

        410 => Event::WindowResize,

        // Values 512 and above are probably extensions
        // Those keys don't seem to be documented...
        520 => Event::Alt(Key::Del),
        521 => Event::AltShift(Key::Del),
        522 => Event::Ctrl(Key::Del),
        523 => Event::CtrlShift(Key::Del),
        //
        // 524?
        526 => Event::Alt(Key::Down),
        527 => Event::AltShift(Key::Down),
        528 => Event::Ctrl(Key::Down),
        529 => Event::CtrlShift(Key::Down),
        530 => Event::CtrlAlt(Key::Down),

        531 => Event::Alt(Key::End),
        532 => Event::AltShift(Key::End),
        533 => Event::Ctrl(Key::End),
        534 => Event::CtrlShift(Key::End),
        535 => Event::CtrlAlt(Key::End),

        536 => Event::Alt(Key::Home),
        537 => Event::AltShift(Key::Home),
        538 => Event::Ctrl(Key::Home),
        539 => Event::CtrlShift(Key::Home),
        540 => Event::CtrlAlt(Key::Home),

        541 => Event::Alt(Key::Ins),
        542 => Event::AltShift(Key::Ins),
        543 => Event::Ctrl(Key::Ins),
        // 544: CtrlShiftIns?
        545 => Event::CtrlAlt(Key::Ins),

        546 => Event::Alt(Key::Left),
        547 => Event::AltShift(Key::Left),
        548 => Event::Ctrl(Key::Left),
        549 => Event::CtrlShift(Key::Left),
        550 => Event::CtrlAlt(Key::Left),

        551 => Event::Alt(Key::PageDown),
        552 => Event::AltShift(Key::PageDown),
        553 => Event::Ctrl(Key::PageDown),
        554 => Event::CtrlShift(Key::PageDown),
        555 => Event::CtrlAlt(Key::PageDown),

        556 => Event::Alt(Key::PageUp),
        557 => Event::AltShift(Key::PageUp),
        558 => Event::Ctrl(Key::PageUp),
        559 => Event::CtrlShift(Key::PageUp),
        560 => Event::CtrlAlt(Key::PageUp),

        561 => Event::Alt(Key::Right),
        562 => Event::AltShift(Key::Right),
        563 => Event::Ctrl(Key::Right),
        564 => Event::CtrlShift(Key::Right),
        565 => Event::CtrlAlt(Key::Right),
        // 566?
        567 => Event::Alt(Key::Up),
        568 => Event::AltShift(Key::Up),
        569 => Event::Ctrl(Key::Up),
        570 => Event::CtrlShift(Key::Up),
        571 => Event::CtrlAlt(Key::Up),

        ncurses::KEY_B2 => Event::Key(Key::NumpadCenter),
        ncurses::KEY_DC => Event::Key(Key::Del),
        ncurses::KEY_IC => Event::Key(Key::Ins),
        ncurses::KEY_BTAB => Event::Shift(Key::Tab),
        ncurses::KEY_SLEFT => Event::Shift(Key::Left),
        ncurses::KEY_SRIGHT => Event::Shift(Key::Right),
        ncurses::KEY_LEFT => Event::Key(Key::Left),
        ncurses::KEY_RIGHT => Event::Key(Key::Right),
        ncurses::KEY_UP => Event::Key(Key::Up),
        ncurses::KEY_DOWN => Event::Key(Key::Down),
        ncurses::KEY_SR => Event::Shift(Key::Up),
        ncurses::KEY_SF => Event::Shift(Key::Down),
        ncurses::KEY_PPAGE => Event::Key(Key::PageUp),
        ncurses::KEY_NPAGE => Event::Key(Key::PageDown),
        ncurses::KEY_HOME => Event::Key(Key::Home),
        ncurses::KEY_END => Event::Key(Key::End),
        ncurses::KEY_SHOME => Event::Shift(Key::Home),
        ncurses::KEY_SEND => Event::Shift(Key::End),
        ncurses::KEY_SDC => Event::Shift(Key::Del),
        ncurses::KEY_SNEXT => Event::Shift(Key::PageDown),
        ncurses::KEY_SPREVIOUS => Event::Shift(Key::PageUp),
        // All Fn keys use the same enum with associated number
        f @ ncurses::KEY_F1...ncurses::KEY_F12 => {
            Event::Key(Key::from_f((f - ncurses::KEY_F0) as u8))
        }
        f @ 277...288 => Event::Shift(Key::from_f((f - 276) as u8)),
        f @ 289...300 => Event::Ctrl(Key::from_f((f - 288) as u8)),
        f @ 301...312 => Event::CtrlShift(Key::from_f((f - 300) as u8)),
        f @ 313...324 => Event::Alt(Key::from_f((f - 312) as u8)),
        // Values 8-10 (H,I,J) are used by other commands,
        // so we probably won't receive them. Meh~
        c @ 1...25 => Event::CtrlChar((b'a' + (c - 1) as u8) as char),
        other => {
            // Split the i32 into 4 bytes
            Event::Unknown((0..4)
                               .map(|i| ((other >> (8 * i)) & 0xFF) as u8)
                               .collect())
        }
    }
}
<|endoftext|>


<commit_before>#![deny(clippy::all, clippy::pedantic)]

use std::fmt::Display;
use thiserror::Error;

// Some of the elaborate cases from the rcc codebase, which is a C compiler in
// Rust. https://github.com/jyn514/rcc/blob/0.8.0/src/data/error.rs
#[derive(Error, Debug)]
pub enum CompilerError {
    #[error("cannot shift {} by {maximum} or more bits (got {current})", if *.is_left { "left" } else { "right" })]
    TooManyShiftBits {
        is_left: bool,
        maximum: u64,
        current: u64,
    },

    #[error("#error {}", (.0).iter().copied().collect::<Vec<_>>().join(" "))]
    User(Vec<&'static str>),

    #[error("overflow while parsing {}integer literal",
        if let Some(signed) = .is_signed {
            if *signed { "signed "} else { "unsigned "}
        } else {
            ""
        }
    )]
    IntegerOverflow { is_signed: Option<bool> },

    #[error("overflow while parsing {}integer literal", match .is_signed {
        Some(true) => "signed ",
        Some(false) => "unsigned ",
        None => "",
    })]
    IntegerOverflow2 { is_signed: Option<bool> },
}

// Examples drawn from Rustup.
#[derive(Error, Debug)]
pub enum RustupError {
    #[error(
        "toolchain '{name}' does not contain component {component}{}",
        .suggestion
            .as_ref()
            .map_or_else(String::new, |s| format!("; did you mean '{}'?", s)),
    )]
    UnknownComponent {
        name: String,
        component: String,
        suggestion: Option<String>,
    },
}

fn assert<T: Display>(expected: &str, value: T) {
    assert_eq!(expected, value.to_string());
}

#[test]
fn test_rcc() {
    assert(
        "cannot shift left by 32 or more bits (got 50)",
        CompilerError::TooManyShiftBits {
            is_left: true,
            maximum: 32,
            current: 50,
        },
    );

    assert("#error A B C", CompilerError::User(vec!["A", "B", "C"]));

    assert(
        "overflow while parsing signed integer literal",
        CompilerError::IntegerOverflow {
            is_signed: Some(true),
        },
    );
}

#[test]
fn test_rustup() {
    assert(
        "toolchain 'nightly' does not contain component clipy; did you mean 'clippy'?",
        RustupError::UnknownComponent {
            name: "nightly".to_owned(),
            component: "clipy".to_owned(),
            suggestion: Some("clippy".to_owned()),
        },
    );
}
<commit_msg>Suppress option_if_let_else clippy pedantic lint in test<commit_after>#![deny(clippy::all, clippy::pedantic)]
#![allow(clippy::option_if_let_else)]

use std::fmt::Display;
use thiserror::Error;

// Some of the elaborate cases from the rcc codebase, which is a C compiler in
// Rust. https://github.com/jyn514/rcc/blob/0.8.0/src/data/error.rs
#[derive(Error, Debug)]
pub enum CompilerError {
    #[error("cannot shift {} by {maximum} or more bits (got {current})", if *.is_left { "left" } else { "right" })]
    TooManyShiftBits {
        is_left: bool,
        maximum: u64,
        current: u64,
    },

    #[error("#error {}", (.0).iter().copied().collect::<Vec<_>>().join(" "))]
    User(Vec<&'static str>),

    #[error("overflow while parsing {}integer literal",
        if let Some(signed) = .is_signed {
            if *signed { "signed "} else { "unsigned "}
        } else {
            ""
        }
    )]
    IntegerOverflow { is_signed: Option<bool> },

    #[error("overflow while parsing {}integer literal", match .is_signed {
        Some(true) => "signed ",
        Some(false) => "unsigned ",
        None => "",
    })]
    IntegerOverflow2 { is_signed: Option<bool> },
}

// Examples drawn from Rustup.
#[derive(Error, Debug)]
pub enum RustupError {
    #[error(
        "toolchain '{name}' does not contain component {component}{}",
        .suggestion
            .as_ref()
            .map_or_else(String::new, |s| format!("; did you mean '{}'?", s)),
    )]
    UnknownComponent {
        name: String,
        component: String,
        suggestion: Option<String>,
    },
}

fn assert<T: Display>(expected: &str, value: T) {
    assert_eq!(expected, value.to_string());
}

#[test]
fn test_rcc() {
    assert(
        "cannot shift left by 32 or more bits (got 50)",
        CompilerError::TooManyShiftBits {
            is_left: true,
            maximum: 32,
            current: 50,
        },
    );

    assert("#error A B C", CompilerError::User(vec!["A", "B", "C"]));

    assert(
        "overflow while parsing signed integer literal",
        CompilerError::IntegerOverflow {
            is_signed: Some(true),
        },
    );
}

#[test]
fn test_rustup() {
    assert(
        "toolchain 'nightly' does not contain component clipy; did you mean 'clippy'?",
        RustupError::UnknownComponent {
            name: "nightly".to_owned(),
            component: "clipy".to_owned(),
            suggestion: Some("clippy".to_owned()),
        },
    );
}
<|endoftext|>