<commit_before>/// Compute an estimate of the population mean from a finite sample.
#[inline]
pub fn mean(data: &[f64]) -> f64 {
    data.iter().fold(0.0, |sum, &x| sum + x) / data.len() as f64
}

/// Compute an estimate of the population variance from a finite sample.
///
/// The estimate is unbiased. The computation is based on the
/// compensated-summation version of the [two-pass algorithm][1].
///
/// [1]: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass_algorithm
pub fn variance(data: &[f64]) -> f64 {
    let mu = mean(data);
    let n = data.len() as f64;
    let (mut sum1, mut sum2) = (0.0, 0.0);
    for &x in data {
        let delta = x - mu;
        sum1 = sum1 + delta * delta;
        sum2 = sum2 + delta;
    }
    (sum1 - sum2 * sum2 / n) / (n - 1.0)
}

#[cfg(test)]
mod tests {
    use assert;

    #[test]
    fn mean() {
        let data = [
             5.3766713954610001e-01,  1.8338850145950865e+00,
            -2.2588468610036481e+00,  8.6217332036812055e-01,
             3.1876523985898081e-01, -1.3076882963052734e+00,
            -4.3359202230568356e-01,  3.4262446653864992e-01,
             3.5783969397257605e+00,  2.7694370298848772e+00,
        ];
        assert::close(&[super::mean(&data)], &[6.2428219709029698e-01], 1e-15);
    }

    #[test]
    fn variance() {
        let data = [
             5.3766713954610001e-01,  1.8338850145950865e+00,
            -2.2588468610036481e+00,  8.6217332036812055e-01,
             3.1876523985898081e-01, -1.3076882963052734e+00,
            -4.3359202230568356e-01,  3.4262446653864992e-01,
             3.5783969397257605e+00,  2.7694370298848772e+00,
        ];
        assert::close(&[super::variance(&data)], &[3.1324921339484746e+00], 1e-15);
    }
}
<commit_msg>Account for the enge cases of mean and variance<commit_after>/// Compute an estimate of the population mean from a finite sample.
#[inline]
pub fn mean(data: &[f64]) -> f64 {
    data.iter().fold(0.0, |sum, &x| sum + x) / data.len() as f64
}

/// Compute an estimate of the population variance from a finite sample.
///
/// The estimate is unbiased. The computation is based on the
/// compensated-summation version of the [two-pass algorithm][1].
///
/// [1]: https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Two-pass_algorithm
pub fn variance(data: &[f64]) -> f64 {
    match data.len() {
        1 => 0.0,
        n => {
            let mu = data.iter().fold(0.0, |sum, &x| sum + x) / n as f64;
            let (mut sum1, mut sum2) = (0.0, 0.0);
            for &x in data {
                let delta = x - mu;
                sum1 = sum1 + delta * delta;
                sum2 = sum2 + delta;
            }
            (sum1 - sum2 * sum2 / n as f64) / (n as f64 - 1.0)
        },
    }
}

#[cfg(test)]
mod tests {
    use assert;

    #[test]
    fn mean() {
        let data = [
             5.3766713954610001e-01,  1.8338850145950865e+00,
            -2.2588468610036481e+00,  8.6217332036812055e-01,
             3.1876523985898081e-01, -1.3076882963052734e+00,
            -4.3359202230568356e-01,  3.4262446653864992e-01,
             3.5783969397257605e+00,  2.7694370298848772e+00,
        ];
        assert::close(&[super::mean(&data)], &[6.2428219709029698e-01], 1e-15);
    }

    #[test]
    fn mean_0() {
        assert!(super::mean(&[]).is_nan());
    }

    #[test]
    fn variance() {
        let data = [
             5.3766713954610001e-01,  1.8338850145950865e+00,
            -2.2588468610036481e+00,  8.6217332036812055e-01,
             3.1876523985898081e-01, -1.3076882963052734e+00,
            -4.3359202230568356e-01,  3.4262446653864992e-01,
             3.5783969397257605e+00,  2.7694370298848772e+00,
        ];
        assert::close(&[super::variance(&data)], &[3.1324921339484746e+00], 1e-15);
    }

    #[test]
    fn variance_0() {
        assert!(super::variance(&[]).is_nan());
    }

    #[test]
    fn variance_1() {
        assert_eq!(super::variance(&[1.0]), 0.0);
    }
}
<|endoftext|>


<commit_before>/// a very small mod just for ui data send by net. It is important to
/// not send all discovery blindly (e.g. duplicates)
use libp2p_core::{Multiaddr, PeerId};
use std::{collections::HashSet, sync::mpsc::Sender};

use super::super::{
    ctrl::{self, ForwardNetMessage, UiPeer, UiUpdateMsg},
    net::peer_representation,
    net::sm::*,
};

pub struct UiData {
    sender: Option<Sender<UiUpdateMsg>>,
    ui_shown_peers: HashSet<PeerId>,
}
impl UiData {
    pub fn new(sender: Option<Sender<UiUpdateMsg>>) -> Self {
        Self {
            sender,
            ui_shown_peers: HashSet::new(),
        }
    }

    pub fn has_peer(&mut self, peer_id: &PeerId) -> bool {
        let ref collection = self.ui_shown_peers;
        collection.get(peer_id).is_some()
    }

    pub fn register_address(&mut self, peer_id: &PeerId, multi_addresses: &Multiaddr) {
        let ref mut collection = self.ui_shown_peers;
        if collection.get(peer_id).is_none() {
            // add
            collection.insert(peer_id.clone());
            trace!(
                "found new peer {}",
                peer_representation::peer_to_hash_string(peer_id)
            );
            // and send
            if let Some(ctrl_sender) = &self.sender {
                let addr_as_string = multi_addresses.iter().map(|x| x.to_string()).collect();

                ctrl_sender
                    .send(ctrl::UiUpdateMsg::NetUpdate(ForwardNetMessage::Add(
                        UiPeer {
                            id: peer_id.clone(),
                            addresses: addr_as_string,
                        },
                    )))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        } else {
            error!("Hey, the StateMachine should have made this impossible!!!");
        }
    }
    pub fn unregister_address(&mut self, peer_id: &PeerId) {
        let ref mut collection = self.ui_shown_peers;
        if collection.remove(peer_id) {
            trace!(
                "removed peer {}",
                peer_representation::peer_to_hash_string(peer_id)
            );
            if let Some(ctrl_sender) = &self.sender {
                ctrl_sender
                    .send(ctrl::UiUpdateMsg::NetUpdate(ForwardNetMessage::Delete(
                        peer_id.clone(),
                    )))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        } else {
            warn!("Trying to remove something which wasn't there ...");
        }
    }

    pub fn update_peer_data(&mut self, update_data: &UpdateData) {
        // todo: of course not only u32 but more data and not only finish message
        if self.has_peer(&update_data.id) {
            if let Some(ctrl_sender) = &self.sender {
                ctrl_sender
                    .send(ctrl::UiUpdateMsg::PeerSearchFinished(
                        update_data.id.clone(),
                        update_data.count,
                    ))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        } else {
            warn!(
                "Peer {} is not known!",
                peer_representation::peer_to_hash_string(&update_data.id)
            );
        }
    }
}
<commit_msg>[clean-up] remove unneccessary todo<commit_after>/// a very small mod just for ui data send by net. It is important to
/// not send all discovery blindly (e.g. duplicates)
use libp2p_core::{Multiaddr, PeerId};
use std::{collections::HashSet, sync::mpsc::Sender};

use super::super::{
    ctrl::{self, ForwardNetMessage, UiPeer, UiUpdateMsg},
    net::peer_representation,
    net::sm::*,
};

pub struct UiData {
    sender: Option<Sender<UiUpdateMsg>>,
    ui_shown_peers: HashSet<PeerId>,
}
impl UiData {
    pub fn new(sender: Option<Sender<UiUpdateMsg>>) -> Self {
        Self {
            sender,
            ui_shown_peers: HashSet::new(),
        }
    }

    pub fn has_peer(&mut self, peer_id: &PeerId) -> bool {
        let ref collection = self.ui_shown_peers;
        collection.get(peer_id).is_some()
    }

    pub fn register_address(&mut self, peer_id: &PeerId, multi_addresses: &Multiaddr) {
        let ref mut collection = self.ui_shown_peers;
        if collection.get(peer_id).is_none() {
            // add
            collection.insert(peer_id.clone());
            trace!(
                "found new peer {}",
                peer_representation::peer_to_hash_string(peer_id)
            );
            // and send
            if let Some(ctrl_sender) = &self.sender {
                let addr_as_string = multi_addresses.iter().map(|x| x.to_string()).collect();

                ctrl_sender
                    .send(ctrl::UiUpdateMsg::NetUpdate(ForwardNetMessage::Add(
                        UiPeer {
                            id: peer_id.clone(),
                            addresses: addr_as_string,
                        },
                    )))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        } else {
            error!("Hey, the StateMachine should have made this impossible!!!");
        }
    }
    pub fn unregister_address(&mut self, peer_id: &PeerId) {
        let ref mut collection = self.ui_shown_peers;
        if collection.remove(peer_id) {
            trace!(
                "removed peer {}",
                peer_representation::peer_to_hash_string(peer_id)
            );
            if let Some(ctrl_sender) = &self.sender {
                ctrl_sender
                    .send(ctrl::UiUpdateMsg::NetUpdate(ForwardNetMessage::Delete(
                        peer_id.clone(),
                    )))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        } else {
            warn!("Trying to remove something which wasn't there ...");
        }
    }

    pub fn update_peer_data(&mut self, update_data: &UpdateData) {
        if self.has_peer(&update_data.id) {
            if let Some(ctrl_sender) = &self.sender {
                ctrl_sender
                    .send(ctrl::UiUpdateMsg::PeerSearchFinished(
                        update_data.id.clone(),
                        update_data.count,
                    ))
                    .unwrap_or_else(|e| error!("use one: {}", e));
            }
        } else {
            warn!(
                "Peer {} is not known!",
                peer_representation::peer_to_hash_string(&update_data.id)
            );
        }
    }
}
<|endoftext|>


<commit_before>use std::mem;

pub use self::Token::*;

use self::TokenClass::*;
use self::ParserState::*;

/// `Token` is a section of a compiled mustache string.
#[derive(Clone, Debug)]
pub enum Token {
    Text(String),
    ETag(Vec<String>, String),
    UTag(Vec<String>, String),
    Section(Vec<String>, bool, Vec<Token>, String, String, String, String, String),
    IncompleteSection(Vec<String>, bool, String, bool),
    Partial(String, String, String),
}

enum TokenClass {
    Normal,
    StandAlone,
    WhiteSpace(String, usize),
    NewLineWhiteSpace(String, usize),
}

/// `Parser` parses a string into a series of `Token`s.
pub struct Parser<'a, T: 'a> {
    reader: &'a mut T,
    ch: Option<char>,
    lookahead: Option<char>,
    line: usize,
    col: usize,
    content: String,
    state: ParserState,
    otag: String,
    ctag: String,
    otag_chars: Vec<char>,
    ctag_chars: Vec<char>,
    tag_position: usize,
    tokens: Vec<Token>,
    partials: Vec<String>,
}

enum ParserState { TEXT, OTAG, TAG, CTAG }

impl<'a, T: Iterator<Item=char>> Parser<'a, T> {
    pub fn new(reader: &'a mut T, otag: &str, ctag: &str) -> Parser<'a, T> {
        let mut parser = Parser {
            reader: reader,
            ch: None,
            lookahead: None,
            line: 1,
            col: 1,
            content: String::new(),
            state: TEXT,
            otag: otag.to_string(),
            ctag: ctag.to_string(),
            otag_chars: otag.chars().collect(),
            ctag_chars: ctag.chars().collect(),
            tag_position: 0,
            tokens: Vec::new(),
            partials: Vec::new(),
        };

        parser.bump();
        parser
    }

    fn bump(&mut self) {
        match self.lookahead.take() {
            None => { self.ch = self.reader.next(); }
            Some(ch) => { self.ch = Some(ch); }
        }

        if let Some(ch) = self.ch {
            if ch == '\n' {
                self.line += 1;
                self.col = 1;
            } else {
                self.col += 1;
            }
        }
    }

    fn peek(&mut self) -> Option<char> {
        match self.lookahead {
            None => {
                self.lookahead = self.reader.next();
                self.lookahead
            }
            Some(ch) => Some(ch),
        }
    }

    fn ch_is(&self, ch: char) -> bool {
        match self.ch {
            Some(c) => c == ch,
            None => false,
        }
    }

    /// Parse the template into tokens and a list of partial files.
    pub fn parse(mut self) -> (Vec<Token>, Vec<String>) {
        let mut curly_brace_tag = false;

        while let Some(ch) = self.ch {
            match self.state {
                TEXT => {
                    if ch == self.otag_chars[0] {
                        if self.otag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = OTAG;
                        } else {
                            self.add_text();
                            self.state = TAG;
                        }
                    } else {
                        self.content.push(ch);
                    }
                    self.bump();
                }
                OTAG => {
                    if ch == self.otag_chars[self.tag_position] {
                        if self.tag_position == self.otag_chars.len() - 1 {
                            self.add_text();
                            curly_brace_tag = false;
                            self.state = TAG;
                        } else {
                            self.tag_position = self.tag_position + 1;
                        }
                    } else {
                        // We don't have a tag, so add all the tag parts we've seen
                        // so far to the string.
                        self.state = TEXT;
                        self.not_otag();
                        self.content.push(ch);
                    }
                    self.bump();
                }
                TAG => {
                    if self.content.is_empty() && ch == '{' {
                        curly_brace_tag = true;
                        self.content.push(ch);
                        self.bump();
                    } else if curly_brace_tag && ch == '}' {
                        curly_brace_tag = false;
                        self.content.push(ch);
                        self.bump();
                    } else if ch == self.ctag_chars[0] {
                        if self.ctag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = CTAG;
                            self.bump();
                        } else {
                            self.add_tag();
                            self.state = TEXT;
                        }
                    } else {
                        self.content.push(ch);
                        self.bump();
                    }
                }
                CTAG => {
                    if ch == self.ctag_chars[self.tag_position] {
                        if self.tag_position == self.ctag_chars.len() - 1 {
                            self.add_tag();
                            self.state = TEXT;
                        } else {
                            self.state = TAG;
                            self.not_ctag();
                            self.content.push(ch);
                            self.bump();
                        }
                    } else {
                        panic!("character {} is not part of CTAG: {}",
                              ch,
                              self.ctag_chars[self.tag_position]);
                    }
                }
            }
        }

        match self.state {
            TEXT => { self.add_text(); }
            OTAG => { self.not_otag(); self.add_text(); }
            TAG => { panic!("unclosed tag"); }
            CTAG => { self.not_ctag(); self.add_text(); }
        }

        // Check that we don't have any incomplete sections.
        for token in self.tokens.iter() {
            if let IncompleteSection(ref path, _, _, _) = *token {
                panic!("Unclosed mustache section {}", path.join("."));
            }
        }

        let Parser { tokens, partials, .. } = self;

        (tokens, partials)
    }

    fn add_text(&mut self) {
        if !self.content.is_empty() {
            let mut content = String::new();
            mem::swap(&mut content, &mut self.content);

            self.tokens.push(Text(content.to_string()));
        }
    }

    // This function classifies whether or not a token is standalone, or if it
    // has trailing whitespace. It's looking for this pattern:
    //
    //   ("\n" | "\r\n") whitespace* token ("\n" | "\r\n")
    //
    fn classify_token(&mut self) -> TokenClass {
        // Exit early if the next character is not '\n' or '\r\n'.
        if let Some(ch) = self.ch {
            if !(ch == '\n' || (ch == '\r' && self.peek() == Some('\n'))) {
                return Normal;
            }
        }

        match self.tokens.last() {
            // If the last token ends with a newline (or there is no previous
            // token), then this token is standalone.
            None => { StandAlone }

            Some(&IncompleteSection(_, _, _, true)) => { StandAlone }

            Some(&Text(ref s)) if !s.is_empty() => {
                // Look for the last newline character that may have whitespace
                // following it.
                match s.rfind(| c:char| c == '\n' || !c.is_whitespace()) {
                    // It's all whitespace.
                    None => {
                        if self.tokens.len() == 1 {
                            WhiteSpace(s.to_string(), 0)
                        } else {
                            Normal
                        }
                    }
                    Some(pos) => {
                        if s.as_bytes()[pos] == b'\n' {
                            if pos == s.len() - 1 {
                                StandAlone
                            } else {
                                WhiteSpace(s.to_string(), pos + 1)
                            }
                        } else { Normal }
                    }
                }
            }
            Some(_) => Normal,
        }
    }

    fn eat_whitespace(&mut self) -> bool {
        // If the next character is a newline, and the last token ends with a
        // newline and whitespace, clear out the whitespace.

        match self.classify_token() {
            Normal => { false }
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                true
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                true
            }
        }
    }

    fn add_tag(&mut self) {
        self.bump();

        let tag = self.otag.clone() + &self.content + &self.ctag;

        // Move the content to avoid a copy.
        let mut content = String::new();
        mem::swap(&mut content, &mut self.content);
        let len = content.len();
        let content = content;

        match content.as_bytes()[0] as char {
            '!' => {
                // ignore comments
                self.eat_whitespace();
            }
            '&' => {
                let name = &content[1..len];
                let name = self.check_content(name);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(UTag(name, tag));
            }
            '{' => {
                if content.ends_with('}') {
                    let name = &content[1..len - 1];
                    let name = self.check_content(name);
                    let name = name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect();
                    self.tokens.push(UTag(name, tag));
                } else { panic!("unbalanced \"{\" in tag"); }
            }
            '#' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, false, tag, newlined));
            }
            '^' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, true, tag, newlined));
            }
            '/' => {
                self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                let mut children: Vec<Token> = Vec::new();

                loop {
                    if self.tokens.is_empty() {
                        panic!("closing unopened section");
                    }

                    let last = self.tokens.pop();

                    match last {
                        Some(IncompleteSection(section_name, inverted, osection, _)) => {
                            children.reverse();

                            // Collect all the children's sources.
                            let mut srcs = Vec::new();
                            for child in children.iter() {
                                match *child {
                                    Text(ref s)
                                    | ETag(_, ref s)
                                    | UTag(_, ref s)
                                    | Partial(_, _, ref s) => {
                                        srcs.push(s.clone())
                                    }
                                    Section(_, _, _, _, ref osection, ref src, ref csection, _) => {
                                        srcs.push(osection.clone());
                                        srcs.push(src.clone());
                                        srcs.push(csection.clone());
                                    }
                                    _ => panic!(),
                                }
                            }

                            if section_name == name {
                                // Cache the combination of all the sources in the
                                // section. It's unfortunate, but we need to do this in
                                // case the user uses a function to instantiate the
                                // tag.
                                let mut src = String::new();
                                for s in srcs.iter() { src.push_str(s); }

                                self.tokens.push(
                                    Section(
                                        name,
                                        inverted,
                                        children,
                                        self.otag.to_string(),
                                        osection,
                                        src.to_string(),
                                        tag,
                                        self.ctag.to_string()));
                                break;
                            } else {
                                panic!("Unclosed section");
                            }
                        }
                        Some(last_token) => children.push(last_token),
                        None => (),
                    }
                }
            }
            '>' => { self.add_partial(&content, tag); }
            '=' => {
                self.eat_whitespace();

                if len > 2usize && content.ends_with('=') {
                    let s = self.check_content(&content[1..len - 1]);

                    fn is_whitespace(c: char) -> bool { c.is_whitespace() }
                    let pos = s.find(is_whitespace);
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.otag = s[0..pos].to_string();
                    self.otag_chars = self.otag.chars().collect();

                    let s2 = &s[pos..];
                    let pos = s2.find(| c : char| !c.is_whitespace());
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.ctag = s2[pos..].to_string();
                    self.ctag_chars = self.ctag.chars().collect();
                } else {
                    panic!("invalid change delimiter tag content");
                }
            }
            _ => {
                // If the name is "." then we want the top element, which we represent with
                // an empty name.
                let name = self.check_content(&content);
                let name = if name == "." {
                    Vec::new()
                } else {
                    name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect()
                };

                self.tokens.push(ETag(name, tag));
            }
        }
    }

    fn add_partial(&mut self, content: &str, tag: String) {
        let indent = match self.classify_token() {
            Normal => "".to_string(),
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                "".to_string()
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                let ws = &s[pos..];

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                ws.to_string()
            }
        };

        // We can't inline the tokens directly as we may have a recursive
        // partial. So instead, we'll cache the partials we used and look them
        // up later.
        let name = &content[1..content.len()];
        let name = self.check_content(name);

        self.tokens.push(Partial(name.to_string(), indent, tag));
        self.partials.push(name);
    }

    fn not_otag(&mut self) {
        for (i, ch) in self.otag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn not_ctag(&mut self) {
        for (i, ch) in self.ctag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn check_content(&self, content: &str) -> String {
        let trimmed = content.trim();
        if trimmed.is_empty() {
            panic!("empty tag");
        }
        trimmed.to_string()
    }
}
<commit_msg>chore(clippy): don’t use String::to_string<commit_after>use std::mem;

pub use self::Token::*;

use self::TokenClass::*;
use self::ParserState::*;

/// `Token` is a section of a compiled mustache string.
#[derive(Clone, Debug)]
pub enum Token {
    Text(String),
    ETag(Vec<String>, String),
    UTag(Vec<String>, String),
    Section(Vec<String>, bool, Vec<Token>, String, String, String, String, String),
    IncompleteSection(Vec<String>, bool, String, bool),
    Partial(String, String, String),
}

enum TokenClass {
    Normal,
    StandAlone,
    WhiteSpace(String, usize),
    NewLineWhiteSpace(String, usize),
}

/// `Parser` parses a string into a series of `Token`s.
pub struct Parser<'a, T: 'a> {
    reader: &'a mut T,
    ch: Option<char>,
    lookahead: Option<char>,
    line: usize,
    col: usize,
    content: String,
    state: ParserState,
    otag: String,
    ctag: String,
    otag_chars: Vec<char>,
    ctag_chars: Vec<char>,
    tag_position: usize,
    tokens: Vec<Token>,
    partials: Vec<String>,
}

enum ParserState { TEXT, OTAG, TAG, CTAG }

impl<'a, T: Iterator<Item=char>> Parser<'a, T> {
    pub fn new(reader: &'a mut T, otag: &str, ctag: &str) -> Parser<'a, T> {
        let mut parser = Parser {
            reader: reader,
            ch: None,
            lookahead: None,
            line: 1,
            col: 1,
            content: String::new(),
            state: TEXT,
            otag: otag.to_string(),
            ctag: ctag.to_string(),
            otag_chars: otag.chars().collect(),
            ctag_chars: ctag.chars().collect(),
            tag_position: 0,
            tokens: Vec::new(),
            partials: Vec::new(),
        };

        parser.bump();
        parser
    }

    fn bump(&mut self) {
        match self.lookahead.take() {
            None => { self.ch = self.reader.next(); }
            Some(ch) => { self.ch = Some(ch); }
        }

        if let Some(ch) = self.ch {
            if ch == '\n' {
                self.line += 1;
                self.col = 1;
            } else {
                self.col += 1;
            }
        }
    }

    fn peek(&mut self) -> Option<char> {
        match self.lookahead {
            None => {
                self.lookahead = self.reader.next();
                self.lookahead
            }
            Some(ch) => Some(ch),
        }
    }

    fn ch_is(&self, ch: char) -> bool {
        match self.ch {
            Some(c) => c == ch,
            None => false,
        }
    }

    /// Parse the template into tokens and a list of partial files.
    pub fn parse(mut self) -> (Vec<Token>, Vec<String>) {
        let mut curly_brace_tag = false;

        while let Some(ch) = self.ch {
            match self.state {
                TEXT => {
                    if ch == self.otag_chars[0] {
                        if self.otag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = OTAG;
                        } else {
                            self.add_text();
                            self.state = TAG;
                        }
                    } else {
                        self.content.push(ch);
                    }
                    self.bump();
                }
                OTAG => {
                    if ch == self.otag_chars[self.tag_position] {
                        if self.tag_position == self.otag_chars.len() - 1 {
                            self.add_text();
                            curly_brace_tag = false;
                            self.state = TAG;
                        } else {
                            self.tag_position = self.tag_position + 1;
                        }
                    } else {
                        // We don't have a tag, so add all the tag parts we've seen
                        // so far to the string.
                        self.state = TEXT;
                        self.not_otag();
                        self.content.push(ch);
                    }
                    self.bump();
                }
                TAG => {
                    if self.content.is_empty() && ch == '{' {
                        curly_brace_tag = true;
                        self.content.push(ch);
                        self.bump();
                    } else if curly_brace_tag && ch == '}' {
                        curly_brace_tag = false;
                        self.content.push(ch);
                        self.bump();
                    } else if ch == self.ctag_chars[0] {
                        if self.ctag_chars.len() > 1 {
                            self.tag_position = 1;
                            self.state = CTAG;
                            self.bump();
                        } else {
                            self.add_tag();
                            self.state = TEXT;
                        }
                    } else {
                        self.content.push(ch);
                        self.bump();
                    }
                }
                CTAG => {
                    if ch == self.ctag_chars[self.tag_position] {
                        if self.tag_position == self.ctag_chars.len() - 1 {
                            self.add_tag();
                            self.state = TEXT;
                        } else {
                            self.state = TAG;
                            self.not_ctag();
                            self.content.push(ch);
                            self.bump();
                        }
                    } else {
                        panic!("character {} is not part of CTAG: {}",
                              ch,
                              self.ctag_chars[self.tag_position]);
                    }
                }
            }
        }

        match self.state {
            TEXT => { self.add_text(); }
            OTAG => { self.not_otag(); self.add_text(); }
            TAG => { panic!("unclosed tag"); }
            CTAG => { self.not_ctag(); self.add_text(); }
        }

        // Check that we don't have any incomplete sections.
        for token in self.tokens.iter() {
            if let IncompleteSection(ref path, _, _, _) = *token {
                panic!("Unclosed mustache section {}", path.join("."));
            }
        }

        let Parser { tokens, partials, .. } = self;

        (tokens, partials)
    }

    fn add_text(&mut self) {
        if !self.content.is_empty() {
            let mut content = String::new();
            mem::swap(&mut content, &mut self.content);

            self.tokens.push(Text(content));
        }
    }

    // This function classifies whether or not a token is standalone, or if it
    // has trailing whitespace. It's looking for this pattern:
    //
    //   ("\n" | "\r\n") whitespace* token ("\n" | "\r\n")
    //
    fn classify_token(&mut self) -> TokenClass {
        // Exit early if the next character is not '\n' or '\r\n'.
        if let Some(ch) = self.ch {
            if !(ch == '\n' || (ch == '\r' && self.peek() == Some('\n'))) {
                return Normal;
            }
        }

        match self.tokens.last() {
            // If the last token ends with a newline (or there is no previous
            // token), then this token is standalone.
            None => { StandAlone }

            Some(&IncompleteSection(_, _, _, true)) => { StandAlone }

            Some(&Text(ref s)) if !s.is_empty() => {
                // Look for the last newline character that may have whitespace
                // following it.
                match s.rfind(| c:char| c == '\n' || !c.is_whitespace()) {
                    // It's all whitespace.
                    None => {
                        if self.tokens.len() == 1 {
                            WhiteSpace(s.clone(), 0)
                        } else {
                            Normal
                        }
                    }
                    Some(pos) => {
                        if s.as_bytes()[pos] == b'\n' {
                            if pos == s.len() - 1 {
                                StandAlone
                            } else {
                                WhiteSpace(s.clone(), pos + 1)
                            }
                        } else { Normal }
                    }
                }
            }
            Some(_) => Normal,
        }
    }

    fn eat_whitespace(&mut self) -> bool {
        // If the next character is a newline, and the last token ends with a
        // newline and whitespace, clear out the whitespace.

        match self.classify_token() {
            Normal => { false }
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                true
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                true
            }
        }
    }

    fn add_tag(&mut self) {
        self.bump();

        let tag = self.otag.clone() + &self.content + &self.ctag;

        // Move the content to avoid a copy.
        let mut content = String::new();
        mem::swap(&mut content, &mut self.content);
        let len = content.len();
        let content = content;

        match content.as_bytes()[0] as char {
            '!' => {
                // ignore comments
                self.eat_whitespace();
            }
            '&' => {
                let name = &content[1..len];
                let name = self.check_content(name);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(UTag(name, tag));
            }
            '{' => {
                if content.ends_with('}') {
                    let name = &content[1..len - 1];
                    let name = self.check_content(name);
                    let name = name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect();
                    self.tokens.push(UTag(name, tag));
                } else { panic!("unbalanced \"{\" in tag"); }
            }
            '#' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, false, tag, newlined));
            }
            '^' => {
                let newlined = self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                self.tokens.push(IncompleteSection(name, true, tag, newlined));
            }
            '/' => {
                self.eat_whitespace();

                let name = self.check_content(&content[1..len]);
                let name = name.split_terminator('.')
                    .map(|x| x.to_string())
                    .collect();
                let mut children: Vec<Token> = Vec::new();

                loop {
                    if self.tokens.is_empty() {
                        panic!("closing unopened section");
                    }

                    let last = self.tokens.pop();

                    match last {
                        Some(IncompleteSection(section_name, inverted, osection, _)) => {
                            children.reverse();

                            // Collect all the children's sources.
                            let mut srcs = Vec::new();
                            for child in children.iter() {
                                match *child {
                                    Text(ref s)
                                    | ETag(_, ref s)
                                    | UTag(_, ref s)
                                    | Partial(_, _, ref s) => {
                                        srcs.push(s.clone())
                                    }
                                    Section(_, _, _, _, ref osection, ref src, ref csection, _) => {
                                        srcs.push(osection.clone());
                                        srcs.push(src.clone());
                                        srcs.push(csection.clone());
                                    }
                                    _ => panic!(),
                                }
                            }

                            if section_name == name {
                                // Cache the combination of all the sources in the
                                // section. It's unfortunate, but we need to do this in
                                // case the user uses a function to instantiate the
                                // tag.
                                let mut src = String::new();
                                for s in srcs.iter() { src.push_str(s); }

                                self.tokens.push(
                                    Section(
                                        name,
                                        inverted,
                                        children,
                                        self.otag.clone(),
                                        osection,
                                        src,
                                        tag,
                                        self.ctag.clone()));
                                break;
                            } else {
                                panic!("Unclosed section");
                            }
                        }
                        Some(last_token) => children.push(last_token),
                        None => (),
                    }
                }
            }
            '>' => { self.add_partial(&content, tag); }
            '=' => {
                self.eat_whitespace();

                if len > 2usize && content.ends_with('=') {
                    let s = self.check_content(&content[1..len - 1]);

                    fn is_whitespace(c: char) -> bool { c.is_whitespace() }
                    let pos = s.find(is_whitespace);
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.otag = s[0..pos].to_string();
                    self.otag_chars = self.otag.chars().collect();

                    let s2 = &s[pos..];
                    let pos = s2.find(| c : char| !c.is_whitespace());
                    let pos = match pos {
                      None => { panic!("invalid change delimiter tag content"); }
                      Some(pos) => { pos }
                    };

                    self.ctag = s2[pos..].to_string();
                    self.ctag_chars = self.ctag.chars().collect();
                } else {
                    panic!("invalid change delimiter tag content");
                }
            }
            _ => {
                // If the name is "." then we want the top element, which we represent with
                // an empty name.
                let name = self.check_content(&content);
                let name = if name == "." {
                    Vec::new()
                } else {
                    name.split_terminator('.')
                        .map(|x| x.to_string())
                        .collect()
                };

                self.tokens.push(ETag(name, tag));
            }
        }
    }

    fn add_partial(&mut self, content: &str, tag: String) {
        let indent = match self.classify_token() {
            Normal => "".to_string(),
            StandAlone => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();
                "".to_string()
            }
            WhiteSpace(s, pos) | NewLineWhiteSpace(s, pos) => {
                if self.ch_is('\r') { self.bump(); }
                self.bump();

                let ws = &s[pos..];

                // Trim the whitespace from the last token.
                self.tokens.pop();
                self.tokens.push(Text(s[0..pos].to_string()));

                ws.to_string()
            }
        };

        // We can't inline the tokens directly as we may have a recursive
        // partial. So instead, we'll cache the partials we used and look them
        // up later.
        let name = &content[1..content.len()];
        let name = self.check_content(name);

        self.tokens.push(Partial(name.clone(), indent, tag));
        self.partials.push(name);
    }

    fn not_otag(&mut self) {
        for (i, ch) in self.otag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn not_ctag(&mut self) {
        for (i, ch) in self.ctag_chars.iter().enumerate() {
            if !(i < self.tag_position) {
                break
            }
            self.content.push(*ch);
        }
    }

    fn check_content(&self, content: &str) -> String {
        let trimmed = content.trim();
        if trimmed.is_empty() {
            panic!("empty tag");
        }
        trimmed.to_string()
    }
}
<|endoftext|>


<commit_before>use nom::{self, digit, multispace, eof, ErrorKind, IResult};
use std::str;
use std::fmt;
use super::value::Value;
use super::scope::Scope;

static UTF8_ERROR: &'static str = "File is no valid UTF8!";

macro_rules! fix {
    ($i:expr, $it:ident) => (fix_error!($i, ParserError, $it));
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
      fix_error!($i, ParserError, complete!($submac!($($args)*)))
    );
}

named!(bool_<&[u8], Value, ParserError>, fix!(map!(
    alt!(
        tag!("true") |
        tag!("false")),
    |x|{ Value::new_bool(x == b"true") })));

named!(char_<&[u8], Value, ParserError>, fix!(chain!(
    tag!("#\\") ~
    c: take!(1) ,
    ||{ Value::new_char(c[0] as char) })));

named!(end_of_item<&[u8], &[u8], ParserError>,
    fix!(alt!(
        multispace |
        eof |
        tag!(")"))));

fn is_valid_in_ident(x: u8) -> bool {
    (x >= 0x3A && x <= 0x7E) ||
    (x >= 0x2A && x <= 0x2F) ||
    (x >= 0x23 && x <= 0x27) ||
    x == '!' as u8
}

named!(ident<&[u8], Value, ParserError>, fix!(chain!(
    peek!(none_of!("0123456789().")) ~
    x: take_while1!(is_valid_in_ident),
    || Value::new_symbol((str::from_utf8(x).unwrap())))));

named!(pub integer<&[u8], Value, ParserError>,
    fix!(chain!(
        s: opt!(char!('-')) ~
        x: digit ,
        ||{
            let num: i64 = str::from_utf8(x).expect(UTF8_ERROR).parse().unwrap();
            if s.is_some() {
                Value::new_integer(-num)
            } else {
                Value::new_integer(num)
            }
        })));

// eats everything until a " char is encountered
// recognizes escaped characters
fn eat_string_content(input: &[u8]) -> IResult<&[u8], String, ParserError> {
    macro_rules! itry {
        ($e:expr) => ({
            match $e {
                Ok(_) => (),
                Err(x) => return IResult::Error(x),
            }
        })
    }

    fn save_str<'a>(input: &'a[u8], start: &mut usize, end: &mut usize, content: &mut String) -> Result<(), nom::Err<&'a [u8], ParserError>> {
        if start >= end { return Ok(())}
        let utf8_str = match str::from_utf8(&input[*start..*end]) {
            Ok(s) => s,
            Err(x) => return Err(nom::Err::Position(ErrorKind::Custom(ParserError::NonUtf8String), &input[0..x.valid_up_to()])),
        };
        content.push_str(utf8_str);
        *start = *end;
        return Ok(())
    }

    if input.len() == 0 {
        return IResult::Done(&input, String::new());
    }

    let mut end: usize = 0;
    let mut start: usize = 0;
    let mut content = String::new();
    while end < input.len() {
        if input[end] == '"' as u8 {
            break
        }
        if input[end] == '\\' as u8 {
            if (&input[end..]).len() <= 1 {
                break;
            }

            if input[end+1..].starts_with("n".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\n');
                start += 2;
                end += 2;
            } else if input[end+1..].starts_with("t".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\t');
                start += 2;
                end += 2;
            } else if input[end+1..].starts_with("\"".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\"');
                start += 2;
                end += 2;
            } else if input[end+1..].starts_with("\\".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\\');
                start += 2;
                end += 2;
            } else {
                end += 1;
            }
        } else {
            end += 1;
        }
    }

    itry!(save_str(input, &mut start, &mut end, &mut content));
    IResult::Done(&input[end..], content)
}

named!(string<&[u8], Value, ParserError>,
    chain!(
        fix!(tag!("\"")) ~
        x: eat_string_content ~
        fix!(tag!("\"")) ,
        || { Value::new_string(x) }
    ));

named!(item<&[u8], Value, ParserError>,
    complete!(chain!(
        opt!(multispace) ~
        quoted: opt!(tag!("'")) ~
        value: alt_complete!(
            bool_ |
            char_ |
            integer |
            string |
            ident |
            pair |
            list) ~
        peek!(end_of_item),
        || if quoted.is_some() {
            Value::new_pair(Value::new_symbol("quote"), Value::new_pair(value, Value::empty_list()))
        } else { value })));

named!(pair<&[u8], Value, ParserError>,
    error!(ErrorKind::Custom(ParserError::InvalidPair), complete!(delimited!(
            fix!(tag!("(")),
            chain!(
                a: item ~
                fix!(multispace) ~
                fix!(tag!(".")) ~
                fix!(multispace) ~
                b: item ,
                || { Value::new_pair(a,b) }
            ),
            fix!(tag!(")"))))));

named!(list_inner<Vec<Value> >,
    many0!(item));

named!(list<&[u8], Value, ParserError>,
    alt!(
        fix!(map!(
            tag!("()") ,
            |_| Value::empty_list())) |
        fix!(map!(
            delimited!(
                tag!("("),
                list_inner,
                tag!(")")),
            |x: Vec<Value>| {
                Value::new_list(&x)
            }))
        ));

pub fn parse(input: &[u8]) -> Result<Value, String> {
    use nom::Err as NomErr;
    match item(input) {
        IResult::Done(remaining, value) => {
            if remaining.len() > 0 {
                Err(format!("error, expected EOF, found: {}", str::from_utf8(remaining).unwrap()))
            } else {
                Ok(value)
            }
        },
        IResult::Incomplete(_) => unimplemented!(), // because of complete
        IResult::Error(NomErr::Position(err, _)) => Err(create_error_string(err)),
        IResult::Error(NomErr::NodePosition(err, _, _)) => Err(create_error_string(err)),
        _ => unimplemented!(),
    }
}

fn create_error_string(err: ErrorKind<ParserError>) -> String {
    let msg = match err {
        ErrorKind::Custom(x) => format!("{}", x),
        x => format!("parser error {}", x.description()),
    };

    format!("error: {}", msg)
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ParserError {
    MultipleChars, // more than 1 character between '', eg. 'ab'
    MissingChar,   // no char between '', eg. ''
    InvalidPair,   // eg. (1 2 . 3) or (1 . 2 3)
    InvalidItem,   // eg. 1 2
    NonUtf8String, // well..
}

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParserError::MultipleChars => write!(f, "more than one character between ''"),
            ParserError::MissingChar => write!(f, "no character between ''"),
            ParserError::InvalidPair => write!(f, "invalid pair"),
            ParserError::InvalidItem => write!(f, "invalid item"),
            ParserError::NonUtf8String => write!(f, "string is no valid UTF8"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::{ParserError, bool_, char_, integer, string, ident, pair, list, item, parse};
    use nom::{IResult, Err, ErrorKind};
    use super::super::value::Value;

    macro_rules! expect_error {
        // with error kind
        ($parser:ident, $input:expr, $pos:expr, $errorkind:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            let res = $parser(input);
            assert!(match res {
                IResult::Error(
                    Err::NodePosition(
                        ErrorKind::Custom(kind),
                        pos,
                        _)
                ) if (pos == error_pos && kind == $errorkind) => { true },
                x => { println!("{:?}", x); false }
            });
        );
        // without error kind
        ($parser:ident, $input:expr, $pos:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            assert!(match $parser(input) {
                IResult::Error(Err::Position(_, pos)) if pos == error_pos => true,
                x => { println!("{:?}", x); false },
            });
        )
    }

    macro_rules! expect_ok {
        // with rest
        ($parser:ident, $input:expr, $expected:expr, $rest:expr) => (
            let input = $input.as_bytes();
            let rest = $rest.as_bytes();
            assert_eq!(
                $parser(input),
                IResult::Done(rest, $expected)););

        // without rest
        ($parser:ident, $input:expr, $expected:expr) => (
            expect_ok!($parser, $input, $expected, "");
        )
    }

    #[test]
    fn bool() {
        expect_ok!(bool_, "true", Value::new_bool(true));
        expect_ok!(bool_, "false", Value::new_bool(false));
        expect_error!(bool_, "trude", 0);
        expect_error!(bool_, "fale", 0);
    }

    #[test]
    fn char() {
        use std::u8;
        for x in 0..127 {
            if x == '\'' as u8 { continue } // skip ''', which is invalid (tested below)
            let input = String::from_utf8(vec!['#' as u8, '\\' as u8, x]).unwrap();
            expect_ok!(char_, input, Value::new_char(x as char));
        }
        expect_error!(char_, "#\\", 0);
    }

    #[test]
    fn integer_() {
        expect_ok!(integer, "007", Value::new_integer(7));
        expect_ok!(integer, "-007", Value::new_integer(-7));
        expect_ok!(integer, "123456789", Value::new_integer(123456789));
        expect_ok!(integer, "-123456789", Value::new_integer(-123456789));
        expect_ok!(integer, "123b456789", Value::new_integer(123), "b456789");
        expect_ok!(integer, "123456789c", Value::new_integer(123456789), "c");
        expect_ok!(integer, "00-7", Value::new_integer(0), "-7");

        expect_error!(integer, "a123456789", 0);
        expect_error!(integer, "--7", 1);
    }

    #[test]
    fn ident_() {
        expect_ok!(ident, "+", Value::new_ident("+"));
        expect_ok!(ident, "a1a", Value::new_ident("a"), "1a");
        expect_ok!(ident, "num->str", Value::new_ident("num->str"));
        expect_ok!(ident, "//", Value::new_ident("//"));

        expect_error!(ident, "1a", 0);
    }

    #[test]
    fn list_() {
        expect_ok!(list, "()", Value::empty_list());
        //TODO: add more tests for lists with content
    }

    #[test]
    fn pair_() {
        let t = Value::new_bool(true);
        let f = Value::new_bool(false);
        let e = Value::empty_list();

        expect_ok!(pair, "(true . false)", Value::new_pair(t.clone(), f.clone()));
        expect_ok!(pair, "(true . (false . ()))", Value::new_pair(t, Value::new_pair(f.clone(), e.clone())));

        expect_error!(pair, "(1 .)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(. 2)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 . 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 2 . 3)", 0, ParserError::InvalidPair);
    }

    #[test]
    fn string_() {
        fn q(s: &str) -> String { format!("\"{}\"", s) }

        macro_rules! expect_str_ok {
            ($s:expr, $e:expr) => ({
                let s = q($s);
                expect_ok!(string, s, Value::new_string($e));
            });

            ($s:expr) => (expect_str_ok!($s, $s));
        }

        expect_str_ok!("");
        expect_str_ok!("abc");
        expect_str_ok!("Hello, World!!");
        expect_str_ok!("\n");
        expect_str_ok!("\\n", "\n");
        expect_str_ok!("\t");
        expect_str_ok!("\\t", "\t");
        expect_str_ok!("\\\\", "\\");
        expect_str_ok!("Hi there: \\\" \\\\ \\n \\t", "Hi there: \" \\ \n \t");

        //TODO add failing examples, es. UTF8 related
    }

    //TODO
    #[test]
    fn parse_() {
        //expect_error!(parse, "1 1", 1, ParserError::InvalidItem);

    }
}
<commit_msg>fix parser tests<commit_after>use nom::{self, digit, multispace, eof, ErrorKind, IResult};
use std::str;
use std::fmt;
use super::value::Value;
use super::scope::Scope;

static UTF8_ERROR: &'static str = "File is no valid UTF8!";

macro_rules! fix {
    ($i:expr, $it:ident) => (fix_error!($i, ParserError, $it));
    ($i:expr, $submac:ident!( $($args:tt)* )) => (
      fix_error!($i, ParserError, complete!($submac!($($args)*)))
    );
}

named!(bool_<&[u8], Value, ParserError>, fix!(map!(
    alt!(
        tag!("true") |
        tag!("false")),
    |x|{ Value::new_bool(x == b"true") })));

named!(char_<&[u8], Value, ParserError>, fix!(chain!(
    tag!("#\\") ~
    c: take!(1) ,
    ||{ Value::new_char(c[0] as char) })));

named!(end_of_item<&[u8], &[u8], ParserError>,
    fix!(alt!(
        multispace |
        eof |
        tag!(")"))));

fn is_valid_in_ident(x: u8) -> bool {
    (x >= 0x3A && x <= 0x7E) ||
    (x >= 0x2A && x <= 0x2F) ||
    (x >= 0x23 && x <= 0x27) ||
    x == '!' as u8
}

named!(ident<&[u8], Value, ParserError>, fix!(chain!(
    peek!(none_of!("0123456789().")) ~
    x: take_while1!(is_valid_in_ident),
    || Value::new_symbol((str::from_utf8(x).unwrap())))));

named!(pub integer<&[u8], Value, ParserError>,
    fix!(chain!(
        s: opt!(char!('-')) ~
        x: digit ,
        ||{
            let num: i64 = str::from_utf8(x).expect(UTF8_ERROR).parse().unwrap();
            if s.is_some() {
                Value::new_integer(-num)
            } else {
                Value::new_integer(num)
            }
        })));

// eats everything until a " char is encountered
// recognizes escaped characters
fn eat_string_content(input: &[u8]) -> IResult<&[u8], String, ParserError> {
    macro_rules! itry {
        ($e:expr) => ({
            match $e {
                Ok(_) => (),
                Err(x) => return IResult::Error(x),
            }
        })
    }

    fn save_str<'a>(input: &'a[u8], start: &mut usize, end: &mut usize, content: &mut String) -> Result<(), nom::Err<&'a [u8], ParserError>> {
        if start >= end { return Ok(())}
        let utf8_str = match str::from_utf8(&input[*start..*end]) {
            Ok(s) => s,
            Err(x) => return Err(nom::Err::Position(ErrorKind::Custom(ParserError::NonUtf8String), &input[0..x.valid_up_to()])),
        };
        content.push_str(utf8_str);
        *start = *end;
        return Ok(())
    }

    if input.len() == 0 {
        return IResult::Done(&input, String::new());
    }

    let mut end: usize = 0;
    let mut start: usize = 0;
    let mut content = String::new();
    while end < input.len() {
        if input[end] == '"' as u8 {
            break
        }
        if input[end] == '\\' as u8 {
            if (&input[end..]).len() <= 1 {
                break;
            }

            if input[end+1..].starts_with("n".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\n');
                start += 2;
                end += 2;
            } else if input[end+1..].starts_with("t".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\t');
                start += 2;
                end += 2;
            } else if input[end+1..].starts_with("\"".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\"');
                start += 2;
                end += 2;
            } else if input[end+1..].starts_with("\\".as_bytes()) {
                itry!(save_str(input, &mut start, &mut end, &mut content));
                content.push('\\');
                start += 2;
                end += 2;
            } else {
                end += 1;
            }
        } else {
            end += 1;
        }
    }

    itry!(save_str(input, &mut start, &mut end, &mut content));
    IResult::Done(&input[end..], content)
}

named!(string<&[u8], Value, ParserError>,
    chain!(
        fix!(tag!("\"")) ~
        x: eat_string_content ~
        fix!(tag!("\"")) ,
        || { Value::new_string(x) }
    ));

named!(item<&[u8], Value, ParserError>,
    complete!(chain!(
        opt!(multispace) ~
        quoted: opt!(tag!("'")) ~
        value: alt_complete!(
            bool_ |
            char_ |
            integer |
            string |
            ident |
            pair |
            list) ~
        peek!(end_of_item),
        || if quoted.is_some() {
            Value::new_pair(Value::new_symbol("quote"), Value::new_pair(value, Value::empty_list()))
        } else { value })));

named!(pair<&[u8], Value, ParserError>,
    error!(ErrorKind::Custom(ParserError::InvalidPair), complete!(delimited!(
            fix!(tag!("(")),
            chain!(
                a: item ~
                fix!(multispace) ~
                fix!(tag!(".")) ~
                fix!(multispace) ~
                b: item ,
                || { Value::new_pair(a,b) }
            ),
            fix!(tag!(")"))))));

named!(list_inner<Vec<Value> >,
    many0!(item));

named!(list<&[u8], Value, ParserError>,
    alt!(
        fix!(map!(
            tag!("()") ,
            |_| Value::empty_list())) |
        fix!(map!(
            delimited!(
                tag!("("),
                list_inner,
                tag!(")")),
            |x: Vec<Value>| {
                Value::new_list(&x)
            }))
        ));

pub fn parse(input: &[u8]) -> Result<Value, String> {
    use nom::Err as NomErr;
    match item(input) {
        IResult::Done(remaining, value) => {
            if remaining.len() > 0 {
                Err(format!("error, expected EOF, found: {}", str::from_utf8(remaining).unwrap()))
            } else {
                Ok(value)
            }
        },
        IResult::Incomplete(_) => unimplemented!(), // because of complete
        IResult::Error(NomErr::Position(err, _)) => Err(create_error_string(err)),
        IResult::Error(NomErr::NodePosition(err, _, _)) => Err(create_error_string(err)),
        _ => unimplemented!(),
    }
}

fn create_error_string(err: ErrorKind<ParserError>) -> String {
    let msg = match err {
        ErrorKind::Custom(x) => format!("{}", x),
        x => format!("parser error {}", x.description()),
    };

    format!("error: {}", msg)
}

#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ParserError {
    MultipleChars, // more than 1 character between '', eg. 'ab'
    MissingChar,   // no char between '', eg. ''
    InvalidPair,   // eg. (1 2 . 3) or (1 . 2 3)
    InvalidItem,   // eg. 1 2
    NonUtf8String, // well..
}

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParserError::MultipleChars => write!(f, "more than one character between ''"),
            ParserError::MissingChar => write!(f, "no character between ''"),
            ParserError::InvalidPair => write!(f, "invalid pair"),
            ParserError::InvalidItem => write!(f, "invalid item"),
            ParserError::NonUtf8String => write!(f, "string is no valid UTF8"),
        }
    }
}

#[cfg(test)]
mod test {
    use super::{ParserError, bool_, char_, integer, string, ident, pair, list, item, parse};
    use nom::{IResult, Err, ErrorKind};
    use super::super::value::Value;

    macro_rules! expect_error {
        // with error kind
        ($parser:ident, $input:expr, $pos:expr, $errorkind:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            let res = $parser(input);
            assert!(match res {
                IResult::Error(
                    Err::NodePosition(
                        ErrorKind::Custom(kind),
                        pos,
                        _)
                ) if (pos == error_pos && kind == $errorkind) => { true },
                x => { println!("{:?}", x); false }
            });
        );
        // without error kind
        ($parser:ident, $input:expr, $pos:expr) => (
            let input = $input.as_bytes();
            let error_pos = &input[$pos..];
            assert!(match $parser(input) {
                IResult::Error(Err::Position(_, pos)) if pos == error_pos => true,
                x => { println!("{:?}", x); false },
            });
        )
    }

    macro_rules! expect_ok {
        // with rest
        ($parser:ident, $input:expr, $expected:expr, $rest:expr) => (
            let input = $input.as_bytes();
            let rest = $rest.as_bytes();
            assert_eq!(
                $parser(input),
                IResult::Done(rest, $expected)););

        // without rest
        ($parser:ident, $input:expr, $expected:expr) => (
            expect_ok!($parser, $input, $expected, "");
        )
    }

    #[test]
    fn bool() {
        expect_ok!(bool_, "true", Value::new_bool(true));
        expect_ok!(bool_, "false", Value::new_bool(false));
        expect_error!(bool_, "trude", 0);
        expect_error!(bool_, "fale", 0);
    }

    #[test]
    fn char() {
        use std::u8;
        for x in 0..127 {
            if x == '\'' as u8 { continue } // skip ''', which is invalid (tested below)
            let input = String::from_utf8(vec!['#' as u8, '\\' as u8, x]).unwrap();
            expect_ok!(char_, input, Value::new_char(x as char));
        }
        expect_error!(char_, "#\\", 0);
    }

    #[test]
    fn integer_() {
        expect_ok!(integer, "007", Value::new_integer(7));
        expect_ok!(integer, "-007", Value::new_integer(-7));
        expect_ok!(integer, "123456789", Value::new_integer(123456789));
        expect_ok!(integer, "-123456789", Value::new_integer(-123456789));
        expect_ok!(integer, "123b456789", Value::new_integer(123), "b456789");
        expect_ok!(integer, "123456789c", Value::new_integer(123456789), "c");
        expect_ok!(integer, "00-7", Value::new_integer(0), "-7");

        expect_error!(integer, "a123456789", 0);
        expect_error!(integer, "--7", 1);
    }

    #[test]
    fn ident_() {
        expect_ok!(ident, "+", Value::new_symbol("+"));
        expect_ok!(ident, "a1a", Value::new_symbol("a"), "1a");
        expect_ok!(ident, "num->str", Value::new_symbol("num->str"));
        expect_ok!(ident, "//", Value::new_symbol("//"));

        expect_error!(ident, "1a", 0);
    }

    #[test]
    fn list_() {
        expect_ok!(list, "()", Value::empty_list());
        //TODO: add more tests for lists with content
    }

    #[test]
    fn pair_() {
        let t = Value::new_bool(true);
        let f = Value::new_bool(false);
        let e = Value::empty_list();

        expect_ok!(pair, "(true . false)", Value::new_pair(t.clone(), f.clone()));
        expect_ok!(pair, "(true . (false . ()))", Value::new_pair(t, Value::new_pair(f.clone(), e.clone())));

        expect_error!(pair, "(1 .)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(. 2)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 . 2 . 3)", 0, ParserError::InvalidPair);
        expect_error!(pair, "(1 2 . 3)", 0, ParserError::InvalidPair);
    }

    #[test]
    fn string_() {
        fn q(s: &str) -> String { format!("\"{}\"", s) }

        macro_rules! expect_str_ok {
            ($s:expr, $e:expr) => ({
                let s = q($s);
                expect_ok!(string, s, Value::new_string($e));
            });

            ($s:expr) => (expect_str_ok!($s, $s));
        }

        expect_str_ok!("");
        expect_str_ok!("abc");
        expect_str_ok!("Hello, World!!");
        expect_str_ok!("\n");
        expect_str_ok!("\\n", "\n");
        expect_str_ok!("\t");
        expect_str_ok!("\\t", "\t");
        expect_str_ok!("\\\\", "\\");
        expect_str_ok!("Hi there: \\\" \\\\ \\n \\t", "Hi there: \" \\ \n \t");

        //TODO add failing examples, es. UTF8 related
    }

    //TODO
    #[test]
    fn parse_() {
        //expect_error!(parse, "1 1", 1, ParserError::InvalidItem);

    }
}
<|endoftext|>


<commit_before>use {KeyboardControllable, Key};
use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub enum ParseError {
    /// When a tag doesn't exist.
    /// Example: {+TEST}{-TEST}
    ///            ^^^^   ^^^^
    UnknownTag(String),

    /// When a { is encountered inside a {TAG}.
    /// Example: {+HELLO{WORLD}
    ///                 ^
    UnexpectedOpen,

    /// When a { is never matched with a }.
    /// Example: {+SHIFT}Hello{-SHIFT
    ///                              ^
    UnmatchedOpen,

    /// Opposite of UnmatchedOpen.
    /// Example: +SHIFT}Hello{-SHIFT}
    ///         ^
    UnmatchedClose,
}
impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::UnknownTag(_) => "Unknown tag",
            ParseError::UnexpectedOpen => "Unexpected { inside tag name",
            ParseError::UnmatchedOpen => "Unmatched {. No matching }",
            ParseError::UnmatchedClose => "Unmatched }. No matching {",
        }
    }
}
impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

pub fn parse(enigo: &mut KeyboardControllable, string: &str) -> Result<(), ParseError> {
    let mut capture = None;

    let mut unicode = false;
    let mut escapeopen = false;
    let mut escapeclose = false;

    for c in string.chars() {
        if escapeopen {
            escapeopen = false;

            if c != '{' {
                if capture.is_none() {
                    capture = Some(String::with_capacity(1));
                } else {
                    return Err(ParseError::UnexpectedOpen);
                }
            }
        } else if c == '{' {
            escapeopen = true;
            continue;
        }

        if c == '}' {
            if escapeclose {
                escapeclose = false;
                continue;
            }

            if capture.is_some() {
                match capture.unwrap().as_str() {
                    "+SHIFT" => enigo.key_down(Key::Shift),
                    "-SHIFT" => enigo.key_up(Key::Shift),
                    "+CTRL" => enigo.key_down(Key::Control),
                    "-CTRL" => enigo.key_up(Key::Control),
                    "+UNICODE" => unicode = true,
                    "-UNICODE" => unicode = false,
                    string => return Err(ParseError::UnknownTag(string.to_string())),
                }
                capture = None;
                continue;
            } else {
                escapeclose = true;
            }
        } else if escapeclose {
            return Err(ParseError::UnmatchedClose);
        }

        if let Some(ref mut string) = capture {
            string.push(c);
        } else if unicode {
            enigo.key_sequence(c.to_string().as_str());
        } else {
            enigo.key_click(Key::Layout(c));
        }
    }

    if escapeopen || capture.is_some() {
        return Err(ParseError::UnmatchedOpen);
    }
    if escapeclose {
        return Err(ParseError::UnmatchedClose);
    }
    Ok(())
}
<commit_msg>Allow space and tab in parser<commit_after>use {KeyboardControllable, Key};
use std::error::Error;
use std::fmt;

#[derive(Debug)]
pub enum ParseError {
    /// When a tag doesn't exist.
    /// Example: {+TEST}{-TEST}
    ///            ^^^^   ^^^^
    UnknownTag(String),

    /// When a { is encountered inside a {TAG}.
    /// Example: {+HELLO{WORLD}
    ///                 ^
    UnexpectedOpen,

    /// When a { is never matched with a }.
    /// Example: {+SHIFT}Hello{-SHIFT
    ///                              ^
    UnmatchedOpen,

    /// Opposite of UnmatchedOpen.
    /// Example: +SHIFT}Hello{-SHIFT}
    ///         ^
    UnmatchedClose,
}
impl Error for ParseError {
    fn description(&self) -> &str {
        match *self {
            ParseError::UnknownTag(_) => "Unknown tag",
            ParseError::UnexpectedOpen => "Unexpected { inside tag name",
            ParseError::UnmatchedOpen => "Unmatched {. No matching }",
            ParseError::UnmatchedClose => "Unmatched }. No matching {",
        }
    }
}
impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.description())
    }
}

pub fn parse(enigo: &mut KeyboardControllable, string: &str) -> Result<(), ParseError> {
    let mut capture = None;

    let mut unicode = false;
    let mut escapeopen = false;
    let mut escapeclose = false;

    for c in string.chars() {
        if escapeopen {
            escapeopen = false;

            if c != '{' {
                if capture.is_none() {
                    capture = Some(String::with_capacity(1));
                } else {
                    return Err(ParseError::UnexpectedOpen);
                }
            }
        } else if c == '{' {
            escapeopen = true;
            continue;
        }

        if c == '}' {
            if escapeclose {
                escapeclose = false;
                continue;
            }

            if capture.is_some() {
                match capture.unwrap().as_str() {
                    "+SHIFT" => enigo.key_down(Key::Shift),
                    "-SHIFT" => enigo.key_up(Key::Shift),
                    "+CTRL" => enigo.key_down(Key::Control),
                    "-CTRL" => enigo.key_up(Key::Control),
                    "+UNICODE" => unicode = true,
                    "-UNICODE" => unicode = false,
                    string => return Err(ParseError::UnknownTag(string.to_string())),
                }
                capture = None;
                continue;
            } else {
                escapeclose = true;
            }
        } else if escapeclose {
            return Err(ParseError::UnmatchedClose);
        }

        if let Some(ref mut string) = capture {
            string.push(c);
        } else if unicode {
            enigo.key_sequence(c.to_string().as_str());
        } else {
            enigo.key_click(match c {
                ' '  => Key::Space,
                '\t' => Key::Tab,
                c    => Key::Layout(c)
            })
        }
    }

    if escapeopen || capture.is_some() {
        return Err(ParseError::UnmatchedOpen);
    }
    if escapeclose {
        return Err(ParseError::UnmatchedClose);
    }
    Ok(())
}
<|endoftext|>


<commit_before>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error::Error;
use std::sync::Arc;

use buffer::Buffer;
use command_buffer::cb::CommandsListBuildPrimaryPool;
use command_buffer::cb::Flags;
use command_buffer::cb::Kind;
use command_buffer::cb::UnsyncedCommandBuffer;
use command_buffer::pool::CommandPool;
use command_buffer::submit::Submit;
use command_buffer::submit::SubmitBuilder;
use command_buffer::CommandsList;
use command_buffer::CommandsListSink;
use command_buffer::CommandsListSinkCaller;
use command_buffer::SecondaryCommandBuffer;
use device::Device;
use device::Queue;
use image::Layout;
use image::Image;
use sync::AccessFlagBits;
use sync::PipelineStages;
use VulkanObject;
use vk;

use OomError;

pub struct AutobarriersCommandBuffer<L, P> where P: CommandPool {
    // The actual command buffer.
    inner: UnsyncedCommandBuffer<WrappedCommandsList<L>, P>
}

impl<L, P> CommandsListBuildPrimaryPool<L, P> for AutobarriersCommandBuffer<L, P>
    where L: CommandsList, P: CommandPool
{
    fn build_primary_with_pool(pool: P, list: L)
                               -> Result<AutobarriersCommandBuffer<L, P>, OomError>
        where Self: Sized
    {
        let kind = Kind::primary();
        let flags = Flags::SimultaneousUse;

        let cmd = unsafe {
            let device = pool.device().clone();
            try!(UnsyncedCommandBuffer::new(WrappedCommandsList(list, device), pool, kind, flags))
        };

        Ok(AutobarriersCommandBuffer {
            inner: cmd,
        })
    }
}

unsafe impl<L, P> Submit for AutobarriersCommandBuffer<L, P>
    where L: CommandsList, P: CommandPool
{
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }

    unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
                                    -> Result<SubmitBuilder<'a>, Box<Error>>
    {
        // FIXME: totally unsynchronized here
        Ok(base.add_command_buffer(&self.inner))
    }
}

// TODO: we're not necessarily a secondary command buffer
unsafe impl<L, P> SecondaryCommandBuffer for AutobarriersCommandBuffer<L, P>
    where L: CommandsList, P: CommandPool
{
    #[inline]
    fn inner(&self) -> vk::CommandBuffer {
        self.inner.internal_object()
    }

    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }

    #[inline]
    fn append<'a>(&'a self, builder: &mut CommandsListSink<'a>) {
        self.inner.commands_list().append(builder);
    }
}

struct WrappedCommandsList<L>(L, Arc<Device>);
unsafe impl<L> CommandsList for WrappedCommandsList<L> where L: CommandsList {
    #[inline]
    fn append<'a>(&'a self, builder: &mut CommandsListSink<'a>) {
        self.0.append(&mut Sink {
            output: builder,
            device: &self.1,
        });
    }
}

// Helper object for AutobarriersCommandBuffer. Implementation detail.
struct Sink<'c: 'o, 'o> {
    output: &'o mut CommandsListSink<'c>,
    device: &'o Arc<Device>,
}

impl<'c: 'o, 'o> CommandsListSink<'c> for Sink<'c, 'o> {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.device
    }

    #[inline]
    fn add_command(&mut self, f: Box<CommandsListSinkCaller<'c> + 'c>) {
        self.output.add_command(f);
    }

    #[inline]
    fn add_buffer_transition(&mut self, _: &Buffer, _: usize, _: usize, _: bool,
                             _: PipelineStages, _: AccessFlagBits)
    {
    }

    #[inline]
    fn add_image_transition(&mut self, _: &Image, _: u32, _: u32, _: u32, _: u32,
                            _: bool, _: Layout, _: PipelineStages, _: AccessFlagBits)
    {
    }

    #[inline]
    fn add_image_transition_notification(&mut self, _: &Image, _: u32, _: u32, _: u32,
                                         _: u32, _: Layout, _: PipelineStages, _: AccessFlagBits)
    {
    }
}
<commit_msg>Add Key in autobarriers<commit_after>// Copyright (c) 2016 The vulkano developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT
// license <LICENSE-MIT or http://opensource.org/licenses/MIT>,
// at your option. All files in the project carrying such
// notice may not be copied, modified, or distributed except
// according to those terms.

use std::error::Error;
use std::hash::{Hash, Hasher};
use std::sync::Arc;

use buffer::Buffer;
use command_buffer::cb::CommandsListBuildPrimaryPool;
use command_buffer::cb::Flags;
use command_buffer::cb::Kind;
use command_buffer::cb::UnsyncedCommandBuffer;
use command_buffer::pool::CommandPool;
use command_buffer::submit::Submit;
use command_buffer::submit::SubmitBuilder;
use command_buffer::CommandsList;
use command_buffer::CommandsListSink;
use command_buffer::CommandsListSinkCaller;
use command_buffer::SecondaryCommandBuffer;
use device::Device;
use device::Queue;
use image::Layout;
use image::Image;
use sync::AccessFlagBits;
use sync::PipelineStages;
use VulkanObject;
use vk;

use OomError;

pub struct AutobarriersCommandBuffer<L, P> where P: CommandPool {
    // The actual command buffer.
    inner: UnsyncedCommandBuffer<WrappedCommandsList<L>, P>
}

impl<L, P> CommandsListBuildPrimaryPool<L, P> for AutobarriersCommandBuffer<L, P>
    where L: CommandsList, P: CommandPool
{
    fn build_primary_with_pool(pool: P, list: L)
                               -> Result<AutobarriersCommandBuffer<L, P>, OomError>
        where Self: Sized
    {
        let kind = Kind::primary();
        let flags = Flags::SimultaneousUse;

        let cmd = unsafe {
            let device = pool.device().clone();
            try!(UnsyncedCommandBuffer::new(WrappedCommandsList(list, device), pool, kind, flags))
        };

        Ok(AutobarriersCommandBuffer {
            inner: cmd,
        })
    }
}

unsafe impl<L, P> Submit for AutobarriersCommandBuffer<L, P>
    where L: CommandsList, P: CommandPool
{
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }

    unsafe fn append_submission<'a>(&'a self, base: SubmitBuilder<'a>, queue: &Arc<Queue>)
                                    -> Result<SubmitBuilder<'a>, Box<Error>>
    {
        // FIXME: totally unsynchronized here
        Ok(base.add_command_buffer(&self.inner))
    }
}

// TODO: we're not necessarily a secondary command buffer
unsafe impl<L, P> SecondaryCommandBuffer for AutobarriersCommandBuffer<L, P>
    where L: CommandsList, P: CommandPool
{
    #[inline]
    fn inner(&self) -> vk::CommandBuffer {
        self.inner.internal_object()
    }

    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.inner.device()
    }

    #[inline]
    fn append<'a>(&'a self, builder: &mut CommandsListSink<'a>) {
        self.inner.commands_list().append(builder);
    }
}

struct WrappedCommandsList<L>(L, Arc<Device>);
unsafe impl<L> CommandsList for WrappedCommandsList<L> where L: CommandsList {
    #[inline]
    fn append<'a>(&'a self, builder: &mut CommandsListSink<'a>) {
        self.0.append(&mut Sink {
            output: builder,
            device: &self.1,
        });
    }
}

// Helper object for AutobarriersCommandBuffer. Implementation detail.
struct Sink<'c: 'o, 'o> {
    output: &'o mut CommandsListSink<'c>,
    device: &'o Arc<Device>,
}

impl<'c: 'o, 'o> CommandsListSink<'c> for Sink<'c, 'o> {
    #[inline]
    fn device(&self) -> &Arc<Device> {
        self.device
    }

    #[inline]
    fn add_command(&mut self, f: Box<CommandsListSinkCaller<'c> + 'c>) {
        self.output.add_command(f);
    }

    #[inline]
    fn add_buffer_transition(&mut self, _: &Buffer, _: usize, _: usize, _: bool,
                             _: PipelineStages, _: AccessFlagBits)
    {
    }

    #[inline]
    fn add_image_transition(&mut self, _: &Image, _: u32, _: u32, _: u32, _: u32,
                            _: bool, _: Layout, _: PipelineStages, _: AccessFlagBits)
    {
    }

    #[inline]
    fn add_image_transition_notification(&mut self, _: &Image, _: u32, _: u32, _: u32,
                                         _: u32, _: Layout, _: PipelineStages, _: AccessFlagBits)
    {
    }
}

struct Key<'a> {
    hash: u64,
    stages: PipelineStages,
    access: AccessFlagBits,
    inner: KeyInner<'a>,
}

impl<'a> Hash for Key<'a> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        state.write_u64(self.hash);
    }
}

impl<'a> PartialEq for Key<'a> {
    fn eq(&self, other: &Key<'a>) -> bool {
        match (&self.inner, &other.inner) {
            (&KeyInner::Buffer { buffer: self_buffer, offset: self_offset, size: self_size,
                                 write: self_write },
             &KeyInner::Buffer { buffer: other_buffer, offset: other_offset, size: other_size,
                                 write: other_write }) =>
            {
                 self_buffer.conflicts_buffer(self_offset, self_size, self_write, other_buffer,
                                              other_offset, other_size, other_write)
             },
        }
    }
}

impl<'a> Eq for Key<'a> {}

enum KeyInner<'a> {
    Buffer {
        buffer: &'a Buffer,
        offset: usize,
        size: usize,
        write: bool,
    },
}
<|endoftext|>