<commit_before>use crate::{Comment, Span};
use logos::{Logos, SpannedIter};

pub struct Lexer<'input> {
    pub comments: Vec<Comment>,
    raw_token_stream: std::iter::Peekable<SpannedIter<'input, RawToken>>,
}

impl<'input> Lexer<'input> {
    pub fn new(input: &'input str) -> Self {
        Self {
            comments: Vec::new(),
            raw_token_stream: RawToken::lexer(input).spanned().peekable(),
        }
    }

    fn collect_comments(&mut self) -> Comments {
        let leading = std::mem::take(&mut self.comments);
        let has_trailing_comment = matches!(
            self.raw_token_stream.peek(),
            Some((RawToken::Comment(_), _))
        );
        if has_trailing_comment {
            if let Some((RawToken::Comment(string), _)) = self.raw_token_stream.next() {
                return Comments {
                    leading,
                    trailing: Some(Comment(string)),
                };
            } else {
                unreachable!()
            }
        }
        Comments {
            leading,
            trailing: None,
        }
    }

    // TODO: pub method for draining any dangling comments?
}

#[derive(Debug)]
pub enum Error {
    InvalidToken(Span),
}

impl<'input> Iterator for Lexer<'input> {
    type Item = Result<(usize, Token, usize), Error>;

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.raw_token_stream.next();
        let (start_offset, raw_token, end_offset) = match next {
            None => return None,
            Some((RawToken::Error, range)) => {
                return Some(Err(Error::InvalidToken(Span {
                    start_offset: range.start,
                    end_offset: range.end,
                })))
            }
            Some((RawToken::Newline, _)) => {
                // skip
                return self.next();
            }
            Some((RawToken::Comment(string), _)) => {
                self.comments.push(Comment(string));
                return self.next();
            }
            Some((raw_token, range)) => (range.start, raw_token, range.end),
        };

        let token = match raw_token {
            // Handled above ☝
            RawToken::Comment(_) | RawToken::Newline | RawToken::Error => unreachable!(),

            RawToken::Dot => Token::Dot(self.collect_comments()),
            RawToken::DoubleDot => Token::DoubleDot(self.collect_comments()),
            RawToken::Comma => Token::Comma(self.collect_comments()),
            RawToken::Colon => Token::Colon(self.collect_comments()),
            RawToken::Semicolon => Token::Semicolon(self.collect_comments()),
            RawToken::Equals => Token::Equals(self.collect_comments()),
            RawToken::OpenParen => Token::OpenParen(self.collect_comments()),
            RawToken::CloseParen => Token::CloseParen(self.collect_comments()),
            RawToken::OpenBracket => Token::OpenBracket(self.collect_comments()),
            RawToken::CloseBracket => Token::CloseBracket(self.collect_comments()),
            RawToken::OpenBrace => Token::OpenBrace(self.collect_comments()),
            RawToken::CloseBrace => Token::CloseBrace(self.collect_comments()),
            RawToken::LeftArrow => Token::LeftArrow(self.collect_comments()),
            RawToken::RightArrow => Token::RightArrow(self.collect_comments()),
            RawToken::Pipe => Token::Pipe(self.collect_comments()),
            RawToken::ModuleKeyword => Token::ModuleKeyword(self.collect_comments()),
            RawToken::ExportsKeyword => Token::ExportsKeyword(self.collect_comments()),
            RawToken::ImportKeyword => Token::ImportKeyword(self.collect_comments()),
            RawToken::AsKeyword => Token::AsKeyword(self.collect_comments()),
            RawToken::TrueKeyword => Token::TrueKeyword(self.collect_comments()),
            RawToken::FalseKeyword => Token::FalseKeyword(self.collect_comments()),
            RawToken::UnitKeyword => Token::UnitKeyword(self.collect_comments()),
            RawToken::IfKeyword => Token::IfKeyword(self.collect_comments()),
            RawToken::ThenKeyword => Token::ThenKeyword(self.collect_comments()),
            RawToken::ElseKeyword => Token::ElseKeyword(self.collect_comments()),
            RawToken::TypeKeyword => Token::TypeKeyword(self.collect_comments()),
            RawToken::ForeignKeyword => Token::ForeignKeyword(self.collect_comments()),
            RawToken::MatchKeyword => Token::MatchKeyword(self.collect_comments()),
            RawToken::WithKeyword => Token::WithKeyword(self.collect_comments()),
            RawToken::LetKeyword => Token::LetKeyword(self.collect_comments()),
            RawToken::DoKeyword => Token::DoKeyword(self.collect_comments()),
            RawToken::ReturnKeyword => Token::ReturnKeyword(self.collect_comments()),
            RawToken::FnKeyword => Token::FnKeyword(self.collect_comments()),
            RawToken::EndKeyword => Token::EndKeyword(self.collect_comments()),
            RawToken::RightPizzaOperator => Token::RightPizzaOperator(self.collect_comments()),
            RawToken::Name(string) => Token::Name((self.collect_comments(), string)),
            RawToken::ProperName(string) => Token::ProperName((self.collect_comments(), string)),
            RawToken::UnusedName(string) => Token::UnusedName((self.collect_comments(), string)),
            RawToken::PackageName(string) => Token::PackageName((self.collect_comments(), string)),
            RawToken::String(string) => Token::String((
                self.collect_comments(),
                // Remove the surrounding quotes
                string[1..string.len() - 1].to_owned(),
            )),
            RawToken::Number(string) => {
                if string.contains('.') {
                    Token::Float((self.collect_comments(), string))
                } else {
                    Token::Int((self.collect_comments(), string))
                }
            }
        };
        Some(Ok((start_offset, token, end_offset)))
    }
}

#[derive(Debug, Clone)]
pub enum Token {
    Dot(Comments),
    DoubleDot(Comments),
    Comma(Comments),
    Colon(Comments),
    Semicolon(Comments),
    Equals(Comments),
    OpenParen(Comments),
    CloseParen(Comments),
    OpenBracket(Comments),
    CloseBracket(Comments),
    OpenBrace(Comments),
    CloseBrace(Comments),
    LeftArrow(Comments),
    RightArrow(Comments),
    Pipe(Comments),
    ModuleKeyword(Comments),
    ExportsKeyword(Comments),
    ImportKeyword(Comments),
    AsKeyword(Comments),
    TrueKeyword(Comments),
    FalseKeyword(Comments),
    UnitKeyword(Comments),
    IfKeyword(Comments),
    ThenKeyword(Comments),
    ElseKeyword(Comments),
    TypeKeyword(Comments),
    ForeignKeyword(Comments),
    MatchKeyword(Comments),
    WithKeyword(Comments),
    LetKeyword(Comments),
    DoKeyword(Comments),
    ReturnKeyword(Comments),
    FnKeyword(Comments),
    EndKeyword(Comments),
    RightPizzaOperator(Comments),
    Name((Comments, String)),
    ProperName((Comments, String)),
    UnusedName((Comments, String)),
    PackageName((Comments, String)),
    String((Comments, String)),
    Int((Comments, String)),
    Float((Comments, String)),
}

#[derive(Debug, Clone)]
pub struct Comments {
    pub leading: Vec<Comment>,
    pub trailing: Option<Comment>,
}

#[derive(Logos, Debug, PartialEq)]
enum RawToken {
    #[token(".")]
    Dot,
    #[token("..")]
    DoubleDot,
    #[token(",")]
    Comma,
    #[token(":")]
    Colon,
    #[token(";")]
    Semicolon,
    #[token("=")]
    Equals,
    #[token("(")]
    OpenParen,
    #[token(")")]
    CloseParen,
    #[token("[")]
    OpenBracket,
    #[token("]")]
    CloseBracket,
    #[token("{")]
    OpenBrace,
    #[token("}")]
    CloseBrace,
    #[token("<-")]
    LeftArrow,
    #[token("->")]
    RightArrow,
    #[token("|")]
    Pipe,
    #[token("module")]
    ModuleKeyword,
    #[token("exports")]
    ExportsKeyword,
    #[token("import")]
    ImportKeyword,
    #[token("as")]
    AsKeyword,
    #[token("true")]
    TrueKeyword,
    #[token("false")]
    FalseKeyword,
    #[token("unit")]
    UnitKeyword,
    #[token("if")]
    IfKeyword,
    #[token("then")]
    ThenKeyword,
    #[token("else")]
    ElseKeyword,
    #[token("type")]
    TypeKeyword,
    #[token("foreign")]
    ForeignKeyword,
    #[token("match")]
    MatchKeyword,
    #[token("with")]
    WithKeyword,
    #[token("let")]
    LetKeyword,
    #[token("do")]
    DoKeyword,
    #[token("return")]
    ReturnKeyword,
    #[token("fn")]
    FnKeyword,
    #[token("end")]
    EndKeyword,

    #[token("|>")]
    RightPizzaOperator,

    #[regex(r"[a-z]\w*", priority = 2, callback = |lex| lex.slice().parse())]
    Name(String), //     ^^ Needs to be higher priority than PackageName

    #[regex(r"[A-Z]\w*", callback = |lex| lex.slice().parse())]
    ProperName(String),

    #[regex(r"_(?:[a-z]\w*)?", callback = |lex| lex.slice().parse())]
    UnusedName(String),

    #[regex(r"[a-z][a-z0-9-]*", callback = |lex| lex.slice().parse())]
    PackageName(String),

    #[regex(r"--[^\n]*", callback = |lex| lex.slice().parse())]
    Comment(String),

    #[regex(r"\d[\d_]*(?:\.\d[\d_]*)?", callback = |lex| lex.slice().parse())]
    Number(String),

    // TODO: improve this regex
    #[regex(r#""[^"]*""#, callback = |lex| lex.slice().parse())]
    String(String),

    #[regex(r"\n")]
    Newline,

    #[regex(r"[ \t]+", logos::skip)]
    #[error]
    Error,
}

#[cfg(test)]
mod tests {
    use super::{Comments, Error, Lexer, Token};

    #[test]
    fn it_lexes_as_expected() {
        let mut lexer = Lexer::new(
            r"  unit
                match with
                -- comment 0
                do
                --   comment 1
                -- comment 2
                return
                module     -- comment 3

                -- comment 4
                type -- comment 5
                -- comment 6

                -- comment 7
                exports -- comment 8
        ",
        );
        assert!(matches!(
            lexer.next(),
            Some(Ok((_, Token::UnitKeyword { .. }, _)))
        ));
        assert!(matches!(
            lexer.next(),
            Some(Ok((_, Token::MatchKeyword { .. }, _)))
        ));
        assert!(matches!(
            lexer.next(),
            Some(Ok((_, Token::WithKeyword { .. }, _)))
        ));
        if let Some(Ok((_, Token::DoKeyword(Comments { leading, trailing }), _))) = lexer.next() {
            assert_eq!(leading.len(), 1);
            assert_eq!(leading[0].0, "-- comment 0");
            assert!(trailing.is_none());
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::ReturnKeyword(Comments { leading, trailing }), _))) = lexer.next()
        {
            assert_eq!(leading.len(), 2);
            assert!(trailing.is_none());
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::ModuleKeyword(Comments { leading, trailing }), _))) = lexer.next()
        {
            assert!(leading.is_empty());
            assert!(trailing.is_some());
            assert_eq!(trailing.unwrap().0, "-- comment 3");
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::TypeKeyword(Comments { leading, trailing }), _))) = lexer.next() {
            assert_eq!(leading.len(), 1);
            assert_eq!(leading[0].0, "-- comment 4");
            assert!(trailing.is_some());
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::ExportsKeyword(Comments { leading, trailing }), _))) =
            lexer.next()
        {
            assert_eq!(leading.len(), 2);
            assert!(trailing.is_some());
            assert_eq!(trailing.unwrap().0, "-- comment 8");
        } else {
            panic!()
        }
    }

    macro_rules! assert_token {
        ($input:expr, $want:pat_param) => {{
            let mut lexer = crate::lexer::Lexer::new($input);
            let next = lexer.next();
            assert!(matches!(next, Some(Ok((_, $want, _)))), "{:?}", next);
        }};
    }

    #[test]
    fn it_lexes_names() {
        assert_token!("this_is_a_Name", Token::Name { .. });
        assert_token!("abcde", Token::Name { .. });
        assert_token!("a_b_cde_", Token::Name { .. });
        assert_token!("a123456789", Token::Name { .. });
        assert_token!("héllö", Token::Name { .. });
    }

    #[test]
    fn it_lexes_proper_names() {
        assert_token!("Abcde", Token::ProperName { .. });
        assert_token!("A_b_cde_", Token::ProperName { .. });
        assert_token!("A123456789", Token::ProperName { .. });
        assert_token!("ABCD", Token::ProperName { .. });
        assert_token!("Héllö", Token::ProperName { .. });
    }

    #[test]
    fn it_lexes_unsued_names() {
        assert_token!("_abcde", Token::UnusedName { .. });
        assert_token!("_a_b_cde_", Token::UnusedName { .. });
        assert_token!("_a123456789", Token::UnusedName { .. });
        assert_token!("_héllö", Token::UnusedName { .. });
    }

    #[test]
    fn it_lexes_package_names() {
        assert_token!("some-package3", Token::PackageName { .. });
    }

    #[test]
    fn it_lexes_integers() {
        assert_token!("5", Token::Int { .. });
        assert_token!("0", Token::Int { .. });
        assert_token!("123456789000000", Token::Int { .. });
        assert_token!("0005", Token::Int { .. });
        assert_token!("10_000_000", Token::Int { .. });
        assert_token!("--leading\n--leading0\n10 --trailing", Token::Int { .. });
    }

    #[test]
    fn it_lexes_floats() {
        assert_token!("5.0", Token::Float { .. });
        assert_token!("0.0", Token::Float { .. });
        assert_token!("5.0000", Token::Float { .. });
        assert_token!("123456789000000.123456", Token::Float { .. });
        assert_token!("1___2__3_.0___", Token::Float { .. });
        assert_token!(
            "--leading\n--leading0\n10.10 --trailing",
            Token::Float { .. }
        );
    }

    #[test]
    fn it_lexes_strings() {
        assert_token!(r#" "" "#, Token::String { .. });
        assert_token!(r#" "old school" "#, Token::String { .. });
        assert_token!(r#" " padded " "#, Token::String { .. });
        assert_token!(r#" "Hello, 世界" "#, Token::String { .. });
        assert_token!(r#" "👌🚀" "#, Token::String { .. });
        assert_token!(r#" "\n\r\t\"\\" "#, Token::String { .. });
    }

    #[test]
    fn it_errors_as_expected() {
        let mut lexer = Lexer::new("++");
        assert!(matches!(lexer.next(), Some(Err(Error::InvalidToken(_)))));
    }
}
<commit_msg>Fix lexer to handle CRLF<commit_after>use crate::{Comment, Span};
use logos::{Logos, SpannedIter};

pub struct Lexer<'input> {
    pub comments: Vec<Comment>,
    raw_token_stream: std::iter::Peekable<SpannedIter<'input, RawToken>>,
}

impl<'input> Lexer<'input> {
    pub fn new(input: &'input str) -> Self {
        Self {
            comments: Vec::new(),
            raw_token_stream: RawToken::lexer(input).spanned().peekable(),
        }
    }

    fn collect_comments(&mut self) -> Comments {
        let leading = std::mem::take(&mut self.comments);
        let has_trailing_comment = matches!(
            self.raw_token_stream.peek(),
            Some((RawToken::Comment(_), _))
        );
        if has_trailing_comment {
            if let Some((RawToken::Comment(string), _)) = self.raw_token_stream.next() {
                return Comments {
                    leading,
                    trailing: Some(Comment(string)),
                };
            } else {
                unreachable!()
            }
        }
        Comments {
            leading,
            trailing: None,
        }
    }

    // TODO: pub method for draining any dangling comments?
}

#[derive(Debug)]
pub enum Error {
    InvalidToken(Span),
}

impl<'input> Iterator for Lexer<'input> {
    type Item = Result<(usize, Token, usize), Error>;

    fn next(&mut self) -> Option<Self::Item> {
        let next = self.raw_token_stream.next();
        let (start_offset, raw_token, end_offset) = match next {
            None => return None,
            Some((RawToken::Error, range)) => {
                return Some(Err(Error::InvalidToken(Span {
                    start_offset: range.start,
                    end_offset: range.end,
                })))
            }
            Some((RawToken::Newline, _)) => {
                // skip
                return self.next();
            }
            Some((RawToken::Comment(string), _)) => {
                self.comments.push(Comment(string));
                return self.next();
            }
            Some((raw_token, range)) => (range.start, raw_token, range.end),
        };

        let token = match raw_token {
            // Handled above ☝
            RawToken::Comment(_) | RawToken::Newline | RawToken::Error => unreachable!(),

            RawToken::Dot => Token::Dot(self.collect_comments()),
            RawToken::DoubleDot => Token::DoubleDot(self.collect_comments()),
            RawToken::Comma => Token::Comma(self.collect_comments()),
            RawToken::Colon => Token::Colon(self.collect_comments()),
            RawToken::Semicolon => Token::Semicolon(self.collect_comments()),
            RawToken::Equals => Token::Equals(self.collect_comments()),
            RawToken::OpenParen => Token::OpenParen(self.collect_comments()),
            RawToken::CloseParen => Token::CloseParen(self.collect_comments()),
            RawToken::OpenBracket => Token::OpenBracket(self.collect_comments()),
            RawToken::CloseBracket => Token::CloseBracket(self.collect_comments()),
            RawToken::OpenBrace => Token::OpenBrace(self.collect_comments()),
            RawToken::CloseBrace => Token::CloseBrace(self.collect_comments()),
            RawToken::LeftArrow => Token::LeftArrow(self.collect_comments()),
            RawToken::RightArrow => Token::RightArrow(self.collect_comments()),
            RawToken::Pipe => Token::Pipe(self.collect_comments()),
            RawToken::ModuleKeyword => Token::ModuleKeyword(self.collect_comments()),
            RawToken::ExportsKeyword => Token::ExportsKeyword(self.collect_comments()),
            RawToken::ImportKeyword => Token::ImportKeyword(self.collect_comments()),
            RawToken::AsKeyword => Token::AsKeyword(self.collect_comments()),
            RawToken::TrueKeyword => Token::TrueKeyword(self.collect_comments()),
            RawToken::FalseKeyword => Token::FalseKeyword(self.collect_comments()),
            RawToken::UnitKeyword => Token::UnitKeyword(self.collect_comments()),
            RawToken::IfKeyword => Token::IfKeyword(self.collect_comments()),
            RawToken::ThenKeyword => Token::ThenKeyword(self.collect_comments()),
            RawToken::ElseKeyword => Token::ElseKeyword(self.collect_comments()),
            RawToken::TypeKeyword => Token::TypeKeyword(self.collect_comments()),
            RawToken::ForeignKeyword => Token::ForeignKeyword(self.collect_comments()),
            RawToken::MatchKeyword => Token::MatchKeyword(self.collect_comments()),
            RawToken::WithKeyword => Token::WithKeyword(self.collect_comments()),
            RawToken::LetKeyword => Token::LetKeyword(self.collect_comments()),
            RawToken::DoKeyword => Token::DoKeyword(self.collect_comments()),
            RawToken::ReturnKeyword => Token::ReturnKeyword(self.collect_comments()),
            RawToken::FnKeyword => Token::FnKeyword(self.collect_comments()),
            RawToken::EndKeyword => Token::EndKeyword(self.collect_comments()),
            RawToken::RightPizzaOperator => Token::RightPizzaOperator(self.collect_comments()),
            RawToken::Name(string) => Token::Name((self.collect_comments(), string)),
            RawToken::ProperName(string) => Token::ProperName((self.collect_comments(), string)),
            RawToken::UnusedName(string) => Token::UnusedName((self.collect_comments(), string)),
            RawToken::PackageName(string) => Token::PackageName((self.collect_comments(), string)),
            RawToken::String(string) => Token::String((
                self.collect_comments(),
                // Remove the surrounding quotes
                string[1..string.len() - 1].to_owned(),
            )),
            RawToken::Number(string) => {
                if string.contains('.') {
                    Token::Float((self.collect_comments(), string))
                } else {
                    Token::Int((self.collect_comments(), string))
                }
            }
        };
        Some(Ok((start_offset, token, end_offset)))
    }
}

#[derive(Debug, Clone)]
pub enum Token {
    Dot(Comments),
    DoubleDot(Comments),
    Comma(Comments),
    Colon(Comments),
    Semicolon(Comments),
    Equals(Comments),
    OpenParen(Comments),
    CloseParen(Comments),
    OpenBracket(Comments),
    CloseBracket(Comments),
    OpenBrace(Comments),
    CloseBrace(Comments),
    LeftArrow(Comments),
    RightArrow(Comments),
    Pipe(Comments),
    ModuleKeyword(Comments),
    ExportsKeyword(Comments),
    ImportKeyword(Comments),
    AsKeyword(Comments),
    TrueKeyword(Comments),
    FalseKeyword(Comments),
    UnitKeyword(Comments),
    IfKeyword(Comments),
    ThenKeyword(Comments),
    ElseKeyword(Comments),
    TypeKeyword(Comments),
    ForeignKeyword(Comments),
    MatchKeyword(Comments),
    WithKeyword(Comments),
    LetKeyword(Comments),
    DoKeyword(Comments),
    ReturnKeyword(Comments),
    FnKeyword(Comments),
    EndKeyword(Comments),
    RightPizzaOperator(Comments),
    Name((Comments, String)),
    ProperName((Comments, String)),
    UnusedName((Comments, String)),
    PackageName((Comments, String)),
    String((Comments, String)),
    Int((Comments, String)),
    Float((Comments, String)),
}

#[derive(Debug, Clone)]
pub struct Comments {
    pub leading: Vec<Comment>,
    pub trailing: Option<Comment>,
}

#[derive(Logos, Debug, PartialEq)]
enum RawToken {
    #[token(".")]
    Dot,
    #[token("..")]
    DoubleDot,
    #[token(",")]
    Comma,
    #[token(":")]
    Colon,
    #[token(";")]
    Semicolon,
    #[token("=")]
    Equals,
    #[token("(")]
    OpenParen,
    #[token(")")]
    CloseParen,
    #[token("[")]
    OpenBracket,
    #[token("]")]
    CloseBracket,
    #[token("{")]
    OpenBrace,
    #[token("}")]
    CloseBrace,
    #[token("<-")]
    LeftArrow,
    #[token("->")]
    RightArrow,
    #[token("|")]
    Pipe,
    #[token("module")]
    ModuleKeyword,
    #[token("exports")]
    ExportsKeyword,
    #[token("import")]
    ImportKeyword,
    #[token("as")]
    AsKeyword,
    #[token("true")]
    TrueKeyword,
    #[token("false")]
    FalseKeyword,
    #[token("unit")]
    UnitKeyword,
    #[token("if")]
    IfKeyword,
    #[token("then")]
    ThenKeyword,
    #[token("else")]
    ElseKeyword,
    #[token("type")]
    TypeKeyword,
    #[token("foreign")]
    ForeignKeyword,
    #[token("match")]
    MatchKeyword,
    #[token("with")]
    WithKeyword,
    #[token("let")]
    LetKeyword,
    #[token("do")]
    DoKeyword,
    #[token("return")]
    ReturnKeyword,
    #[token("fn")]
    FnKeyword,
    #[token("end")]
    EndKeyword,

    #[token("|>")]
    RightPizzaOperator,

    #[regex(r"[a-z]\w*", priority = 2, callback = |lex| lex.slice().parse())]
    Name(String), //     ^^ Needs to be higher priority than PackageName

    #[regex(r"[A-Z]\w*", callback = |lex| lex.slice().parse())]
    ProperName(String),

    #[regex(r"_(?:[a-z]\w*)?", callback = |lex| lex.slice().parse())]
    UnusedName(String),

    #[regex(r"[a-z][a-z0-9-]*", callback = |lex| lex.slice().parse())]
    PackageName(String),

    #[regex(r"--[^\n]*", callback = |lex| lex.slice().parse())]
    Comment(String),

    #[regex(r"\d[\d_]*(?:\.\d[\d_]*)?", callback = |lex| lex.slice().parse())]
    Number(String),

    // TODO: improve this regex
    #[regex(r#""[^"]*""#, callback = |lex| lex.slice().parse())]
    String(String),

    #[regex(r"\r?\n")]
    Newline,

    // NOTE: this doesn't cover all non-newline whitespace,
    // but I'm not stressing that rn
    #[regex(r"[ \t]+", logos::skip)]
    #[error]
    Error,
}

#[cfg(test)]
mod tests {
    use super::{Comments, Error, Lexer, Token};

    #[test]
    fn it_lexes_as_expected() {
        let mut lexer = Lexer::new(
            r"  unit
                match with
                -- comment 0
                do
                --   comment 1
                -- comment 2
                return
                module     -- comment 3

                -- comment 4
                type -- comment 5
                -- comment 6

                -- comment 7
                exports -- comment 8
        ",
        );
        assert!(matches!(
            lexer.next(),
            Some(Ok((_, Token::UnitKeyword { .. }, _)))
        ));
        assert!(matches!(
            lexer.next(),
            Some(Ok((_, Token::MatchKeyword { .. }, _)))
        ));
        assert!(matches!(
            lexer.next(),
            Some(Ok((_, Token::WithKeyword { .. }, _)))
        ));
        if let Some(Ok((_, Token::DoKeyword(Comments { leading, trailing }), _))) = lexer.next() {
            assert_eq!(leading.len(), 1);
            assert_eq!(leading[0].0, "-- comment 0");
            assert!(trailing.is_none());
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::ReturnKeyword(Comments { leading, trailing }), _))) = lexer.next()
        {
            assert_eq!(leading.len(), 2);
            assert!(trailing.is_none());
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::ModuleKeyword(Comments { leading, trailing }), _))) = lexer.next()
        {
            assert!(leading.is_empty());
            assert!(trailing.is_some());
            assert_eq!(trailing.unwrap().0, "-- comment 3");
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::TypeKeyword(Comments { leading, trailing }), _))) = lexer.next() {
            assert_eq!(leading.len(), 1);
            assert_eq!(leading[0].0, "-- comment 4");
            assert!(trailing.is_some());
        } else {
            panic!()
        }
        if let Some(Ok((_, Token::ExportsKeyword(Comments { leading, trailing }), _))) =
            lexer.next()
        {
            assert_eq!(leading.len(), 2);
            assert!(trailing.is_some());
            assert_eq!(trailing.unwrap().0, "-- comment 8");
        } else {
            panic!()
        }
    }

    macro_rules! assert_token {
        ($input:expr, $want:pat_param) => {{
            let mut lexer = crate::lexer::Lexer::new($input);
            let next = lexer.next();
            assert!(matches!(next, Some(Ok((_, $want, _)))), "{:?}", next);
        }};
    }

    #[test]
    fn it_lexes_names() {
        assert_token!("this_is_a_Name", Token::Name { .. });
        assert_token!("abcde", Token::Name { .. });
        assert_token!("a_b_cde_", Token::Name { .. });
        assert_token!("a123456789", Token::Name { .. });
        assert_token!("héllö", Token::Name { .. });
    }

    #[test]
    fn it_lexes_proper_names() {
        assert_token!("Abcde", Token::ProperName { .. });
        assert_token!("A_b_cde_", Token::ProperName { .. });
        assert_token!("A123456789", Token::ProperName { .. });
        assert_token!("ABCD", Token::ProperName { .. });
        assert_token!("Héllö", Token::ProperName { .. });
    }

    #[test]
    fn it_lexes_unsued_names() {
        assert_token!("_abcde", Token::UnusedName { .. });
        assert_token!("_a_b_cde_", Token::UnusedName { .. });
        assert_token!("_a123456789", Token::UnusedName { .. });
        assert_token!("_héllö", Token::UnusedName { .. });
    }

    #[test]
    fn it_lexes_package_names() {
        assert_token!("some-package3", Token::PackageName { .. });
    }

    #[test]
    fn it_lexes_integers() {
        assert_token!("5", Token::Int { .. });
        assert_token!("0", Token::Int { .. });
        assert_token!("123456789000000", Token::Int { .. });
        assert_token!("0005", Token::Int { .. });
        assert_token!("10_000_000", Token::Int { .. });
        assert_token!("--leading\n--leading0\n10 --trailing", Token::Int { .. });
    }

    #[test]
    fn it_lexes_floats() {
        assert_token!("5.0", Token::Float { .. });
        assert_token!("0.0", Token::Float { .. });
        assert_token!("5.0000", Token::Float { .. });
        assert_token!("123456789000000.123456", Token::Float { .. });
        assert_token!("1___2__3_.0___", Token::Float { .. });
        assert_token!(
            "--leading\n--leading0\n10.10 --trailing",
            Token::Float { .. }
        );
    }

    #[test]
    fn it_lexes_strings() {
        assert_token!(r#" "" "#, Token::String { .. });
        assert_token!(r#" "old school" "#, Token::String { .. });
        assert_token!(r#" " padded " "#, Token::String { .. });
        assert_token!(r#" "Hello, 世界" "#, Token::String { .. });
        assert_token!(r#" "👌🚀" "#, Token::String { .. });
        assert_token!(r#" "\n\r\t\"\\" "#, Token::String { .. });
    }

    #[test]
    fn it_errors_as_expected() {
        let mut lexer = Lexer::new("++");
        assert!(matches!(lexer.next(), Some(Err(Error::InvalidToken(_)))));
    }
}
<|endoftext|>


<commit_before>extern crate chrono;

use std::fmt;

use chrono::NaiveTime;

pub struct Timestamp {
    timestamp: NaiveTime,
    is_positive: bool,
}

impl Timestamp {
    pub fn new() -> Timestamp {
        Timestamp {
            timestamp: NaiveTime::from_num_seconds_from_midnight(0, 0),
            is_positive: true,
        }
    }

    pub fn from_sec_time_factor(sec: i64, time_factor: f64) -> Timestamp {
        println!("from_sec_time_factor: {}, {}", sec, time_factor);
        let sec_f = sec.abs() as f64 * time_factor;
        Timestamp {
            timestamp: NaiveTime::from_num_seconds_from_midnight(
                sec_f.trunc() as u32,
                (sec_f.fract() * 1_000_000f64) as u32
            ),
            is_positive: if sec >= 0 { true } else { false },
        }
    }
}

impl fmt::Display for Timestamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}{}",
            if self.is_positive { "".to_owned() } else  { "-".to_owned() },
            self.timestamp.format("%H:%M:%S%.3f").to_string()
        )
    }
}
<commit_msg>Shorten timestamp display Display hours only if timestamp is larger than an hour<commit_after>extern crate chrono;

use std::fmt;

use chrono::{NaiveTime, Timelike};

pub struct Timestamp {
    timestamp: NaiveTime,
    is_positive: bool,
}

impl Timestamp {
    pub fn new() -> Timestamp {
        Timestamp {
            timestamp: NaiveTime::from_num_seconds_from_midnight(0, 0),
            is_positive: true,
        }
    }

    pub fn from_sec_time_factor(sec: i64, time_factor: f64) -> Timestamp {
        println!("from_sec_time_factor: {}, {}", sec, time_factor);
        let sec_f = sec.abs() as f64 * time_factor;
        Timestamp {
            timestamp: NaiveTime::from_num_seconds_from_midnight(
                sec_f.trunc() as u32,
                (sec_f.fract() * 1_000_000f64) as u32
            ),
            is_positive: if sec >= 0 { true } else { false },
        }
    }
}

impl fmt::Display for Timestamp {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut format = String::new();
        if self.timestamp.hour() > 0 {
            format = "%H:".to_owned();
        }
        format += "%M:%S%.3f";

        write!(f, "{}{}",
            if self.is_positive { "".to_owned() } else  { "-".to_owned() },
            self.timestamp.format(&format).to_string()
        )
    }
}
<|endoftext|>


<commit_before>use nom::{
  be_u8, be_u16, be_u32, be_u64,
  le_u32,
  IResult,
  ErrorCode, Err,
  Needed,
};

use std::str::from_utf8;

use metadata::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

use utility::to_u32;

named!(stream_info <&[u8], BlockData>,
  chain!(
    min_block_size: be_u16 ~
    max_block_size: be_u16 ~
    min_frame_size: map!(take!(3), to_u32) ~
    max_frame_size: map!(take!(3), to_u32) ~
    bytes: take!(8) ~
    md5_sum: take!(16),
    || {
      let sample_rate     = ((bytes[0] as u32) << 12) +
                            ((bytes[1] as u32) << 4)  +
                            (bytes[2] as u32) >> 4;
      let channels        = (bytes[2] >> 1) & 0b0111;
      let bits_per_sample = ((bytes[2] & 0b01) << 4) +
                            bytes[3] >> 4;
      let total_samples   = (((bytes[3] as u64) & 0x0f) << 32) +
                            ((bytes[4] as u64) << 24) +
                            ((bytes[5] as u64) << 16) +
                            ((bytes[6] as u64) << 8) +
                            (bytes[7] as u64);

      BlockData::StreamInfo(StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: min_frame_size,
        max_frame_size: max_frame_size,
        sample_rate: sample_rate,
        channels: channels + 1,
        bits_per_sample: bits_per_sample + 1,
        total_samples: total_samples,
        md5_sum: md5_sum,
      })
    }
  )
);

fn padding(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), |_| BlockData::Padding(0))
}

fn application(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  chain!(input,
    id: take_str!(4) ~
    data: take!(length - 4),
    || {
      BlockData::Application(Application {
        id: id,
        data: data,
      })
    }
  )
}

named!(seek_point <&[u8], SeekPoint>,
  chain!(
    sample_number: be_u64 ~
    stream_offset: be_u64 ~
    frame_samples: be_u16,
    || {
      SeekPoint {
        sample_number: sample_number,
        stream_offset: stream_offset,
        frame_samples: frame_samples,
      }
    }
  )
);

fn seek_table(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  let seek_count = (length / 18) as usize;

  map!(input, count!(seek_point, seek_count), BlockData::SeekTable)
}

named!(vorbis_comment <&[u8], BlockData>,
  chain!(
    vendor_string_length: le_u32 ~
    vendor_string: take_str!(vendor_string_length)  ~
    number_of_comments: le_u32 ~
    comments: count!(comment_field, number_of_comments as usize),
    || {
      BlockData::VorbisComment(VorbisComment {
        vendor_string: vendor_string,
        comments: comments,
      })
    }
  )
);

named!(comment_field <&[u8], &str>,
  chain!(
    comment_length: le_u32 ~
    comment: take_str!(comment_length),
    || { comment }
  )
);

named!(cue_sheet <&[u8], BlockData>,
  chain!(
    media_catalog_number: take_str!(128) ~
    lead_in: be_u64 ~
    bytes: take!(259) ~ // TODO: last (7 + 258 * 8) bits must be 0
    num_tracks: be_u8 ~
    tracks: count!(cue_sheet_track, num_tracks as usize),
    || {
      let is_cd = ((bytes[0] >> 7) & 0b01) == 1;

      BlockData::CueSheet(CueSheet {
        media_catalog_number: media_catalog_number,
        lead_in: lead_in,
        is_cd: is_cd,
        tracks: tracks,
      })
    }
  )
);

named!(cue_sheet_track <&[u8], CueSheetTrack>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    isrc: take_str!(12) ~
    bytes: take!(14) ~ // TODO: last (6 + 13 * 8) bits must be 0
    num_indices: be_u8 ~
    indices: count!(cue_sheet_track_index, num_indices as usize),
    || {
      let isnt_audio      = ((bytes[0] >> 7) & 0b01) == 1;
      let is_pre_emphasis = ((bytes[0] >> 6) & 0b01) == 1;

      CueSheetTrack {
        offset: offset,
        number: number,
        isrc: isrc,
        isnt_audio: isnt_audio,
        is_pre_emphasis: is_pre_emphasis,
        indices: indices,
      }
    }
  )
);

named!(cue_sheet_track_index <&[u8], CueSheetTrackIndex>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    take!(3), // TODO: these bytes must be 0
    || {
      CueSheetTrackIndex {
        offset: offset,
        number: number,
      }
    }
  )
);

named!(picture <&[u8], BlockData>,
  chain!(
    picture_type_num: be_u32 ~
    mime_type_length:  be_u32 ~
    mime_type: take_str!(mime_type_length) ~
    description_length: be_u32 ~
    description: take_str!(description_length) ~
    width: be_u32 ~
    height: be_u32 ~
    depth: be_u32 ~
    colors: be_u32 ~
    data_length: be_u32 ~
    data: take!(data_length),
    || {
      let picture_type = match picture_type_num {
        0  => PictureType::Other,
        1  => PictureType::FileIconStandard,
        2  => PictureType::FileIcon,
        3  => PictureType::FrontCover,
        4  => PictureType::BackCover,
        5  => PictureType::LeafletPage,
        6  => PictureType::Media,
        7  => PictureType::LeadArtist,
        8  => PictureType::Artist,
        9  => PictureType::Conductor,
        10 => PictureType::Band,
        11 => PictureType::Composer,
        12 => PictureType::Lyricist,
        13 => PictureType::RecordingLocation,
        14 => PictureType::DuringRecording,
        15 => PictureType::DuringPerformace,
        16 => PictureType::VideoScreenCapture,
        17 => PictureType::Fish,
        18 => PictureType::Illustration,
        19 => PictureType::BandLogoType,
        20 => PictureType::PublisherLogoType,
        _  => PictureType::Other,
      };

      BlockData::Picture(Picture {
        picture_type: picture_type,
        mime_type: mime_type,
        description: description,
        width: width,
        height: height,
        depth: depth,
        colors: colors,
        data: data,
      })
    }
  )
);

fn unknown(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), BlockData::Unknown)
}

named!(header <&[u8], (u8, bool, u32)>,
  chain!(
    block_byte: be_u8 ~
    length: map!(take!(3), to_u32),
    || {
      let is_last    = (block_byte >> 7) == 1;
      let block_type = block_byte & 0b01111111;

      (block_type, is_last, length)
    }
  )
);

fn block_data(input: &[u8], block_type: u8, length: u32)
              -> IResult<&[u8], BlockData> {
  match block_type {
    0       => stream_info(input),
    1       => padding(input, length),
    2       => application(input, length),
    3       => seek_table(input, length),
    4       => vorbis_comment(input),
    5       => cue_sheet(input),
    6       => picture(input),
    7...126 => unknown(input, length),
    _       => IResult::Error(Err::Position(ErrorCode::Alt as u32, input)),
  }
}

named!(block <&[u8], Block>,
  chain!(
    block_header: header ~
    data: apply!(block_data, block_header.0, block_header.2),
    || {
      Block {
        is_last: block_header.1,
        length: block_header.2,
        data: data
      }
    }
  )
);

pub fn many_blocks(input: &[u8]) -> IResult<&[u8], Vec<Block>> {
  let mut is_last   = false;
  let mut blocks    = Vec::new();
  let mut start     = 0;
  let mut remaining = input.len();

  while !is_last {
    match block(&input[start..]) {
      IResult::Done(i, block) => {
        let result_len = i.len();

        if result_len == input[start..].len() {
          break;
        }

        start    += remaining - result_len;
        remaining = result_len;
        is_last   = block.is_last;

        blocks.push(block);
      }
      _                       => break,
    }
  }

  if blocks.len() == 0 {
    IResult::Error(Err::Position(ErrorCode::Many1 as u32, input))
  } else if is_last {
    IResult::Done(&input[start..], blocks)
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}
<commit_msg>Add documentation about a few parsers<commit_after>use nom::{
  be_u8, be_u16, be_u32, be_u64,
  le_u32,
  IResult,
  ErrorCode, Err,
  Needed,
};

use std::str::from_utf8;

use metadata::{
  Block, BlockData,
  StreamInfo, Application, VorbisComment, CueSheet, Picture,
  SeekPoint, CueSheetTrack, CueSheetTrackIndex, PictureType,
};

use utility::to_u32;

named!(stream_info <&[u8], BlockData>,
  chain!(
    min_block_size: be_u16 ~
    max_block_size: be_u16 ~
    min_frame_size: map!(take!(3), to_u32) ~
    max_frame_size: map!(take!(3), to_u32) ~
    bytes: take!(8) ~
    md5_sum: take!(16),
    || {
      let sample_rate     = ((bytes[0] as u32) << 12) +
                            ((bytes[1] as u32) << 4)  +
                            (bytes[2] as u32) >> 4;
      let channels        = (bytes[2] >> 1) & 0b0111;
      let bits_per_sample = ((bytes[2] & 0b01) << 4) +
                            bytes[3] >> 4;
      let total_samples   = (((bytes[3] as u64) & 0x0f) << 32) +
                            ((bytes[4] as u64) << 24) +
                            ((bytes[5] as u64) << 16) +
                            ((bytes[6] as u64) << 8) +
                            (bytes[7] as u64);

      BlockData::StreamInfo(StreamInfo {
        min_block_size: min_block_size,
        max_block_size: max_block_size,
        min_frame_size: min_frame_size,
        max_frame_size: max_frame_size,
        sample_rate: sample_rate,
        channels: channels + 1,
        bits_per_sample: bits_per_sample + 1,
        total_samples: total_samples,
        md5_sum: md5_sum,
      })
    }
  )
);

fn padding(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), |_| BlockData::Padding(0))
}

fn application(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  chain!(input,
    id: take_str!(4) ~
    data: take!(length - 4),
    || {
      BlockData::Application(Application {
        id: id,
        data: data,
      })
    }
  )
}

named!(seek_point <&[u8], SeekPoint>,
  chain!(
    sample_number: be_u64 ~
    stream_offset: be_u64 ~
    frame_samples: be_u16,
    || {
      SeekPoint {
        sample_number: sample_number,
        stream_offset: stream_offset,
        frame_samples: frame_samples,
      }
    }
  )
);

fn seek_table(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  let seek_count = (length / 18) as usize;

  map!(input, count!(seek_point, seek_count), BlockData::SeekTable)
}

named!(vorbis_comment <&[u8], BlockData>,
  chain!(
    vendor_string_length: le_u32 ~
    vendor_string: take_str!(vendor_string_length)  ~
    number_of_comments: le_u32 ~
    comments: count!(comment_field, number_of_comments as usize),
    || {
      BlockData::VorbisComment(VorbisComment {
        vendor_string: vendor_string,
        comments: comments,
      })
    }
  )
);

named!(comment_field <&[u8], &str>,
  chain!(
    comment_length: le_u32 ~
    comment: take_str!(comment_length),
    || { comment }
  )
);

named!(cue_sheet <&[u8], BlockData>,
  chain!(
    media_catalog_number: take_str!(128) ~
    lead_in: be_u64 ~
    bytes: take!(259) ~ // TODO: last (7 + 258 * 8) bits must be 0
    num_tracks: be_u8 ~
    tracks: count!(cue_sheet_track, num_tracks as usize),
    || {
      let is_cd = ((bytes[0] >> 7) & 0b01) == 1;

      BlockData::CueSheet(CueSheet {
        media_catalog_number: media_catalog_number,
        lead_in: lead_in,
        is_cd: is_cd,
        tracks: tracks,
      })
    }
  )
);

named!(cue_sheet_track <&[u8], CueSheetTrack>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    isrc: take_str!(12) ~
    bytes: take!(14) ~ // TODO: last (6 + 13 * 8) bits must be 0
    num_indices: be_u8 ~
    indices: count!(cue_sheet_track_index, num_indices as usize),
    || {
      let isnt_audio      = ((bytes[0] >> 7) & 0b01) == 1;
      let is_pre_emphasis = ((bytes[0] >> 6) & 0b01) == 1;

      CueSheetTrack {
        offset: offset,
        number: number,
        isrc: isrc,
        isnt_audio: isnt_audio,
        is_pre_emphasis: is_pre_emphasis,
        indices: indices,
      }
    }
  )
);

named!(cue_sheet_track_index <&[u8], CueSheetTrackIndex>,
  chain!(
    offset: be_u64 ~
    number: be_u8 ~
    take!(3), // TODO: these bytes must be 0
    || {
      CueSheetTrackIndex {
        offset: offset,
        number: number,
      }
    }
  )
);

named!(picture <&[u8], BlockData>,
  chain!(
    picture_type_num: be_u32 ~
    mime_type_length:  be_u32 ~
    mime_type: take_str!(mime_type_length) ~
    description_length: be_u32 ~
    description: take_str!(description_length) ~
    width: be_u32 ~
    height: be_u32 ~
    depth: be_u32 ~
    colors: be_u32 ~
    data_length: be_u32 ~
    data: take!(data_length),
    || {
      let picture_type = match picture_type_num {
        0  => PictureType::Other,
        1  => PictureType::FileIconStandard,
        2  => PictureType::FileIcon,
        3  => PictureType::FrontCover,
        4  => PictureType::BackCover,
        5  => PictureType::LeafletPage,
        6  => PictureType::Media,
        7  => PictureType::LeadArtist,
        8  => PictureType::Artist,
        9  => PictureType::Conductor,
        10 => PictureType::Band,
        11 => PictureType::Composer,
        12 => PictureType::Lyricist,
        13 => PictureType::RecordingLocation,
        14 => PictureType::DuringRecording,
        15 => PictureType::DuringPerformace,
        16 => PictureType::VideoScreenCapture,
        17 => PictureType::Fish,
        18 => PictureType::Illustration,
        19 => PictureType::BandLogoType,
        20 => PictureType::PublisherLogoType,
        _  => PictureType::Other,
      };

      BlockData::Picture(Picture {
        picture_type: picture_type,
        mime_type: mime_type,
        description: description,
        width: width,
        height: height,
        depth: depth,
        colors: colors,
        data: data,
      })
    }
  )
);

// As of FLAC v1.3.1, there is support for up to 127 different metadata
// `Block`s but actually 7 that are implemented. When the `Block` type isn't
// recognised, this block gets skipped over with this parser.
fn unknown(input: &[u8], length: u32) -> IResult<&[u8], BlockData> {
  map!(input, take!(length), BlockData::Unknown)
}

named!(header <&[u8], (u8, bool, u32)>,
  chain!(
    block_byte: be_u8 ~
    length: map!(take!(3), to_u32),
    || {
      let is_last    = (block_byte >> 7) == 1;
      let block_type = block_byte & 0b01111111;

      (block_type, is_last, length)
    }
  )
);

fn block_data(input: &[u8], block_type: u8, length: u32)
              -> IResult<&[u8], BlockData> {
  match block_type {
    0       => stream_info(input),
    1       => padding(input, length),
    2       => application(input, length),
    3       => seek_table(input, length),
    4       => vorbis_comment(input),
    5       => cue_sheet(input),
    6       => picture(input),
    7...126 => unknown(input, length),
    _       => IResult::Error(Err::Position(ErrorCode::Alt as u32, input)),
  }
}

named!(block <&[u8], Block>,
  chain!(
    block_header: header ~
    data: apply!(block_data, block_header.0, block_header.2),
    || {
      Block {
        is_last: block_header.1,
        length: block_header.2,
        data: data
      }
    }
  )
);

/// Parses one or more metadata `Block`s
///
/// The first block should always be `StreamInfo` since that is the only
/// required `Block`. At the moment `many_blocks` doesn't check that
/// requirement.
pub fn many_blocks(input: &[u8]) -> IResult<&[u8], Vec<Block>> {
  let mut is_last   = false;
  let mut blocks    = Vec::new();
  let mut start     = 0;
  let mut remaining = input.len();

  while !is_last {
    match block(&input[start..]) {
      IResult::Done(i, block) => {
        let result_len = i.len();

        if result_len == input[start..].len() {
          break;
        }

        start    += remaining - result_len;
        remaining = result_len;
        is_last   = block.is_last;

        blocks.push(block);
      }
      _                       => break,
    }
  }

  if blocks.len() == 0 {
    IResult::Error(Err::Position(ErrorCode::Many1 as u32, input))
  } else if is_last {
    IResult::Done(&input[start..], blocks)
  } else {
    IResult::Incomplete(Needed::Unknown)
  }
}
<|endoftext|>


<commit_before>use std::str::Chars;

/// Peekable iterator over a char sequence.
///
/// Next characters can be peeked via `nth_char` method,
/// and position can be shifted forward via `bump` method.
pub(crate) struct Cursor<'a> {
    initial_len: usize,
    chars: Chars<'a>,
    #[cfg(debug_assertions)]
    prev: char,
}

pub(crate) const EOF_CHAR: char = '\0';

impl<'a> Cursor<'a> {
    pub(crate) fn new(input: &'a str) -> Cursor<'a> {
        Cursor {
            initial_len: input.len(),
            chars: input.chars(),
            #[cfg(debug_assertions)]
            prev: EOF_CHAR,
        }
    }

    /// Returns the last eaten symbol (or `'\0'` in release builds).
    /// (For debug assertions only.)
    pub(crate) fn prev(&self) -> char {
        #[cfg(debug_assertions)]
        {
            self.prev
        }

        #[cfg(not(debug_assertions))]
        {
            '\0'
        }
    }

    /// Returns nth character relative to the current cursor position.
    /// If requested position doesn't exist, `EOF_CHAR` is returned.
    /// However, getting `EOF_CHAR` doesn't always mean actual end of file,
    /// it should be checked with `is_eof` method.
    fn nth_char(&self, n: usize) -> char {
        self.chars().nth(n).unwrap_or(EOF_CHAR)
    }

    /// Peeks the next symbol from the input stream without consuming it.
    pub(crate) fn first(&self) -> char {
        self.nth_char(0)
    }

    /// Peeks the second symbol from the input stream without consuming it.
    pub(crate) fn second(&self) -> char {
        self.nth_char(1)
    }

    /// Checks if there is nothing more to consume.
    pub(crate) fn is_eof(&self) -> bool {
        self.chars.as_str().is_empty()
    }

    /// Returns amount of already consumed symbols.
    pub(crate) fn len_consumed(&self) -> usize {
        self.initial_len - self.chars.as_str().len()
    }

    /// Returns a `Chars` iterator over the remaining characters.
    fn chars(&self) -> Chars<'a> {
        self.chars.clone()
    }

    /// Moves to the next character.
    pub(crate) fn bump(&mut self) -> Option<char> {
        let c = self.chars.next()?;

        #[cfg(debug_assertions)]
        {
            self.prev = c;
        }

        Some(c)
    }
}
<commit_msg>Rollup merge of #80780 - lianghanzhen:master, r=petrochenkov<commit_after>use std::str::Chars;

/// Peekable iterator over a char sequence.
///
/// Next characters can be peeked via `nth_char` method,
/// and position can be shifted forward via `bump` method.
pub(crate) struct Cursor<'a> {
    initial_len: usize,
    chars: Chars<'a>,
    #[cfg(debug_assertions)]
    prev: char,
}

pub(crate) const EOF_CHAR: char = '\0';

impl<'a> Cursor<'a> {
    pub(crate) fn new(input: &'a str) -> Cursor<'a> {
        Cursor {
            initial_len: input.len(),
            chars: input.chars(),
            #[cfg(debug_assertions)]
            prev: EOF_CHAR,
        }
    }

    /// Returns the last eaten symbol (or `'\0'` in release builds).
    /// (For debug assertions only.)
    pub(crate) fn prev(&self) -> char {
        #[cfg(debug_assertions)]
        {
            self.prev
        }

        #[cfg(not(debug_assertions))]
        {
            EOF_CHAR
        }
    }

    /// Returns nth character relative to the current cursor position.
    /// If requested position doesn't exist, `EOF_CHAR` is returned.
    /// However, getting `EOF_CHAR` doesn't always mean actual end of file,
    /// it should be checked with `is_eof` method.
    fn nth_char(&self, n: usize) -> char {
        self.chars().nth(n).unwrap_or(EOF_CHAR)
    }

    /// Peeks the next symbol from the input stream without consuming it.
    pub(crate) fn first(&self) -> char {
        self.nth_char(0)
    }

    /// Peeks the second symbol from the input stream without consuming it.
    pub(crate) fn second(&self) -> char {
        self.nth_char(1)
    }

    /// Checks if there is nothing more to consume.
    pub(crate) fn is_eof(&self) -> bool {
        self.chars.as_str().is_empty()
    }

    /// Returns amount of already consumed symbols.
    pub(crate) fn len_consumed(&self) -> usize {
        self.initial_len - self.chars.as_str().len()
    }

    /// Returns a `Chars` iterator over the remaining characters.
    fn chars(&self) -> Chars<'a> {
        self.chars.clone()
    }

    /// Moves to the next character.
    pub(crate) fn bump(&mut self) -> Option<char> {
        let c = self.chars.next()?;

        #[cfg(debug_assertions)]
        {
            self.prev = c;
        }

        Some(c)
    }
}
<|endoftext|>


<commit_before>extern crate glutin_window;
extern crate piston;
extern crate graphics;
extern crate gfx_graphics;
extern crate find_folder;
extern crate gfx;
extern crate gfx_device_gl;

use gfx::traits::*;
use gfx::format::{DepthStencil, Formatted, Srgba8};
use gfx::memory::Typed;
use glutin_window::{GlutinWindow, OpenGL};
use piston::window::{OpenGLWindow, Window, WindowSettings};
use piston::event_loop::{Events, EventSettings, EventLoop};
use graphics::draw_state::Blend;
use graphics::*;
use piston::input::*;
use gfx_graphics::{Flip, Gfx2d, Texture, TextureSettings, TextureContext};

fn main() {
    println!("Press A to change blending");
    println!("Press S to change clip inside/out");

    let opengl = OpenGL::V3_2;
    let samples = 4;
    let mut window: GlutinWindow = WindowSettings::new(
            "piston: draw_state",
            [600, 600]
        )
        .exit_on_esc(true)
        .samples(samples)
        .graphics_api(opengl)
        .build()
        .unwrap();

    let (mut device, mut factory) = gfx_device_gl::create(|s|
        window.get_proc_address(s) as *const std::os::raw::c_void);

    // Create the main color/depth targets.
    let draw_size = window.draw_size();
    let aa = samples as gfx::texture::NumSamples;
    let dim = (draw_size.width as u16, draw_size.height as u16, 1, aa.into());
    let color_format = <Srgba8 as Formatted>::get_format();
    let depth_format = <DepthStencil as Formatted>::get_format();
    let (output_color, output_stencil) =
        gfx_device_gl::create_main_targets_raw(dim,
                                               color_format.0,
                                               depth_format.0);
    let output_color = Typed::new(output_color);
    let output_stencil = Typed::new(output_stencil);

    let assets = find_folder::Search::ParentsThenKids(3, 3)
        .for_folder("assets").unwrap();
    let blends = [Blend::Alpha, Blend::Add, Blend::Invert, Blend::Multiply, Blend::Screen];
    let mut blend = 0;
    let mut clip_inside = true;
    let mut texture_context = TextureContext {
        factory: factory.clone(),
        encoder: factory.create_command_buffer().into(),
    };
    let rust_logo = Texture::from_path(&mut texture_context,
                                       assets.join("rust.png"),
                                       Flip::None,
                                       &TextureSettings::new()).unwrap();

    let mut encoder = factory.create_command_buffer().into();
    let mut g2d = Gfx2d::new(opengl, &mut factory);
    let mut events = Events::new(EventSettings::new().lazy(true));
    while let Some(e) = events.next(&mut window) {
        if let Some(args) = e.render_args() {
            g2d.draw(&mut encoder, &output_color, &output_stencil, args.viewport(), |c, g| {
                clear([0.8, 0.8, 0.8, 1.0], g);
                Rectangle::new([1.0, 0.0, 0.0, 1.0])
                    .draw([0.0, 0.0, 100.0, 100.0], &c.draw_state, c.transform, g);

                let draw_state = c.draw_state.blend(blends[blend]);
                Rectangle::new([0.5, 1.0, 0.0, 0.3])
                    .draw([50.0, 50.0, 100.0, 100.0], &draw_state, c.transform, g);

                let transform = c.transform.trans(100.0, 100.0);
                // Clip rectangle from upper left corner.
                let clipped = c.draw_state.scissor([100, 100, 100, 100]);
                Image::new().draw(&rust_logo, &clipped, transform, g);

                let transform = c.transform.trans(200.0, 200.0);
                Ellipse::new([1.0, 0.0, 0.0, 1.0])
                    .draw([0.0, 0.0, 50.0, 50.0], &DrawState::new_clip(), transform, g);
                Image::new().draw(&rust_logo,
                    &if clip_inside { DrawState::new_inside() }
                    else { DrawState::new_outside() },
                    transform, g);
            });
            encoder.flush(&mut device);
        }

        if let Some(_) = e.after_render_args() {
            device.cleanup();
        }

        if let Some(Button::Keyboard(Key::A)) = e.press_args() {
            blend = (blend + 1) % blends.len();
            println!("Changed blending to {:?}", blends[blend]);
        }

        if let Some(Button::Keyboard(Key::S)) = e.press_args() {
            clip_inside = !clip_inside;
            if clip_inside {
                println!("Changed to clip inside");
            } else {
                println!("Changed to clip outside");
            }
        }
    }
}
<commit_msg>Fix examples/draw_state.rs<commit_after>extern crate glutin_window;
extern crate piston;
extern crate graphics;
extern crate gfx_graphics;
extern crate find_folder;
extern crate gfx;
extern crate gfx_device_gl;

use gfx::traits::*;
use gfx::format::{DepthStencil, Formatted, Srgba8};
use gfx::memory::Typed;
use glutin_window::{GlutinWindow, OpenGL};
use piston::window::{OpenGLWindow, Window, WindowSettings};
use piston::event_loop::{Events, EventSettings, EventLoop};
use graphics::draw_state::Blend;
use graphics::*;
use piston::input::*;
use gfx_graphics::{Flip, Gfx2d, Texture, TextureSettings, TextureContext};

fn main() {
    println!("Press A to change blending");
    println!("Press S to change clip inside/out");

    let opengl = OpenGL::V3_2;
    let samples = 4;
    let mut window: GlutinWindow = WindowSettings::new(
            "piston: draw_state",
            [600, 600]
        )
        .exit_on_esc(true)
        .samples(samples)
        .graphics_api(opengl)
        .build()
        .unwrap();

    let (mut device, mut factory) = gfx_device_gl::create(|s|
        window.get_proc_address(s) as *const std::os::raw::c_void);

    // Create the main color/depth targets.
    let draw_size = window.draw_size();
    let aa = samples as gfx::texture::NumSamples;
    let dim = (draw_size.width as u16, draw_size.height as u16, 1, aa.into());
    let color_format = <Srgba8 as Formatted>::get_format();
    let depth_format = <DepthStencil as Formatted>::get_format();
    let (output_color, output_stencil) =
        gfx_device_gl::create_main_targets_raw(dim,
                                               color_format.0,
                                               depth_format.0);
    let output_color = Typed::new(output_color);
    let output_stencil = Typed::new(output_stencil);

    let assets = find_folder::Search::ParentsThenKids(3, 3)
        .for_folder("assets").unwrap();
    let blends = [Blend::Alpha, Blend::Add, Blend::Invert, Blend::Multiply, Blend::Lighter];
    let mut blend = 0;
    let mut clip_inside = true;
    let mut texture_context = TextureContext {
        factory: factory.clone(),
        encoder: factory.create_command_buffer().into(),
    };
    let rust_logo = Texture::from_path(&mut texture_context,
                                       assets.join("rust.png"),
                                       Flip::None,
                                       &TextureSettings::new()).unwrap();

    let mut encoder = factory.create_command_buffer().into();
    let mut g2d = Gfx2d::new(opengl, &mut factory);
    let mut events = Events::new(EventSettings::new().lazy(true));
    while let Some(e) = events.next(&mut window) {
        if let Some(args) = e.render_args() {
            g2d.draw(&mut encoder, &output_color, &output_stencil, args.viewport(), |c, g| {
                clear([0.8, 0.8, 0.8, 1.0], g);
                Rectangle::new([1.0, 0.0, 0.0, 1.0])
                    .draw([0.0, 0.0, 100.0, 100.0], &c.draw_state, c.transform, g);

                let draw_state = c.draw_state.blend(blends[blend]);
                Rectangle::new([0.5, 1.0, 0.0, 0.3])
                    .draw([50.0, 50.0, 100.0, 100.0], &draw_state, c.transform, g);

                let transform = c.transform.trans(100.0, 100.0);
                // Clip rectangle from upper left corner.
                let clipped = c.draw_state.scissor([100, 100, 100, 100]);
                Image::new().draw(&rust_logo, &clipped, transform, g);

                let transform = c.transform.trans(200.0, 200.0);
                Ellipse::new([1.0, 0.0, 0.0, 1.0])
                    .draw([0.0, 0.0, 50.0, 50.0], &DrawState::new_clip(), transform, g);
                Image::new().draw(&rust_logo,
                    &if clip_inside { DrawState::new_inside() }
                    else { DrawState::new_outside() },
                    transform, g);
            });
            encoder.flush(&mut device);
        }

        if let Some(_) = e.after_render_args() {
            device.cleanup();
        }

        if let Some(Button::Keyboard(Key::A)) = e.press_args() {
            blend = (blend + 1) % blends.len();
            println!("Changed blending to {:?}", blends[blend]);
        }

        if let Some(Button::Keyboard(Key::S)) = e.press_args() {
            clip_inside = !clip_inside;
            if clip_inside {
                println!("Changed to clip inside");
            } else {
                println!("Changed to clip outside");
            }
        }
    }
}
<|endoftext|>


<commit_before>//! Example that just prints out all the input events.

extern crate ggez;

use ggez::conf;
use ggez::event::{self, Axis, Button, Keycode, Mod, MouseButton, MouseState};
use ggez::graphics::{self, DrawMode, Point2};
use ggez::{Context, GameResult};
use std::env;
use std::path;

struct MainState {
    pos_x: i32,
    pos_y: i32,
    mouse_down: bool,
}

impl MainState {
    fn new() -> MainState {
        MainState {
            pos_x: 100,
            pos_y: 100,
            mouse_down: false,
        }
    }
}

impl event::EventHandler for MainState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx);
        graphics::circle(
            ctx,
            DrawMode::Fill,
            Point2::new(self.pos_x as f32, self.pos_y as f32),
            100.0,
            1.0,
        )?;
        graphics::present(ctx);
        Ok(())
    }

    fn mouse_button_down_event(&mut self, _ctx: &mut Context, button: MouseButton, x: i32, y: i32) {
        self.mouse_down = true;
        println!("Mouse button pressed: {:?}, x: {}, y: {}", button, x, y);
    }

    fn mouse_button_up_event(&mut self, _ctx: &mut Context, button: MouseButton, x: i32, y: i32) {
        self.mouse_down = false;
        println!("Mouse button released: {:?}, x: {}, y: {}", button, x, y);
    }

    fn mouse_motion_event(
        &mut self,
        _ctx: &mut Context,
        _state: MouseState,
        x: i32,
        y: i32,
        xrel: i32,
        yrel: i32,
    ) {
        if self.mouse_down {
            self.pos_x = x;
            self.pos_y = y;
        }
        println!(
            "Mouse motion, x: {}, y: {}, relative x: {}, relative y: {}",
            x, y, xrel, yrel
        );
    }

    fn mouse_wheel_event(&mut self, _ctx: &mut Context, x: i32, y: i32) {
        println!("Mousewheel event, x: {}, y: {}", x, y);
    }

    fn key_down_event(&mut self, _ctx: &mut Context, keycode: Keycode, keymod: Mod, repeat: bool) {
        println!(
            "Key pressed: {:?}, modifier {:?}, repeat: {}",
            keycode, keymod, repeat
        );
    }
    fn key_up_event(&mut self, _ctx: &mut Context, keycode: Keycode, keymod: Mod, repeat: bool) {
        println!(
            "Key released: {:?}, modifier {:?}, repeat: {}",
            keycode, keymod, repeat
        );
    }

    fn text_editing_event(&mut self, _ctx: &mut Context, text: String, start: i32, length: i32) {
        println!(
            "Text editing: {}, start {}, length: {}",
            text, start, length
        );
    }

    fn text_input_event(&mut self, _ctx: &mut Context, text: String) {
        println!("Text input: {}", text);
    }

    fn controller_button_down_event(&mut self, _ctx: &mut Context, btn: Button, instance_id: i32) {
        println!(
            "Controller button pressed: {:?} Controller_Id: {}",
            btn, instance_id
        );
    }

    fn controller_button_up_event(&mut self, _ctx: &mut Context, btn: Button, instance_id: i32) {
        println!(
            "Controller button released: {:?} Controller_Id: {}",
            btn, instance_id
        );
    }

    fn controller_axis_event(
        &mut self,
        _ctx: &mut Context,
        axis: Axis,
        value: i16,
        instance_id: i32,
    ) {
        println!(
            "Axis Event: {:?} Value: {} Controller_Id: {}",
            axis, value, instance_id
        );
    }

    fn focus_event(&mut self, _ctx: &mut Context, gained: bool) {
        if gained {
            println!("Focus gained");
        } else {
            println!("Focus lost");
        }
    }
}

pub fn main() {
    let c = conf::Conf::new();
    let ctx = &mut Context::load_from_conf("input_test", "ggez", c).unwrap();

    // We add the CARGO_MANIFEST_DIR/resources do the filesystems paths so
    // we we look in the cargo project for files.
    if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        ctx.filesystem.mount(&path, true);
    }

    let state = &mut MainState::new();
    event::run(ctx, state).unwrap();
}
<commit_msg>Fix typos in input_test.rs<commit_after>//! Example that just prints out all the input events.

extern crate ggez;

use ggez::conf;
use ggez::event::{self, Axis, Button, Keycode, Mod, MouseButton, MouseState};
use ggez::graphics::{self, DrawMode, Point2};
use ggez::{Context, GameResult};
use std::env;
use std::path;

struct MainState {
    pos_x: i32,
    pos_y: i32,
    mouse_down: bool,
}

impl MainState {
    fn new() -> MainState {
        MainState {
            pos_x: 100,
            pos_y: 100,
            mouse_down: false,
        }
    }
}

impl event::EventHandler for MainState {
    fn update(&mut self, _ctx: &mut Context) -> GameResult<()> {
        Ok(())
    }

    fn draw(&mut self, ctx: &mut Context) -> GameResult<()> {
        graphics::clear(ctx);
        graphics::circle(
            ctx,
            DrawMode::Fill,
            Point2::new(self.pos_x as f32, self.pos_y as f32),
            100.0,
            1.0,
        )?;
        graphics::present(ctx);
        Ok(())
    }

    fn mouse_button_down_event(&mut self, _ctx: &mut Context, button: MouseButton, x: i32, y: i32) {
        self.mouse_down = true;
        println!("Mouse button pressed: {:?}, x: {}, y: {}", button, x, y);
    }

    fn mouse_button_up_event(&mut self, _ctx: &mut Context, button: MouseButton, x: i32, y: i32) {
        self.mouse_down = false;
        println!("Mouse button released: {:?}, x: {}, y: {}", button, x, y);
    }

    fn mouse_motion_event(
        &mut self,
        _ctx: &mut Context,
        _state: MouseState,
        x: i32,
        y: i32,
        xrel: i32,
        yrel: i32,
    ) {
        if self.mouse_down {
            self.pos_x = x;
            self.pos_y = y;
        }
        println!(
            "Mouse motion, x: {}, y: {}, relative x: {}, relative y: {}",
            x, y, xrel, yrel
        );
    }

    fn mouse_wheel_event(&mut self, _ctx: &mut Context, x: i32, y: i32) {
        println!("Mousewheel event, x: {}, y: {}", x, y);
    }

    fn key_down_event(&mut self, _ctx: &mut Context, keycode: Keycode, keymod: Mod, repeat: bool) {
        println!(
            "Key pressed: {:?}, modifier {:?}, repeat: {}",
            keycode, keymod, repeat
        );
    }
    fn key_up_event(&mut self, _ctx: &mut Context, keycode: Keycode, keymod: Mod, repeat: bool) {
        println!(
            "Key released: {:?}, modifier {:?}, repeat: {}",
            keycode, keymod, repeat
        );
    }

    fn text_editing_event(&mut self, _ctx: &mut Context, text: String, start: i32, length: i32) {
        println!(
            "Text editing: {}, start {}, length: {}",
            text, start, length
        );
    }

    fn text_input_event(&mut self, _ctx: &mut Context, text: String) {
        println!("Text input: {}", text);
    }

    fn controller_button_down_event(&mut self, _ctx: &mut Context, btn: Button, instance_id: i32) {
        println!(
            "Controller button pressed: {:?} Controller_Id: {}",
            btn, instance_id
        );
    }

    fn controller_button_up_event(&mut self, _ctx: &mut Context, btn: Button, instance_id: i32) {
        println!(
            "Controller button released: {:?} Controller_Id: {}",
            btn, instance_id
        );
    }

    fn controller_axis_event(
        &mut self,
        _ctx: &mut Context,
        axis: Axis,
        value: i16,
        instance_id: i32,
    ) {
        println!(
            "Axis Event: {:?} Value: {} Controller_Id: {}",
            axis, value, instance_id
        );
    }

    fn focus_event(&mut self, _ctx: &mut Context, gained: bool) {
        if gained {
            println!("Focus gained");
        } else {
            println!("Focus lost");
        }
    }
}

pub fn main() {
    let c = conf::Conf::new();
    let ctx = &mut Context::load_from_conf("input_test", "ggez", c).unwrap();

    // We add the CARGO_MANIFEST_DIR/resources to the filesystem paths so
    // that ggez will look in our cargo project directory for files.
    if let Ok(manifest_dir) = env::var("CARGO_MANIFEST_DIR") {
        let mut path = path::PathBuf::from(manifest_dir);
        path.push("resources");
        ctx.filesystem.mount(&path, true);
    }

    let state = &mut MainState::new();
    event::run(ctx, state).unwrap();
}
<|endoftext|>


<commit_before>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Primitive traits representing basic 'kinds' of types

Rust types can be classified in various useful ways according to
intrinsic properties of the type. These classifications, often called
'kinds', are represented as traits.

They cannot be implemented by user code, but are instead implemented
by the compiler automatically for the types to which they apply.

*/

/// Types able to be transferred across task boundaries.
#[lang="send"]
pub trait Send {
    // empty.
}

/// Types with a constant size known at compile-time.
#[lang="sized"]
pub trait Sized {
    // Empty.
}

/// Types that can be copied by simply copying bits (i.e. `memcpy`).
#[lang="copy"]
pub trait Copy {
    // Empty.
}

/// Types that can be safely shared between tasks when aliased.
///
/// The precise definition is: a type `T` is `Share` if `&T` is
/// thread-safe. In other words, there is no possibility of data races
/// when passing `&T` references between tasks.
///
/// As one would expect, primitive types like `u8` and `f64` are all
/// `Share`, and so are simple aggregate types containing them (like
/// tuples, structs and enums). More instances of basic `Share` types
/// include "immutable" types like `&T` and those with simple
/// inherited mutability, such as `Box<T>`, `Vec<T>` and most other
/// collection types. (Generic parameters need to be `Share` for their
/// container to be `Share`.)
///
/// A somewhat surprising consequence of the definition is `&mut T` is
/// `Share` (if `T` is `Share`) even though it seems that it might
/// provide unsynchronised mutation. The trick is a mutable reference
/// stored in an aliasable reference (that is, `& &mut T`) becomes
/// read-only, as if it were a `& &T`, hence there is no risk of a data
/// race.
///
/// Types that are not `Share` are those that have "interior
/// mutability" in a non-thread-safe way, such as `Cell` and `RefCell`
/// in `std::cell`. These types allow for mutation of their contents
/// even when in an immutable, aliasable slot, e.g. the contents of
/// `&Cell<T>` can be `.set`, and do not ensure data races are
/// impossible, hence they cannot be `Share`. A higher level example
/// of a non-`Share` type is the reference counted pointer
/// `std::rc::Rc`, because any reference `&Rc<T>` can clone a new
/// reference, which modifies the reference counts in a non-atomic
/// way.
///
/// For cases when one does need thread-safe interior mutability,
/// types like the atomics in `std::sync` and `Mutex` & `RWLock` in
/// the `sync` crate do ensure that any mutation cannot cause data
/// races.  Hence these types are `Share`.
///
/// Users writing their own types with interior mutability (or anything
/// else that is not thread-safe) should use the `NoShare` marker type
/// (from `std::kinds::marker`) to ensure that the compiler doesn't
/// consider the user-defined type to be `Share`.  Any types with
/// interior mutability must also use the `std::cell::UnsafeCell` wrapper
/// around the value(s) which can be mutated when behind a `&`
/// reference; not doing this is undefined behaviour (for example,
/// `transmute`-ing from `&T` to `&mut T` is illegal).
#[lang="share"]
pub trait Share {
    // Empty
}

/// Marker types are special types that are used with unsafe code to
/// inform the compiler of special constraints. Marker types should
/// only be needed when you are creating an abstraction that is
/// implemented using unsafe code. In that case, you may want to embed
/// some of the marker types below into your type.
pub mod marker {

    /// A marker type whose type parameter `T` is considered to be
    /// covariant with respect to the type itself. This is (typically)
    /// used to indicate that an instance of the type `T` is being stored
    /// into memory and read from, even though that may not be apparent.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    ///
    /// *Note:* It is very unusual to have to add a covariant constraint.
    /// If you are not sure, you probably want to use `InvariantType`.
    ///
    /// # Example
    ///
    /// Given a struct `S` that includes a type parameter `T`
    /// but does not actually *reference* that type parameter:
    ///
    /// ```ignore
    /// use std::mem;
    ///
    /// struct S<T> { x: *() }
    /// fn get<T>(s: &S<T>) -> T {
    ///    unsafe {
    ///        let x: *T = mem::transmute(s.x);
    ///        *x
    ///    }
    /// }
    /// ```
    ///
    /// The type system would currently infer that the value of
    /// the type parameter `T` is irrelevant, and hence a `S<int>` is
    /// a subtype of `S<~[int]>` (or, for that matter, `S<U>` for
    /// any `U`). But this is incorrect because `get()` converts the
    /// `*()` into a `*T` and reads from it. Therefore, we should include the
    /// a marker field `CovariantType<T>` to inform the type checker that
    /// `S<T>` is a subtype of `S<U>` if `T` is a subtype of `U`
    /// (for example, `S<&'static int>` is a subtype of `S<&'a int>`
    /// for some lifetime `'a`, but not the other way around).
    #[lang="covariant_type"]
    #[deriving(PartialEq,Clone)]
    pub struct CovariantType<T>;

    /// A marker type whose type parameter `T` is considered to be
    /// contravariant with respect to the type itself. This is (typically)
    /// used to indicate that an instance of the type `T` will be consumed
    /// (but not read from), even though that may not be apparent.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    ///
    /// *Note:* It is very unusual to have to add a contravariant constraint.
    /// If you are not sure, you probably want to use `InvariantType`.
    ///
    /// # Example
    ///
    /// Given a struct `S` that includes a type parameter `T`
    /// but does not actually *reference* that type parameter:
    ///
    /// ```
    /// use std::mem;
    ///
    /// struct S<T> { x: *const () }
    /// fn get<T>(s: &S<T>, v: T) {
    ///    unsafe {
    ///        let x: fn(T) = mem::transmute(s.x);
    ///        x(v)
    ///    }
    /// }
    /// ```
    ///
    /// The type system would currently infer that the value of
    /// the type parameter `T` is irrelevant, and hence a `S<int>` is
    /// a subtype of `S<~[int]>` (or, for that matter, `S<U>` for
    /// any `U`). But this is incorrect because `get()` converts the
    /// `*()` into a `fn(T)` and then passes a value of type `T` to it.
    ///
    /// Supplying a `ContravariantType` marker would correct the
    /// problem, because it would mark `S` so that `S<T>` is only a
    /// subtype of `S<U>` if `U` is a subtype of `T`; given that the
    /// function requires arguments of type `T`, it must also accept
    /// arguments of type `U`, hence such a conversion is safe.
    #[lang="contravariant_type"]
    #[deriving(PartialEq,Clone)]
    pub struct ContravariantType<T>;

    /// A marker type whose type parameter `T` is considered to be
    /// invariant with respect to the type itself. This is (typically)
    /// used to indicate that instances of the type `T` may be read or
    /// written, even though that may not be apparent.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    ///
    /// # Example
    ///
    /// The Cell type is an example which uses unsafe code to achieve
    /// "interior" mutability:
    ///
    /// ```
    /// pub struct Cell<T> { value: T }
    /// # fn main() {}
    /// ```
    ///
    /// The type system would infer that `value` is only read here and
    /// never written, but in fact `Cell` uses unsafe code to achieve
    /// interior mutability.
    #[lang="invariant_type"]
    #[deriving(PartialEq,Clone)]
    pub struct InvariantType<T>;

    /// As `CovariantType`, but for lifetime parameters. Using
    /// `CovariantLifetime<'a>` indicates that it is ok to substitute
    /// a *longer* lifetime for `'a` than the one you originally
    /// started with (e.g., you could convert any lifetime `'foo` to
    /// `'static`). You almost certainly want `ContravariantLifetime`
    /// instead, or possibly `InvariantLifetime`. The only case where
    /// it would be appropriate is that you have a (type-casted, and
    /// hence hidden from the type system) function pointer with a
    /// signature like `fn(&'a T)` (and no other uses of `'a`). In
    /// this case, it is ok to substitute a larger lifetime for `'a`
    /// (e.g., `fn(&'static T)`), because the function is only
    /// becoming more selective in terms of what it accepts as
    /// argument.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    #[lang="covariant_lifetime"]
    #[deriving(PartialEq,Clone)]
    pub struct CovariantLifetime<'a>;

    /// As `ContravariantType`, but for lifetime parameters. Using
    /// `ContravariantLifetime<'a>` indicates that it is ok to
    /// substitute a *shorter* lifetime for `'a` than the one you
    /// originally started with (e.g., you could convert `'static` to
    /// any lifetime `'foo`). This is appropriate for cases where you
    /// have an unsafe pointer that is actually a pointer into some
    /// memory with lifetime `'a`, and thus you want to limit the
    /// lifetime of your data structure to `'a`. An example of where
    /// this is used is the iterator for vectors.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    #[lang="contravariant_lifetime"]
    #[deriving(PartialEq,Clone)]
    pub struct ContravariantLifetime<'a>;

    /// As `InvariantType`, but for lifetime parameters. Using
    /// `InvariantLifetime<'a>` indicates that it is not ok to
    /// substitute any other lifetime for `'a` besides its original
    /// value. This is appropriate for cases where you have an unsafe
    /// pointer that is actually a pointer into memory with lifetime `'a`,
    /// and this pointer is itself stored in an inherently mutable
    /// location (such as a `Cell`).
    #[lang="invariant_lifetime"]
    #[deriving(PartialEq,Clone)]
    pub struct InvariantLifetime<'a>;

    /// A type which is considered "not sendable", meaning that it cannot
    /// be safely sent between tasks, even if it is owned. This is
    /// typically embedded in other types, such as `Gc`, to ensure that
    /// their instances remain thread-local.
    #[lang="no_send_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct NoSend;

    /// A type which is considered "not POD", meaning that it is not
    /// implicitly copyable. This is typically embedded in other types to
    /// ensure that they are never copied, even if they lack a destructor.
    #[lang="no_copy_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct NoCopy;

    /// A type which is considered "not shareable", meaning that
    /// its contents are not threadsafe, hence they cannot be
    /// shared between tasks.
    #[lang="no_share_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct NoShare;

    /// A type which is considered managed by the GC. This is typically
    /// embedded in other types.
    #[lang="managed_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct Managed;
}
<commit_msg>auto merge of #16276 : nham/rust/fix_marker_docs, r=steveklabnik<commit_after>// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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. This file may not be copied, modified, or distributed
// except according to those terms.

/*!
Primitive traits representing basic 'kinds' of types

Rust types can be classified in various useful ways according to
intrinsic properties of the type. These classifications, often called
'kinds', are represented as traits.

They cannot be implemented by user code, but are instead implemented
by the compiler automatically for the types to which they apply.

*/

/// Types able to be transferred across task boundaries.
#[lang="send"]
pub trait Send {
    // empty.
}

/// Types with a constant size known at compile-time.
#[lang="sized"]
pub trait Sized {
    // Empty.
}

/// Types that can be copied by simply copying bits (i.e. `memcpy`).
#[lang="copy"]
pub trait Copy {
    // Empty.
}

/// Types that can be safely shared between tasks when aliased.
///
/// The precise definition is: a type `T` is `Share` if `&T` is
/// thread-safe. In other words, there is no possibility of data races
/// when passing `&T` references between tasks.
///
/// As one would expect, primitive types like `u8` and `f64` are all
/// `Share`, and so are simple aggregate types containing them (like
/// tuples, structs and enums). More instances of basic `Share` types
/// include "immutable" types like `&T` and those with simple
/// inherited mutability, such as `Box<T>`, `Vec<T>` and most other
/// collection types. (Generic parameters need to be `Share` for their
/// container to be `Share`.)
///
/// A somewhat surprising consequence of the definition is `&mut T` is
/// `Share` (if `T` is `Share`) even though it seems that it might
/// provide unsynchronised mutation. The trick is a mutable reference
/// stored in an aliasable reference (that is, `& &mut T`) becomes
/// read-only, as if it were a `& &T`, hence there is no risk of a data
/// race.
///
/// Types that are not `Share` are those that have "interior
/// mutability" in a non-thread-safe way, such as `Cell` and `RefCell`
/// in `std::cell`. These types allow for mutation of their contents
/// even when in an immutable, aliasable slot, e.g. the contents of
/// `&Cell<T>` can be `.set`, and do not ensure data races are
/// impossible, hence they cannot be `Share`. A higher level example
/// of a non-`Share` type is the reference counted pointer
/// `std::rc::Rc`, because any reference `&Rc<T>` can clone a new
/// reference, which modifies the reference counts in a non-atomic
/// way.
///
/// For cases when one does need thread-safe interior mutability,
/// types like the atomics in `std::sync` and `Mutex` & `RWLock` in
/// the `sync` crate do ensure that any mutation cannot cause data
/// races.  Hence these types are `Share`.
///
/// Users writing their own types with interior mutability (or anything
/// else that is not thread-safe) should use the `NoShare` marker type
/// (from `std::kinds::marker`) to ensure that the compiler doesn't
/// consider the user-defined type to be `Share`.  Any types with
/// interior mutability must also use the `std::cell::UnsafeCell` wrapper
/// around the value(s) which can be mutated when behind a `&`
/// reference; not doing this is undefined behaviour (for example,
/// `transmute`-ing from `&T` to `&mut T` is illegal).
#[lang="share"]
pub trait Share {
    // Empty
}

/// Marker types are special types that are used with unsafe code to
/// inform the compiler of special constraints. Marker types should
/// only be needed when you are creating an abstraction that is
/// implemented using unsafe code. In that case, you may want to embed
/// some of the marker types below into your type.
pub mod marker {

    /// A marker type whose type parameter `T` is considered to be
    /// covariant with respect to the type itself. This is (typically)
    /// used to indicate that an instance of the type `T` is being stored
    /// into memory and read from, even though that may not be apparent.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    ///
    /// *Note:* It is very unusual to have to add a covariant constraint.
    /// If you are not sure, you probably want to use `InvariantType`.
    ///
    /// # Example
    ///
    /// Given a struct `S` that includes a type parameter `T`
    /// but does not actually *reference* that type parameter:
    ///
    /// ```ignore
    /// use std::mem;
    ///
    /// struct S<T> { x: *() }
    /// fn get<T>(s: &S<T>) -> T {
    ///    unsafe {
    ///        let x: *T = mem::transmute(s.x);
    ///        *x
    ///    }
    /// }
    /// ```
    ///
    /// The type system would currently infer that the value of
    /// the type parameter `T` is irrelevant, and hence a `S<int>` is
    /// a subtype of `S<Box<int>>` (or, for that matter, `S<U>` for
    /// any `U`). But this is incorrect because `get()` converts the
    /// `*()` into a `*T` and reads from it. Therefore, we should include the
    /// a marker field `CovariantType<T>` to inform the type checker that
    /// `S<T>` is a subtype of `S<U>` if `T` is a subtype of `U`
    /// (for example, `S<&'static int>` is a subtype of `S<&'a int>`
    /// for some lifetime `'a`, but not the other way around).
    #[lang="covariant_type"]
    #[deriving(PartialEq,Clone)]
    pub struct CovariantType<T>;

    /// A marker type whose type parameter `T` is considered to be
    /// contravariant with respect to the type itself. This is (typically)
    /// used to indicate that an instance of the type `T` will be consumed
    /// (but not read from), even though that may not be apparent.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    ///
    /// *Note:* It is very unusual to have to add a contravariant constraint.
    /// If you are not sure, you probably want to use `InvariantType`.
    ///
    /// # Example
    ///
    /// Given a struct `S` that includes a type parameter `T`
    /// but does not actually *reference* that type parameter:
    ///
    /// ```
    /// use std::mem;
    ///
    /// struct S<T> { x: *const () }
    /// fn get<T>(s: &S<T>, v: T) {
    ///    unsafe {
    ///        let x: fn(T) = mem::transmute(s.x);
    ///        x(v)
    ///    }
    /// }
    /// ```
    ///
    /// The type system would currently infer that the value of
    /// the type parameter `T` is irrelevant, and hence a `S<int>` is
    /// a subtype of `S<Box<int>>` (or, for that matter, `S<U>` for
    /// any `U`). But this is incorrect because `get()` converts the
    /// `*()` into a `fn(T)` and then passes a value of type `T` to it.
    ///
    /// Supplying a `ContravariantType` marker would correct the
    /// problem, because it would mark `S` so that `S<T>` is only a
    /// subtype of `S<U>` if `U` is a subtype of `T`; given that the
    /// function requires arguments of type `T`, it must also accept
    /// arguments of type `U`, hence such a conversion is safe.
    #[lang="contravariant_type"]
    #[deriving(PartialEq,Clone)]
    pub struct ContravariantType<T>;

    /// A marker type whose type parameter `T` is considered to be
    /// invariant with respect to the type itself. This is (typically)
    /// used to indicate that instances of the type `T` may be read or
    /// written, even though that may not be apparent.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    ///
    /// # Example
    ///
    /// The Cell type is an example which uses unsafe code to achieve
    /// "interior" mutability:
    ///
    /// ```
    /// pub struct Cell<T> { value: T }
    /// # fn main() {}
    /// ```
    ///
    /// The type system would infer that `value` is only read here and
    /// never written, but in fact `Cell` uses unsafe code to achieve
    /// interior mutability.
    #[lang="invariant_type"]
    #[deriving(PartialEq,Clone)]
    pub struct InvariantType<T>;

    /// As `CovariantType`, but for lifetime parameters. Using
    /// `CovariantLifetime<'a>` indicates that it is ok to substitute
    /// a *longer* lifetime for `'a` than the one you originally
    /// started with (e.g., you could convert any lifetime `'foo` to
    /// `'static`). You almost certainly want `ContravariantLifetime`
    /// instead, or possibly `InvariantLifetime`. The only case where
    /// it would be appropriate is that you have a (type-casted, and
    /// hence hidden from the type system) function pointer with a
    /// signature like `fn(&'a T)` (and no other uses of `'a`). In
    /// this case, it is ok to substitute a larger lifetime for `'a`
    /// (e.g., `fn(&'static T)`), because the function is only
    /// becoming more selective in terms of what it accepts as
    /// argument.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    #[lang="covariant_lifetime"]
    #[deriving(PartialEq,Clone)]
    pub struct CovariantLifetime<'a>;

    /// As `ContravariantType`, but for lifetime parameters. Using
    /// `ContravariantLifetime<'a>` indicates that it is ok to
    /// substitute a *shorter* lifetime for `'a` than the one you
    /// originally started with (e.g., you could convert `'static` to
    /// any lifetime `'foo`). This is appropriate for cases where you
    /// have an unsafe pointer that is actually a pointer into some
    /// memory with lifetime `'a`, and thus you want to limit the
    /// lifetime of your data structure to `'a`. An example of where
    /// this is used is the iterator for vectors.
    ///
    /// For more information about variance, refer to this Wikipedia
    /// article <http://en.wikipedia.org/wiki/Variance_%28computer_science%29>.
    #[lang="contravariant_lifetime"]
    #[deriving(PartialEq,Clone)]
    pub struct ContravariantLifetime<'a>;

    /// As `InvariantType`, but for lifetime parameters. Using
    /// `InvariantLifetime<'a>` indicates that it is not ok to
    /// substitute any other lifetime for `'a` besides its original
    /// value. This is appropriate for cases where you have an unsafe
    /// pointer that is actually a pointer into memory with lifetime `'a`,
    /// and this pointer is itself stored in an inherently mutable
    /// location (such as a `Cell`).
    #[lang="invariant_lifetime"]
    #[deriving(PartialEq,Clone)]
    pub struct InvariantLifetime<'a>;

    /// A type which is considered "not sendable", meaning that it cannot
    /// be safely sent between tasks, even if it is owned. This is
    /// typically embedded in other types, such as `Gc`, to ensure that
    /// their instances remain thread-local.
    #[lang="no_send_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct NoSend;

    /// A type which is considered "not POD", meaning that it is not
    /// implicitly copyable. This is typically embedded in other types to
    /// ensure that they are never copied, even if they lack a destructor.
    #[lang="no_copy_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct NoCopy;

    /// A type which is considered "not shareable", meaning that
    /// its contents are not threadsafe, hence they cannot be
    /// shared between tasks.
    #[lang="no_share_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct NoShare;

    /// A type which is considered managed by the GC. This is typically
    /// embedded in other types.
    #[lang="managed_bound"]
    #[deriving(PartialEq,Clone)]
    pub struct Managed;
}
<|endoftext|>