<commit_before>extern mod extra;
use extra::container::Deque;
use extra::ringbuf::RingBuf;
use std::iter::Peekable;
#[deriving(Eq, ToStr)]
enum TokenEnum {
	EOF,
	NAME,
	OPERATOR,
	NUMBER,
	FLOAT,
	LPARENS,
	RPARENS,
	LBRACKET,
	RBRACKET,
	LBRACE,
	RBRACE,
	INDENTSTART,
	INDENTLEVEL,
	COMMA,
	EQUALSSIGN,
	SEMICOLON,
	MODULE,
	CLASS,
	INSTANCE,
	WHERE,
	LET,
	IN,
	CASE,
	OF,
	ARROW,
	TYPEDECL,
	DATA
}

#[deriving(Eq, ToStr)]
struct Location {
    column : int,
    row : int,
    absolute : int
}

struct Token {
    token : TokenEnum,
    value : ~str,
    location : Location
}
impl Token {
    fn eof() -> Token {
        Token { token : EOF, value : ~"", location : Location { column : -1, row : -1, absolute : -1} }
    }

    fn new(token : TokenEnum, value : ~str, loc : Location) -> Token {
        Token { token : token, value : value, location : loc }
    }
    fn new_(token : TokenEnum, value : ~str) -> Token {
        Token::new(token, value, Location { column : -1, row : -1, absolute : -1 })
    }
}

impl Eq for Token {
    fn eq(&self, rhs : &Token) -> bool {
        self.token == rhs.token && self.value == rhs.value
    }
}

fn name_or_keyword(tok : &str) -> TokenEnum {
    match tok {
        "module" => MODULE,
        "class" => CLASS,
        "instance" => INSTANCE,
        "where" => WHERE,
        "let" => LET,
        "in" => IN,
        "case" => CASE,
        "of" => OF,
        "->" => ARROW,
        "data" => DATA,
        _ => NAME
    }
}

fn is_operator(first_char : char) -> bool {
    match first_char {
        '+' => true,
        '-' => true,
        '*' => true,
        '/' => true,
        '.' => true,
        '$' => true,
        ':' => true,
        '=' => true,
        '<' => true,
        '>' => true,
        '|' => true,
        '&' => true,
        _ => false
    }
}

struct Lexer<Stream> {
    input : Peekable<char, Stream>,
    location : Location,
    previousLocation : Location,
    unprocessedTokens : ~[Token],
    tokens : extra::ringbuf::RingBuf<Token>,
    indentLevels : ~[int],
    offset : uint
}

impl <Stream : Iterator<char>> Lexer<Stream> {

    pub fn new(input : Stream) -> Lexer<Stream> {
        let start = Location { column : 0, row : 0, absolute : 0};
        Lexer {
            input : input.peekable(),
            location : start,
            previousLocation : start,
            unprocessedTokens : ~[],
            tokens : extra::ringbuf::RingBuf::with_capacity(20),
            indentLevels : ~[],
            offset : 0}
    }

    fn peek(&mut self) -> Option<char> {
        match self.input.peek() {
            Some(ch) => Some(*ch),
            None => None
        }
    }

    fn read_char(&mut self) -> Option<char> {
        match self.input.next() {
            Some(c) => {
                self.location = self.location;
                self.location.absolute += 1;
                self.location.column += 1;
                if (c == '\n' || c == '\r')
                {
                    self.location.column = 0;
                    self.location.row += 1;
                }
                Some(c)
            }
            None => None
        }
    }

    fn scan_digits(&mut self) -> ~str {
        let mut result = ~"";
        loop {
            match self.peek() {
                Some(x) => {
                    if !x.is_digit() {
                        return result;
                    }
                    self.read_char();
                    result.push_char(x)
                }
                None => return result
            }
        }
        result
    }

    fn scan_number(&mut self, c : char, location : Location) -> Token {
        let mut number = c.to_str() + self.scan_digits();
        let mut token = NUMBER;
        match self.peek() {
            Some('.') => {
                token = FLOAT;
                number.push_char('.');
                number = number.append(self.scan_digits());
            }
            _ => ()
        }
        Token { token : NUMBER, value : number, location : location }
    }

    pub fn next_<'a>(&'a mut self) -> &'a Token {
        self.next(|_| false)
    }
    pub fn next<'a>(&'a mut self, parseError : |TokenEnum| -> bool) -> &'a Token {
        if self.offset > 0 {
            match self.tokens.iter().idx(self.tokens.len() - 1 - self.offset) {
                Some(token) => token,
                None => fail!("Impossible empty tokens stream")
            }
        }
        else if self.unprocessedTokens.len() > 0 {
            self.layout_independent_token(parseError);
            self.tokens.back().unwrap()
        }
        else {
            self.new_token(parseError)
        }
    }

    fn new_token<'a>(&'a mut self, parseError : |TokenEnum| -> bool) -> &'a Token {
        let mut newline = false;
        let n = self.next_indent_token(&mut newline);
        self.unprocessedTokens.push(n);
        let newTok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;

        if newTok != LBRACE {
            match self.tokens.back() {
                Some(tok) => {
                    if tok.token == LET || tok.token == WHERE || tok.token == OF {
                        let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
                        let indentstart = Token { token : INDENTSTART, value : ~"{n}", location : loc };
                        self.unprocessedTokens.push(indentstart);
                    }
                }
                None => ()
            }
        }
        if newline {
            self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", self.location));
        }
        self.layout_independent_token(parseError);
        self.tokens.back().unwrap()
    }

    fn layout_independent_token(&mut self, parseError : |TokenEnum| -> bool) {
        if self.unprocessedTokens.len() > 0 {
            let tok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;
            let location = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
            match tok {
                INDENTLEVEL => {
                    if (self.indentLevels.len() > 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        //m == n
                        if (m == location.column) {
                            self.tokens.push_back(Token::new(SEMICOLON, ~";", location));
                            self.unprocessedTokens.pop();
                            return;
                        }
                        else if (location.column < m)// n < m
                        {
                            //TODO
                            self.indentLevels.pop();
                            self.tokens.push_back(Token::new(RBRACE, ~"}", location));
                            return;
                        }
                    }
                    self.unprocessedTokens.pop();
                    if (self.unprocessedTokens.len() == 0) {
                        debug!("Skipping to next");
                        self.new_token(parseError);
                        return;
                    }
                    else {
                        return self.layout_independent_token(parseError);
                    }
                }
                INDENTSTART => {
                    //{n} token
                    let n = location.column;
                    if (self.indentLevels.len() != 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        if (n > m) {
                            self.unprocessedTokens.pop();
                            self.tokens.push_back(Token::new(LBRACE, ~"{", location));
                            self.indentLevels.push(n);
                            return;
                        }
                    }
                    if (n > 0)
                    {
                        self.tokens.push_back(Token::new(LBRACE, ~"{", location));
                        self.unprocessedTokens.pop();
                        self.indentLevels.push(n);
                        return;
                    }
                    self.tokens.push_back(Token::new(LBRACE, ~"{", location));
                    self.tokens.push_back(Token::new(RBRACE, ~"}", location));
                    self.unprocessedTokens.pop();
                    self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", location));
                    self.offset += 1;
                    return;
                }
                RBRACE => {
                    if (self.indentLevels.len() > 0 && self.indentLevels[self.indentLevels.len()] == 0) {
                        self.tokens.push_back(self.unprocessedTokens.pop());
                        self.indentLevels.pop();
                        return;
                    }
                    else {
                        return;//parse-error
                    }
                }
                LBRACE => {
                    self.tokens.push_back(self.unprocessedTokens.pop());
                    self.indentLevels.push(0);
                    return;
                }

                _ => ()
            }
            if (self.indentLevels.len() != 0) {
                //L (t:ts) (m:ms) 	= 	} : (L (t:ts) ms) 	if m /= 0 and parse-error(t)
                let m = self.indentLevels[self.indentLevels.len() - 1];
                if (m != 0 && parseError(tok))
                {
                    self.tokens.push_back(Token::new(RBRACE, ~"}", location));
                    self.indentLevels.pop();
                    return;
                }
            }
            self.tokens.push_back(self.unprocessedTokens.pop());
            return;
        }
        else {
            if (self.indentLevels.len() == 0)//End of stream
            {
                return;
            }
            else if (self.indentLevels[self.indentLevels.len() - 1] != 0)//Keep pusing righ brackets
            {
                self.indentLevels.pop();
                self.tokens.push_back(Token::new_(RBRACE, ~"}"));
                return;
            }
        }
    }

    fn next_indent_token(&mut self, newline : &mut bool) -> Token {
        let mut c = ' ';
        //Skip all whitespace before the token
        while c.is_whitespace() {
            match self.read_char() {
                Some(x) => {
                    c = x;
                    if (self.location.column == 0)//newline detected
                    {
                        *newline = true;
                    }
                }
                None => { return Token::eof() }
            }
        }
        let startLocation = self.location;

        //Decide how to tokenize depending on what the first char is
        //ie if its an operator then more operators will follow
        if (is_operator(c))
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !is_operator(ch) {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => { break; }
                }
            }
            let tok = match result {
                ~"="  => EQUALSSIGN,
                ~"->" => ARROW,
                ~"::" => TYPEDECL,
                _    => OPERATOR
            };
            return Token { token : tok, value : result, location : startLocation };
        }
        else if (c.is_digit())
        {
            return self.scan_number(c, startLocation);
        }
        else if (c.is_alphabetic() || c == '_')
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !ch.is_alphanumeric() && ch != '_' {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => return Token::eof()
                }
            }
            return Token {
                token : name_or_keyword(result),
                location : startLocation,
                value : result};
        }
        let tok = match c {
            ';' => SEMICOLON,
            '(' => LPARENS,
            ')' => RPARENS,
            '[' => LBRACKET,
            ']' => RBRACKET,
            '{' => LBRACE,
            '}' => RBRACE,
            ',' => COMMA,
            _   => EOF
        };
        Token { token : tok, location : startLocation, value : c.to_str() }
    }
}

#[test]
fn simple() {
    let mut lexer = Lexer::new("test 2 + 3".chars());

    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
#[test]
fn let_bind() {
    let mut lexer = Lexer::new(
r"let
    test = 2 + 3
in test".chars());

    assert_eq!(*lexer.next_(), Token::new_(LET, ~"let"));
    assert_eq!(*lexer.next_(), Token::new_(LBRACE, ~"{"));
    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(EQUALSSIGN, ~"="));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
<commit_msg>Added backtrack to lexer<commit_after>extern mod extra;
use extra::container::Deque;
use extra::ringbuf::RingBuf;
use std::iter::Peekable;
#[deriving(Eq, ToStr)]
enum TokenEnum {
	EOF,
	NAME,
	OPERATOR,
	NUMBER,
	FLOAT,
	LPARENS,
	RPARENS,
	LBRACKET,
	RBRACKET,
	LBRACE,
	RBRACE,
	INDENTSTART,
	INDENTLEVEL,
	COMMA,
	EQUALSSIGN,
	SEMICOLON,
	MODULE,
	CLASS,
	INSTANCE,
	WHERE,
	LET,
	IN,
	CASE,
	OF,
	ARROW,
	TYPEDECL,
	DATA
}

#[deriving(Eq, ToStr)]
struct Location {
    column : int,
    row : int,
    absolute : int
}

struct Token {
    token : TokenEnum,
    value : ~str,
    location : Location
}
impl Token {
    fn eof() -> Token {
        Token { token : EOF, value : ~"", location : Location { column : -1, row : -1, absolute : -1} }
    }

    fn new(token : TokenEnum, value : ~str, loc : Location) -> Token {
        Token { token : token, value : value, location : loc }
    }
    fn new_(token : TokenEnum, value : ~str) -> Token {
        Token::new(token, value, Location { column : -1, row : -1, absolute : -1 })
    }
}

impl Eq for Token {
    fn eq(&self, rhs : &Token) -> bool {
        self.token == rhs.token && self.value == rhs.value
    }
}

fn name_or_keyword(tok : &str) -> TokenEnum {
    match tok {
        "module" => MODULE,
        "class" => CLASS,
        "instance" => INSTANCE,
        "where" => WHERE,
        "let" => LET,
        "in" => IN,
        "case" => CASE,
        "of" => OF,
        "->" => ARROW,
        "data" => DATA,
        _ => NAME
    }
}

fn is_operator(first_char : char) -> bool {
    match first_char {
        '+' => true,
        '-' => true,
        '*' => true,
        '/' => true,
        '.' => true,
        '$' => true,
        ':' => true,
        '=' => true,
        '<' => true,
        '>' => true,
        '|' => true,
        '&' => true,
        _ => false
    }
}

struct Lexer<Stream> {
    input : Peekable<char, Stream>,
    location : Location,
    previousLocation : Location,
    unprocessedTokens : ~[Token],
    tokens : extra::ringbuf::RingBuf<Token>,
    indentLevels : ~[int],
    offset : uint
}

impl <Stream : Iterator<char>> Lexer<Stream> {

    pub fn new(input : Stream) -> Lexer<Stream> {
        let start = Location { column : 0, row : 0, absolute : 0};
        Lexer {
            input : input.peekable(),
            location : start,
            previousLocation : start,
            unprocessedTokens : ~[],
            tokens : extra::ringbuf::RingBuf::with_capacity(20),
            indentLevels : ~[],
            offset : 0}
    }

    pub fn next_<'a>(&'a mut self) -> &'a Token {
        self.next(|_| false)
    }
    pub fn next<'a>(&'a mut self, parseError : |TokenEnum| -> bool) -> &'a Token {
        if self.offset > 0 {
            match self.tokens.iter().idx(self.tokens.len() - 1 - self.offset) {
                Some(token) => token,
                None => fail!("Impossible empty tokens stream")
            }
        }
        else if self.unprocessedTokens.len() > 0 {
            self.layout_independent_token(parseError);
            self.tokens.back().unwrap()
        }
        else {
            self.new_token(parseError)
        }
    }

    pub fn backtrack(&mut self) {
        self.offset += 1;
    }

    fn peek(&mut self) -> Option<char> {
        match self.input.peek() {
            Some(ch) => Some(*ch),
            None => None
        }
    }

    fn read_char(&mut self) -> Option<char> {
        match self.input.next() {
            Some(c) => {
                self.location = self.location;
                self.location.absolute += 1;
                self.location.column += 1;
                if (c == '\n' || c == '\r')
                {
                    self.location.column = 0;
                    self.location.row += 1;
                }
                Some(c)
            }
            None => None
        }
    }

    fn scan_digits(&mut self) -> ~str {
        let mut result = ~"";
        loop {
            match self.peek() {
                Some(x) => {
                    if !x.is_digit() {
                        return result;
                    }
                    self.read_char();
                    result.push_char(x)
                }
                None => return result
            }
        }
        result
    }

    fn scan_number(&mut self, c : char, location : Location) -> Token {
        let mut number = c.to_str() + self.scan_digits();
        let mut token = NUMBER;
        match self.peek() {
            Some('.') => {
                token = FLOAT;
                number.push_char('.');
                number = number.append(self.scan_digits());
            }
            _ => ()
        }
        Token { token : NUMBER, value : number, location : location }
    }

    fn new_token<'a>(&'a mut self, parseError : |TokenEnum| -> bool) -> &'a Token {
        let mut newline = false;
        let n = self.next_indent_token(&mut newline);
        self.unprocessedTokens.push(n);
        let newTok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;

        if newTok != LBRACE {
            match self.tokens.back() {
                Some(tok) => {
                    if tok.token == LET || tok.token == WHERE || tok.token == OF {
                        let loc = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
                        let indentstart = Token { token : INDENTSTART, value : ~"{n}", location : loc };
                        self.unprocessedTokens.push(indentstart);
                    }
                }
                None => ()
            }
        }
        if newline {
            self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", self.location));
        }
        self.layout_independent_token(parseError);
        self.tokens.back().unwrap()
    }

    fn layout_independent_token(&mut self, parseError : |TokenEnum| -> bool) {
        if self.unprocessedTokens.len() > 0 {
            let tok = self.unprocessedTokens[self.unprocessedTokens.len() - 1].token;
            let location = self.unprocessedTokens[self.unprocessedTokens.len() - 1].location;
            match tok {
                INDENTLEVEL => {
                    if (self.indentLevels.len() > 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        //m == n
                        if (m == location.column) {
                            self.tokens.push_back(Token::new(SEMICOLON, ~";", location));
                            self.unprocessedTokens.pop();
                            return;
                        }
                        else if (location.column < m)// n < m
                        {
                            //TODO
                            self.indentLevels.pop();
                            self.tokens.push_back(Token::new(RBRACE, ~"}", location));
                            return;
                        }
                    }
                    self.unprocessedTokens.pop();
                    if (self.unprocessedTokens.len() == 0) {
                        debug!("Skipping to next");
                        self.new_token(parseError);
                        return;
                    }
                    else {
                        return self.layout_independent_token(parseError);
                    }
                }
                INDENTSTART => {
                    //{n} token
                    let n = location.column;
                    if (self.indentLevels.len() != 0) {
                        //m:ms
                        let m = self.indentLevels[self.indentLevels.len() - 1];
                        if (n > m) {
                            self.unprocessedTokens.pop();
                            self.tokens.push_back(Token::new(LBRACE, ~"{", location));
                            self.indentLevels.push(n);
                            return;
                        }
                    }
                    if (n > 0)
                    {
                        self.tokens.push_back(Token::new(LBRACE, ~"{", location));
                        self.unprocessedTokens.pop();
                        self.indentLevels.push(n);
                        return;
                    }
                    self.tokens.push_back(Token::new(LBRACE, ~"{", location));
                    self.tokens.push_back(Token::new(RBRACE, ~"}", location));
                    self.unprocessedTokens.pop();
                    self.unprocessedTokens.push(Token::new(INDENTLEVEL, ~"<n>", location));
                    self.offset += 1;
                    return;
                }
                RBRACE => {
                    if (self.indentLevels.len() > 0 && self.indentLevels[self.indentLevels.len()] == 0) {
                        self.tokens.push_back(self.unprocessedTokens.pop());
                        self.indentLevels.pop();
                        return;
                    }
                    else {
                        return;//parse-error
                    }
                }
                LBRACE => {
                    self.tokens.push_back(self.unprocessedTokens.pop());
                    self.indentLevels.push(0);
                    return;
                }

                _ => ()
            }
            if (self.indentLevels.len() != 0) {
                //L (t:ts) (m:ms) 	= 	} : (L (t:ts) ms) 	if m /= 0 and parse-error(t)
                let m = self.indentLevels[self.indentLevels.len() - 1];
                if (m != 0 && parseError(tok))
                {
                    self.tokens.push_back(Token::new(RBRACE, ~"}", location));
                    self.indentLevels.pop();
                    return;
                }
            }
            self.tokens.push_back(self.unprocessedTokens.pop());
            return;
        }
        else {
            if (self.indentLevels.len() == 0)//End of stream
            {
                return;
            }
            else if (self.indentLevels[self.indentLevels.len() - 1] != 0)//Keep pusing righ brackets
            {
                self.indentLevels.pop();
                self.tokens.push_back(Token::new_(RBRACE, ~"}"));
                return;
            }
        }
    }

    fn next_indent_token(&mut self, newline : &mut bool) -> Token {
        let mut c = ' ';
        //Skip all whitespace before the token
        while c.is_whitespace() {
            match self.read_char() {
                Some(x) => {
                    c = x;
                    if (self.location.column == 0)//newline detected
                    {
                        *newline = true;
                    }
                }
                None => { return Token::eof() }
            }
        }
        let startLocation = self.location;

        //Decide how to tokenize depending on what the first char is
        //ie if its an operator then more operators will follow
        if (is_operator(c))
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !is_operator(ch) {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => { break; }
                }
            }
            let tok = match result {
                ~"="  => EQUALSSIGN,
                ~"->" => ARROW,
                ~"::" => TYPEDECL,
                _    => OPERATOR
            };
            return Token { token : tok, value : result, location : startLocation };
        }
        else if (c.is_digit())
        {
            return self.scan_number(c, startLocation);
        }
        else if (c.is_alphabetic() || c == '_')
        {
            let mut result = c.to_str();
            loop {
                match self.peek() {
                    Some(ch) => {
                        if !ch.is_alphanumeric() && ch != '_' {
                            break;
                        }
                        self.read_char();
                        result.push_char(ch);
                    }
                    None => return Token::eof()
                }
            }
            return Token {
                token : name_or_keyword(result),
                location : startLocation,
                value : result};
        }
        let tok = match c {
            ';' => SEMICOLON,
            '(' => LPARENS,
            ')' => RPARENS,
            '[' => LBRACKET,
            ']' => RBRACKET,
            '{' => LBRACE,
            '}' => RBRACE,
            ',' => COMMA,
            _   => EOF
        };
        Token { token : tok, location : startLocation, value : c.to_str() }
    }
}

#[test]
fn simple() {
    let mut lexer = Lexer::new("test 2 + 3".chars());

    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
#[test]
fn let_bind() {
    let mut lexer = Lexer::new(
r"let
    test = 2 + 3
in test".chars());

    assert_eq!(*lexer.next_(), Token::new_(LET, ~"let"));
    assert_eq!(*lexer.next_(), Token::new_(LBRACE, ~"{"));
    assert_eq!(*lexer.next_(), Token::new_(NAME, ~"test"));
    assert_eq!(*lexer.next_(), Token::new_(EQUALSSIGN, ~"="));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"2"));
    assert_eq!(*lexer.next_(), Token::new_(OPERATOR, ~"+"));
    assert_eq!(*lexer.next_(), Token::new_(NUMBER, ~"3"));
}
<|endoftext|>


<commit_before>// See LICENSE file for copyright and license details.

use std::f32::consts::PI;
use std::io::File;
use std::str::from_utf8_owned;
use visualizer::types::MFloat;

pub fn deg_to_rad(n: MFloat) -> MFloat {
    n * PI / 180.0
}

// TODO: handle errors
pub fn read_file(path: &Path) -> ~str {
    if !path.exists() {
        fail!("no such path");
    }
    let shader = File::open(path).map(|mut v| v.read_to_end()).unwrap();
    let shader = from_utf8_owned(shader.unwrap());
    shader.unwrap()
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<commit_msg>core/misc.rs: read_file: Fixed TODO 'handle errors'<commit_after>// See LICENSE file for copyright and license details.

use std::f32::consts::PI;
use std::io::File;
use std::str::from_utf8_owned;
use visualizer::types::MFloat;

pub fn deg_to_rad(n: MFloat) -> MFloat {
    n * PI / 180.0
}

pub fn read_file(path: &Path) -> ~str {
    if !path.exists() {
        fail!("Path does not exists: {}", path.display());
    }
    let shader = match File::open(path).map(|mut v| v.read_to_end()) {
        Ok(txt) => from_utf8_owned(match txt {
            Ok(txt) => txt,
            Err(_) => fail!("Can not read file {}", path.display()),
        }),
        Err(_) => fail!("Can not read file {}", path.display()),
    };
    match shader {
        Some(shader) => shader,
        None => fail!("Can not read file {}", path.display()),
    }
}

// vim: set tabstop=4 shiftwidth=4 softtabstop=4 expandtab:
<|endoftext|>


<commit_before>//!
//! Generic addresses
//!

use std::{fmt, mem};
use std::ops::{Not, BitAnd, BitOr, BitXor};

/// A trait for all addresses
pub trait Address: Copy + Not<Output=Self> + BitAnd<Output=Self> + BitOr<Output=Self> + BitXor<Output=Self> + fmt::UpperHex {
    /// Type of address offset
    type Offset;

    /// Calculate new address with given offset (wrapping)
    fn offset (self, offset: Self::Offset) -> Self;

    /// Calculate new address with given offset (wrapping) while protecting the masked part of
    /// the address
    fn offset_masked (&self, offset: Self::Offset, mask: Self) -> Self {
        (*self & mask) | (self.offset(offset) & !mask)
    }

    /// Return an object for displaying the address
    fn display (&self) -> Display<Self> {
        Display { addr: self }
    }
}

macro_rules! impl_address {
    ($addr_type:ty, $offset_type:ty) => (
        impl Address for $addr_type {
            type Offset = $offset_type;

            fn offset (self, offset: $offset_type) -> $addr_type {
                if offset < 0 {
                    self.wrapping_sub((-offset as $addr_type))
                } else {
                    self.wrapping_add(offset as $addr_type)
                }
            }
        }
    );
}

impl_address!( u8,  i8);
impl_address!(u16, i16);
impl_address!(u32, i32);

/// Helper struct for displaying an address
pub struct Display<'a, A: 'a> {
    addr: &'a A,
}

impl<'a, A: Address> fmt::Display for Display<'a, A> {
    fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
        match mem::size_of::<A>() {
            1 => write!(f, "${:02X}", self.addr),
            2 => write!(f, "${:04X}", self.addr),
            4 => write!(f, "${:08X}", self.addr),
            _ => write!(f, "${:X}", self.addr),
        }
    }
}

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

    #[test]
    fn offset () {
        assert_eq!(0x1234_u16.offset( 5), 0x1239);
        assert_eq!(0x1234_u16.offset(-3), 0x1231);
    }

    #[test]
    fn offset_wrapping () {
        assert_eq!(0xffff_u16.offset( 1), 0x0000);
        assert_eq!(0x0000_u16.offset(-1), 0xffff);
    }

    #[test]
    fn offset_masked () {
        assert_eq!(0x12ff_u16.offset_masked( 1, 0x0000), 0x1300);
        assert_eq!(0x12ff_u16.offset_masked( 1, 0xff00), 0x1200);
        assert_eq!(0x1300_u16.offset_masked(-1, 0x0000), 0x12ff);
        assert_eq!(0x1300_u16.offset_masked(-1, 0xff00), 0x13ff);
    }

    #[test]
    fn displaying () {
        assert_eq!(format!("{}", 0x0f_u8.display()), "$0F");
        assert_eq!(format!("{}", 0x01ff_u16.display()), "$01FF");
    }
}
<commit_msg>Implement successive iterator for addresses<commit_after>//!
//! Generic addresses
//!

use std::{fmt, mem};
use std::ops::{Not, BitAnd, BitOr, BitXor};

/// A trait for all addresses
pub trait Address: Copy + Ord + Eq + Not<Output=Self> + BitAnd<Output=Self> + BitOr<Output=Self> + BitXor<Output=Self> + fmt::UpperHex {
    /// Type of address offset
    type Offset;

    /// The zero address
    /// TODO: Use std::num::Zero instead when it becomes stable
    fn zero () -> Self;

    /// The succeeding address (wrapping)
    fn next (self) -> Self;

    /// Calculate new address with given offset (wrapping)
    fn offset (self, offset: Self::Offset) -> Self;

    /// Calculate new address with given offset (wrapping) while protecting the masked part of
    /// the address
    fn offset_masked (&self, offset: Self::Offset, mask: Self) -> Self {
        (*self & mask) | (self.offset(offset) & !mask)
    }

    /// Return an iterator for getting successive addresses
    fn successive (self) -> Iter<Self> {
        Iter { addr: self, done: false }
    }

    /// Return an object for displaying the address
    fn display (&self) -> Display<Self> {
        Display { addr: self }
    }
}

macro_rules! impl_address {
    ($addr_type:ty, $offset_type:ty) => (
        impl Address for $addr_type {
            type Offset = $offset_type;

            fn zero () -> $addr_type { 0 }

            fn next (self) -> $addr_type { self.wrapping_add(1) }

            fn offset (self, offset: $offset_type) -> $addr_type {
                if offset < 0 {
                    self.wrapping_sub((-offset as $addr_type))
                } else {
                    self.wrapping_add(offset as $addr_type)
                }
            }
        }
    );
}

impl_address!( u8,  i8);
impl_address!(u16, i16);
impl_address!(u32, i32);

/// Iterator for getting successive addresses
pub struct Iter<A> {
    addr: A,
    done: bool,
}

impl<A: Address> Iterator for Iter<A> {
    type Item = A;

    fn next (&mut self) -> Option<A> {
        if self.done {
            None
        } else {
            let addr = self.addr;
            self.addr = self.addr.next();
            self.done = self.addr == A::zero();
            Some(addr)
        }
    }
}

/// Helper struct for displaying an address
pub struct Display<'a, A: 'a> {
    addr: &'a A,
}

impl<'a, A: Address> fmt::Display for Display<'a, A> {
    fn fmt (&self, f: &mut fmt::Formatter) -> fmt::Result {
        match mem::size_of::<A>() {
            1 => write!(f, "${:02X}", self.addr),
            2 => write!(f, "${:04X}", self.addr),
            4 => write!(f, "${:08X}", self.addr),
            _ => write!(f, "${:X}", self.addr),
        }
    }
}

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

    #[test]
    fn zero () {
        assert_eq!(u16::zero(), 0x0000);
    }

    #[test]
    fn next () {
        assert_eq!(0x1234_u16.next(), 0x1235);
        assert_eq!(0xffff_u16.next(), 0x0000);
    }

    #[test]
    fn offset () {
        assert_eq!(0x1234_u16.offset( 5), 0x1239);
        assert_eq!(0x1234_u16.offset(-3), 0x1231);
    }

    #[test]
    fn offset_wrapping () {
        assert_eq!(0xffff_u16.offset( 1), 0x0000);
        assert_eq!(0x0000_u16.offset(-1), 0xffff);
    }

    #[test]
    fn offset_masked () {
        assert_eq!(0x12ff_u16.offset_masked( 1, 0x0000), 0x1300);
        assert_eq!(0x12ff_u16.offset_masked( 1, 0xff00), 0x1200);
        assert_eq!(0x1300_u16.offset_masked(-1, 0x0000), 0x12ff);
        assert_eq!(0x1300_u16.offset_masked(-1, 0xff00), 0x13ff);
    }

    #[test]
    fn iterating () {
        let mut it = 0xfffd_u16.successive();
        assert_eq!(it.next(), Some(0xfffd));
        assert_eq!(it.next(), Some(0xfffe));
        assert_eq!(it.next(), Some(0xffff));
        assert_eq!(it.next(), None);
    }

    #[test]
    fn displaying () {
        assert_eq!(format!("{}", 0x0f_u8.display()), "$0F");
        assert_eq!(format!("{}", 0x01ff_u16.display()), "$01FF");
    }
}
<|endoftext|>


<commit_before>use super::config::Config;
use std::collections::BTreeMap;
use std::io::BufRead;
use itertools::Itertools;

struct Replacement {
    add_segment: usize,
    index: usize,
    tag: String,
    pos_segment: usize,
    pos_offset: isize,
}

pub struct Parser<'a> {
    config: &'a Config,
    segments: Vec<Vec<u64>>,
    tags: BTreeMap<String, Vec<usize>>,
    replacements: Vec<Replacement>,
}

impl<'a> Parser<'a> {
    pub fn new(config: &'a Config) -> Self {
        Parser {
            config: config,
            segments: Vec::new(),
            tags: BTreeMap::new(),
            replacements: Vec::new(),
        }
    }

    pub fn parse<B>(&mut self, bufread: B)
        where B: BufRead
    {
        for (index, line) in bufread.lines().enumerate() {
            let line = line.unwrap_or_else(|e| panic!("Error: Failed to read from buffer: {}", e));

            if self.config.split_whitespace {
                for word in line.split_whitespace() {
                    self.parse_segment(word, index + 1);
                }
            } else {
                self.parse_segment(&line, index + 1);
            }
        }
    }

    pub fn parse_segment(&mut self, segment: &str, line: usize) {
        if self.attempt_tag_create(segment) {
            return;
        }
        if self.attempt_tag_use(segment) {
            return;
        }
        if self.attempt_rules(segment) {
            return;
        }
        panic!("Error: Unrecognized symbol \"{}\" on line {}.",
               segment,
               line);
    }

    fn attempt_tag_create(&mut self, segment: &str) -> bool {
        if let Some(caps) = self.config.tag_create.regex.as_ref().unwrap().captures(segment) {
            let config = &self.config;
            self.tags.insert(caps.at(1).unwrap().to_string(),
                             self.segments
                                 .iter()
                                 .map(|v| v.len())
                                 .collect_vec());
            true
        } else {
            false
        }
    }

    fn attempt_tag_use(&mut self, segment: &str) -> bool {
        let config = &self.config;
        for rule in &config.tag_use_rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let tag = caps.at(1).unwrap().to_string();
                for feedback in &rule.feedbacks {
                    let index = self.segments[feedback.add_segment].len();
                    let current_pos_index = self.segments[feedback.pos_segment].len();
                    self.replacements.push(Replacement {
                        add_segment: feedback.add_segment,
                        index: index,
                        tag: tag.clone(),
                        pos_segment: feedback.pos_segment,
                        pos_offset: if feedback.relative {
                            feedback.pos_offset - current_pos_index as isize
                        } else {
                            feedback.pos_offset
                        },
                    });

                    // Add the padding
                    self.segments[feedback.add_segment].push(0);
                }
                return true;
            }
        }
        false
    }

    fn attempt_rules(&mut self, segment: &str) -> bool {
        let config = &self.config;
        for rule in &config.rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let mut segvals = rule.segment_values.clone();
                for (index, capture) in rule.captures.iter().enumerate() {
                    use std::mem::transmute;
                    let cap_string = caps.at(index + 1).unwrap();
                    let pval = i64::from_str_radix(cap_string, capture.base).unwrap_or_else(|e| {
                        panic!("Error: Failed to parse captured string \"{}\" from \"{}\": {}",
                               cap_string,
                               segment,
                               e);
                    });
                    let val: u64 = unsafe { transmute(pval) };

                    for feedback in &capture.feedbacks {
                        let shiftval = val << feedback.shift;
                        if feedback.fill {
                            let baseval = segvals[feedback.segment][index];
                            for _ in 0..(shiftval + feedback.fill_offset as u64) {
                                self.segments[feedback.segment].push(baseval);
                            }
                        } else {
                            segvals[feedback.segment][index] += shiftval;
                        }
                    }
                }
                for (segvec, segment) in segvals.iter_mut().zip(self.segments.iter_mut()) {
                    segment.append(segvec);
                }
                return true;
            }
        }
        false
    }
}
<commit_msg>add function to link tags<commit_after>use super::config::Config;
use std::collections::BTreeMap;
use std::io::BufRead;
use itertools::Itertools;

struct Replacement {
    // Segment to add the value to.
    add_segment: usize,
    // The index in the add_segment to add the value.
    index: usize,
    // The tag to retrieve the position to add.
    tag: String,
    // The segment for which we are retrieving the position from the tag.
    pos_segment: usize,
    // The offset we add after retrieving the value from the tag.
    pos_offset: isize,
}

pub struct Parser<'a> {
    config: &'a Config,
    segments: Vec<Vec<u64>>,
    tags: BTreeMap<String, Vec<usize>>,
    replacements: Vec<Replacement>,
}

impl<'a> Parser<'a> {
    pub fn new(config: &'a Config) -> Self {
        Parser {
            config: config,
            segments: Vec::new(),
            tags: BTreeMap::new(),
            replacements: Vec::new(),
        }
    }
/*
    pub fn link(&mut self) {
        // Iterate through every replacement.
        for r in &self.replacements {
            // Get the tag offset vector corresponding to the replacement.
            let tag = self.tags.get(&r.tag);
            self.segments[r.add_segment][r.index] = (tag[r.pos_segment] as isize + r.pos_offset) as u64;
        }
    }*/

    pub fn parse<B>(&mut self, bufread: B)
        where B: BufRead
    {
        for (index, line) in bufread.lines().enumerate() {
            let line = line.unwrap_or_else(|e| panic!("Error: Failed to read from buffer: {}", e));

            if self.config.split_whitespace {
                for word in line.split_whitespace() {
                    self.parse_segment(word, index + 1);
                }
            } else {
                self.parse_segment(&line, index + 1);
            }
        }
    }

    pub fn parse_segment(&mut self, segment: &str, line: usize) {
        if self.attempt_tag_create(segment) {
            return;
        }
        if self.attempt_tag_use(segment) {
            return;
        }
        if self.attempt_rules(segment) {
            return;
        }
        panic!("Error: Unrecognized symbol \"{}\" on line {}.",
               segment,
               line);
    }

    fn attempt_tag_create(&mut self, segment: &str) -> bool {
        if let Some(caps) = self.config.tag_create.regex.as_ref().unwrap().captures(segment) {
            let config = &self.config;
            self.tags.insert(caps.at(1).unwrap().to_string(),
                             self.segments
                                 .iter()
                                 .map(|v| v.len())
                                 .collect_vec());
            true
        } else {
            false
        }
    }

    fn attempt_tag_use(&mut self, segment: &str) -> bool {
        let config = &self.config;
        for rule in &config.tag_use_rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let tag = caps.at(1).unwrap().to_string();
                for feedback in &rule.feedbacks {
                    let index = self.segments[feedback.add_segment].len();
                    let current_pos_index = self.segments[feedback.pos_segment].len();
                    self.replacements.push(Replacement {
                        add_segment: feedback.add_segment,
                        index: index,
                        tag: tag.clone(),
                        pos_segment: feedback.pos_segment,
                        pos_offset: if feedback.relative {
                            feedback.pos_offset - current_pos_index as isize
                        } else {
                            feedback.pos_offset
                        },
                    });

                    // Add the padding
                    self.segments[feedback.add_segment].push(0);
                }
                return true;
            }
        }
        false
    }

    fn attempt_rules(&mut self, segment: &str) -> bool {
        let config = &self.config;
        for rule in &config.rules {
            if let Some(caps) = rule.regex.as_ref().unwrap().captures(segment) {
                let mut segvals = rule.segment_values.clone();
                for (index, capture) in rule.captures.iter().enumerate() {
                    use std::mem::transmute;
                    let cap_string = caps.at(index + 1).unwrap();
                    let pval = i64::from_str_radix(cap_string, capture.base).unwrap_or_else(|e| {
                        panic!("Error: Failed to parse captured string \"{}\" from \"{}\": {}",
                               cap_string,
                               segment,
                               e);
                    });
                    let val: u64 = unsafe { transmute(pval) };

                    for feedback in &capture.feedbacks {
                        let shiftval = val << feedback.shift;
                        if feedback.fill {
                            let baseval = segvals[feedback.segment][index];
                            for _ in 0..(shiftval + feedback.fill_offset as u64) {
                                self.segments[feedback.segment].push(baseval);
                            }
                        } else {
                            segvals[feedback.segment][index] += shiftval;
                        }
                    }
                }
                for (segvec, segment) in segvals.iter_mut().zip(self.segments.iter_mut()) {
                    segment.append(segvec);
                }
                return true;
            }
        }
        false
    }
}
<|endoftext|>


<commit_before>#[derive(Copy, Clone, Debug)]
pub enum Error {
    InputTooShort,
    InputTooLong,
    MalformedInput,
}
<commit_msg>Add Display impl for parse error enum<commit_after>use core::fmt::{self, Display};

#[derive(Copy, Clone, Debug)]
pub enum Error {
    InputTooShort,
    InputTooLong,
    MalformedInput,
}

impl Display for Error {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        let msg = match self {
            Error::InputTooShort => "input too short",
            Error::InputTooLong => "input too long",
            Error::MalformedInput => "malformed input",
        };
        formatter.write_str(msg)
    }
}
<|endoftext|>


<commit_before>use serde::Deserialize;
use shakmaty::variant::Variant;

#[derive(Debug, Deserialize, Copy, Clone)]
pub enum LilaVariant {
    #[serde(alias = "antichess")]
    Antichess,
    #[serde(alias = "atomic")]
    Atomic,
    #[serde(alias = "chess960")]
    Chess960,
    #[serde(alias = "crazyhouse")]
    Crazyhouse,
    #[serde(alias = "fromPosition")]
    FromPosition,
    #[serde(alias = "horde")]
    Horde,
    #[serde(alias = "kingOfTheHill")]
    KingOfTheHill,
    #[serde(alias = "racingKings")]
    RacingKings,
    #[serde(alias = "chess", alias = "standard")]
    Standard,
    #[serde(alias = "threeCheck")]
    ThreeCheck,
}

impl Default for LilaVariant {
    fn default() -> LilaVariant {
        LilaVariant::Standard
    }
}

impl From<LilaVariant> for Variant {
    fn from(variant: LilaVariant) -> Variant {
        match variant {
            LilaVariant::Standard | LilaVariant::Chess960 | LilaVariant::FromPosition => {
                Variant::Chess
            }
            LilaVariant::Antichess => Variant::Antichess,
            LilaVariant::Atomic => Variant::Atomic,
            LilaVariant::Crazyhouse => Variant::Crazyhouse,
            LilaVariant::Horde => Variant::Horde,
            LilaVariant::KingOfTheHill => Variant::KingOfTheHill,
            LilaVariant::RacingKings => Variant::RacingKings,
            LilaVariant::ThreeCheck => Variant::ThreeCheck,
        }
    }
}
<commit_msg>add even more variant aliases<commit_after>use serde::Deserialize;
use shakmaty::variant::Variant;

#[derive(Debug, Deserialize, Copy, Clone)]
pub enum LilaVariant {
    #[serde(alias = "antichess")]
    Antichess,
    #[serde(alias = "atomic")]
    Atomic,
    #[serde(alias = "chess960")]
    Chess960,
    #[serde(alias = "crazyhouse")]
    Crazyhouse,
    #[serde(alias = "fromPosition", alias = "From Position")]
    FromPosition,
    #[serde(alias = "horde")]
    Horde,
    #[serde(alias = "kingOfTheHill", alias = "King of the Hill")]
    KingOfTheHill,
    #[serde(alias = "racingKings", alias = "Racing Kings")]
    RacingKings,
    #[serde(alias = "chess", alias = "standard")]
    Standard,
    #[serde(alias = "threeCheck", alias = "Three-check")]
    ThreeCheck,
}

impl Default for LilaVariant {
    fn default() -> LilaVariant {
        LilaVariant::Standard
    }
}

impl From<LilaVariant> for Variant {
    fn from(variant: LilaVariant) -> Variant {
        match variant {
            LilaVariant::Standard | LilaVariant::Chess960 | LilaVariant::FromPosition => {
                Variant::Chess
            }
            LilaVariant::Antichess => Variant::Antichess,
            LilaVariant::Atomic => Variant::Atomic,
            LilaVariant::Crazyhouse => Variant::Crazyhouse,
            LilaVariant::Horde => Variant::Horde,
            LilaVariant::KingOfTheHill => Variant::KingOfTheHill,
            LilaVariant::RacingKings => Variant::RacingKings,
            LilaVariant::ThreeCheck => Variant::ThreeCheck,
        }
    }
}
<|endoftext|>


<commit_before>use postgres;
use uuid::Uuid;
use std::fmt;
use chrono::{NaiveDateTime, Utc};

use apple_music;
use spotify;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::state::State;
use model::enclosure::Enclosure;
use model::track::Track;
use model::artist::Artist;

static PROPS: [&'static str; 14]  = ["id",
                                     "provider",
                                     "identifier",
                                     "owner_id",
                                     "owner_name",
                                     "url",
                                     "title",
                                     "description",
                                     "thumbnail_url",
                                     "artwork_url",
                                     "published_at",
                                     "created_at",
                                     "updated_at",
                                     "state"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Album {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub owner_id:      Option<String>,
    pub owner_name:    Option<String>,
    pub url:           String,
    pub title:         String,
    pub description:   Option<String>,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub published_at:  NaiveDateTime,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
    pub state:         State,
    pub tracks:        Vec<Track>,
    pub artists:       Option<Vec<Artist>>,
}

impl PartialEq for Album {
    fn eq(&self, p: &Album) -> bool {
        return self.identifier == p.identifier && self.provider == p.provider
    }
}

impl fmt::Display for Album {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl<'a> Model<'a> for Album {
    fn table_name() -> String { "albums".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }

    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Album> {
        let mut albums = Vec::new();
        for row in rows.iter() {
            let album = Album {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                owner_id:      row.get(3),
                owner_name:    row.get(4),
                url:           row.get(5),
                title:         row.get(6),
                description:   row.get(7),
                thumbnail_url: row.get(8),
                artwork_url:   row.get(9),
                published_at:  row.get(10),
                created_at:    row.get(11),
                updated_at:    row.get(12),
                state:         State::new(row.get(13)),
                tracks:        vec![],
                artists:       None,
            };
            albums.push(album)
        }
        albums
    }

    fn create(&self) -> Result<Album, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO albums (provider, identifier, url, title)
                                      VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.title]));
        let mut album = self.clone();
        for row in rows.iter() {
            album.id = row.get(0);
        }
        Ok(album)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = Utc::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE albums SET
                                      provider      = $2,
                                      identifier    = $3,
                                      owner_id      = $4,
                                      owner_name    = $5,
                                      url           = $6,
                                      title         = $7,
                                      description   = $8,
                                      thumbnail_url = $9,
                                      artwork_url   = $10,
                                      published_at  = $11,
                                      created_at    = $12,
                                      updated_at    = $13,
                                      state         = $14
                                      WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.owner_id,
                                  &self.owner_name,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.published_at,
                                  &self.created_at,
                                  &self.updated_at,
                                  &self.state.to_string(),
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl<'a> Enclosure<'a> for Album {
    fn new(provider: Provider, identifier: String) -> Album {
        Album {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            owner_id:      None,
            owner_name:    None,
            url:           "".to_string(),
            title:         "".to_string(),
            description:   None,
            thumbnail_url: None,
            artwork_url:   None,
            published_at:  Utc::now().naive_utc(),
            created_at:    Utc::now().naive_utc(),
            updated_at:    Utc::now().naive_utc(),
            state:         State::Alive,
            tracks:        vec![],
            artists:       None,
        }
    }
    fn set_url(&mut self, url: String) -> &mut Album {
        self.url = url;
        self
    }
    fn set_owner_id(&mut self, owner_id: Option<String>) -> &mut Album {
        self.owner_id = owner_id;
        self
    }
    fn fetch_props(&mut self) -> Result<(), Error> {
        match self.provider {
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_album(&self.identifier, &country) {
                    Ok(album) => self.update_with_am_album(&album),
                    Err(_)    => self.disable(),
                }
            }
            Provider::Spotify => match spotify::fetch_album(&self.identifier) {
                Ok(album) => self.update_with_sp_album(&album),
                Err(_)    => self.disable(),
            },
            _ => self,
        };
        match self.state {
            State::Alive => Ok(()),
            State::Dead  => Err(Error::NotFound),
        }
    }
    fn find_by_entry_id(entry_id: Uuid) -> Vec<Album> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM albums p LEFT JOIN album_entries pe
                        ON p.id = pe.album_id
                        WHERE pe.entry_id = $1
                        ORDER BY p.published_at DESC",
                     Album::props_str("p."))).unwrap();
        let rows = stmt.query(&[&entry_id]).unwrap();
        Album::rows_to_items(rows)
    }
}

impl Album {
    fn add_track(&mut self, track: &Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_tracks (track_id, album_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        Ok(())
    }
    fn add_artist(&mut self, artist: Artist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_artists (track_id, artist_id) VALUES ($1, $2)"));
        try!(stmt.query(&[&self.id, &artist.id]));
        match self.artists {
            Some(ref mut artists) => artists.push(artist),
            None                  => self.artists = Some(vec![artist]),

        }
        Ok(())
    }

    pub fn from_sp_album(album: &spotify::Album) -> Album {
        Album::new(Provider::Spotify, (*album).id.to_string())
            .update_with_sp_album(album)
            .clone()
    }

    pub fn from_am_album(album: &apple_music::Album) -> Album {
        Album::new(Provider::AppleMusic, (*album).id.to_string())
            .update_with_am_album(album)
            .clone()
    }

    fn add_tracks(&mut self, tracks: Vec<Track>) {
        self.tracks = tracks.iter().map(|t| {
            let mut t = t.clone();
            if let Ok(new_track) = Track::find_or_create(t.provider,
                                                         t.identifier.to_string()) {
                t.id      = new_track.id;
                let _     = self.add_track(&t);
            };
            t
        }).collect::<Vec<_>>();
    }

    pub fn update_with_sp_album(&mut self, album: &spotify::Album) -> &mut Album {
        self.provider       = Provider::Spotify;
        self.identifier     = album.id.to_string();
        if album.artists.len() > 0 {
            self.owner_id   = Some(album.artists[0].id.clone());
            self.owner_name = Some(album.artists[0].name.clone());
        }
        self.url            = album.uri.clone();
        self.title          = album.name.clone();
        self.description    = None;
        self.state          = State::Alive;
        self.published_at   = Utc::now().naive_utc();
        if album.images.len() > 0 {
            self.artwork_url   = Some(album.images[0].url.clone());
            self.thumbnail_url = Some(album.images[0].url.clone());
        }
        if album.images.len() > 1 {
            self.thumbnail_url = Some(album.images[1].url.clone());
        }
        let artists = album.artists
            .iter()
            .map(|artist| {
                Artist::find_or_create(self.provider, artist.id.to_string())
                    .map(|mut a| a.update_with_sp_artist(&artist).clone())
            })
            .filter(|r| r.is_ok())
            .map(|r| r.unwrap())
            .collect::<Vec<Artist>>();
        for mut a in artists {
            let _ = a.save();
            let _ = self.add_artist(a);
        }
        let tracks = album.tracks.clone()
            .map(|t| t.items).unwrap_or(vec![]).iter()
            .map(|ref t| Track::from_sp_track(t))
            .map(|ref mut t| t.update_with_sp_album(&album).clone())
            .collect::<Vec<_>>();
        self.add_tracks(tracks);
        self
    }

    pub fn update_with_am_album(&mut self, album: &apple_music::Album) -> &mut Album {
        self.provider      = Provider::AppleMusic;
        self.identifier    = album.id.to_string();
        self.owner_id      = Some(album.album_artist.to_string());
        self.owner_name    = Some(album.album_artist.to_string());
        self.url           = album.music_url.to_string();
        self.title         = album.title.to_string();
        self.description   = None;
        self.thumbnail_url = Some(album.artwork_url.to_string());
        self.artwork_url   = Some(album.artwork_url.to_string());
        self.state         = State::Alive;
        if let Ok(mut artist) = Artist::find_or_create(self.provider,
                                                       album.album_artist.to_string()) {
            artist.name  = album.album_artist.to_string();
            let _ = artist.save();
            let _ = self.add_artist(artist);
        }
        self
    }

    pub fn disable(&mut self) -> &mut Album {
        self.state = State::Dead;
        self
    }

    pub fn delete(&self) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("DELETE FROM albums WHERE id=$1"));
        let result = stmt.query(&[&self.id]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected)
        }
    }
}

#[cfg(test)]
mod test {
    use model::enclosure::Enclosure;
    use model::Model;
    use super::Album;
    use Provider;
    #[test]
    fn test_new() {
        let album = Album::new(Provider::YouTube,
                               "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string());
        assert_eq!(album.provider, Provider::YouTube);
        assert_eq!(&album.identifier, "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX")
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string();
        let result     = Album::find_or_create(Provider::YouTube, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_delete() {
        let identifier = "test_delete".to_string();
        let album      = Album::find_or_create(Provider::YouTube, identifier).unwrap();
        let result     = album.delete();
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        album.title   = "title".to_string();
        let result    = album.save();
        assert!(result.is_ok());
        let album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        assert_eq!(&album.title, "title");
    }
}
<commit_msg>Implement Album::find_all()<commit_after>use postgres;
use uuid::Uuid;
use std::fmt;
use chrono::{NaiveDateTime, Utc};

use apple_music;
use spotify;
use error::Error;
use super::{conn, Model};
use model::provider::Provider;
use model::state::State;
use model::enclosure::Enclosure;
use model::track::Track;
use model::artist::Artist;

static PROPS: [&'static str; 14]  = ["id",
                                     "provider",
                                     "identifier",
                                     "owner_id",
                                     "owner_name",
                                     "url",
                                     "title",
                                     "description",
                                     "thumbnail_url",
                                     "artwork_url",
                                     "published_at",
                                     "created_at",
                                     "updated_at",
                                     "state"];

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Album {
    pub id:            Uuid,
    pub provider:      Provider,
    pub identifier:    String,
    pub owner_id:      Option<String>,
    pub owner_name:    Option<String>,
    pub url:           String,
    pub title:         String,
    pub description:   Option<String>,
    pub thumbnail_url: Option<String>,
    pub artwork_url:   Option<String>,
    pub published_at:  NaiveDateTime,
    pub created_at:    NaiveDateTime,
    pub updated_at:    NaiveDateTime,
    pub state:         State,
    pub tracks:        Vec<Track>,
    pub artists:       Option<Vec<Artist>>,
}

impl PartialEq for Album {
    fn eq(&self, p: &Album) -> bool {
        return self.identifier == p.identifier && self.provider == p.provider
    }
}

impl fmt::Display for Album {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}:{}", self.provider, self.identifier)
    }
}

impl<'a> Model<'a> for Album {
    fn table_name() -> String { "albums".to_string() }
    fn props_str(prefix: &str) -> String {
        PROPS
            .iter()
            .map(|&p| format!("{}{}", prefix, p))
            .collect::<Vec<String>>().join(",")
    }

    fn rows_to_items(rows: postgres::rows::Rows) -> Vec<Album> {
        let mut albums = Vec::new();
        for row in rows.iter() {
            let album = Album {
                id:            row.get(0),
                provider:      Provider::new(row.get(1)),
                identifier:    row.get(2),
                owner_id:      row.get(3),
                owner_name:    row.get(4),
                url:           row.get(5),
                title:         row.get(6),
                description:   row.get(7),
                thumbnail_url: row.get(8),
                artwork_url:   row.get(9),
                published_at:  row.get(10),
                created_at:    row.get(11),
                updated_at:    row.get(12),
                state:         State::new(row.get(13)),
                tracks:        vec![],
                artists:       None,
            };
            albums.push(album)
        }
        albums
    }

    fn create(&self) -> Result<Album, Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO albums (provider, identifier, url, title)
                                      VALUES ($1, $2, $3, $4) RETURNING id"));
        let rows = try!(stmt.query(&[&self.provider.to_string(), &self.identifier, &self.url, &self.title]));
        let mut album = self.clone();
        for row in rows.iter() {
            album.id = row.get(0);
        }
        Ok(album)
    }

    fn save(&mut self) -> Result<(), Error> {
        self.updated_at = Utc::now().naive_utc();
        let conn = try!(conn());
        let stmt = try!(conn.prepare("UPDATE albums SET
                                      provider      = $2,
                                      identifier    = $3,
                                      owner_id      = $4,
                                      owner_name    = $5,
                                      url           = $6,
                                      title         = $7,
                                      description   = $8,
                                      thumbnail_url = $9,
                                      artwork_url   = $10,
                                      published_at  = $11,
                                      created_at    = $12,
                                      updated_at    = $13,
                                      state         = $14
                                      WHERE id = $1"));
        let result = stmt.query(&[&self.id,
                                  &self.provider.to_string(),
                                  &self.identifier,
                                  &self.owner_id,
                                  &self.owner_name,
                                  &self.url,
                                  &self.title,
                                  &self.description,
                                  &self.thumbnail_url,
                                  &self.artwork_url,
                                  &self.published_at,
                                  &self.created_at,
                                  &self.updated_at,
                                  &self.state.to_string(),
        ]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected),
        }
    }
}

impl<'a> Enclosure<'a> for Album {
    fn new(provider: Provider, identifier: String) -> Album {
        Album {
            id:            Uuid::new_v4(),
            provider:      provider,
            identifier:    identifier,
            owner_id:      None,
            owner_name:    None,
            url:           "".to_string(),
            title:         "".to_string(),
            description:   None,
            thumbnail_url: None,
            artwork_url:   None,
            published_at:  Utc::now().naive_utc(),
            created_at:    Utc::now().naive_utc(),
            updated_at:    Utc::now().naive_utc(),
            state:         State::Alive,
            tracks:        vec![],
            artists:       None,
        }
    }
    fn set_url(&mut self, url: String) -> &mut Album {
        self.url = url;
        self
    }
    fn set_owner_id(&mut self, owner_id: Option<String>) -> &mut Album {
        self.owner_id = owner_id;
        self
    }
    fn fetch_props(&mut self) -> Result<(), Error> {
        match self.provider {
            Provider::AppleMusic => {
                let country = apple_music::country(&self.url);
                match apple_music::fetch_album(&self.identifier, &country) {
                    Ok(album) => self.update_with_am_album(&album),
                    Err(_)    => self.disable(),
                }
            }
            Provider::Spotify => match spotify::fetch_album(&self.identifier) {
                Ok(album) => self.update_with_sp_album(&album),
                Err(_)    => self.disable(),
            },
            _ => self,
        };
        match self.state {
            State::Alive => Ok(()),
            State::Dead  => Err(Error::NotFound),
        }
    }
    fn find_by_entry_id(entry_id: Uuid) -> Vec<Album> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM albums p LEFT JOIN album_entries pe
                        ON p.id = pe.album_id
                        WHERE pe.entry_id = $1
                        ORDER BY p.published_at DESC",
                     Album::props_str("p."))).unwrap();
        let rows = stmt.query(&[&entry_id]).unwrap();
        Album::rows_to_items(rows)
    }
}

impl Album {
    pub fn find_all() -> Vec<Album> {
        let conn = conn().unwrap();
        let stmt = conn.prepare(
            &format!("SELECT {} FROM albums ORDER BY albums.published_at DESC",
                     Album::props_str(""))).unwrap();
        let rows = stmt.query(&[]).unwrap();
        Album::rows_to_items(rows)
    }
    fn add_track(&mut self, track: &Track) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO album_tracks (track_id, album_id)
                                 VALUES ($1, $2)"));
        try!(stmt.query(&[&track.id, &self.id]));
        Ok(())
    }
    fn add_artist(&mut self, artist: Artist) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("INSERT INTO track_artists (track_id, artist_id) VALUES ($1, $2)"));
        try!(stmt.query(&[&self.id, &artist.id]));
        match self.artists {
            Some(ref mut artists) => artists.push(artist),
            None                  => self.artists = Some(vec![artist]),

        }
        Ok(())
    }

    pub fn from_sp_album(album: &spotify::Album) -> Album {
        Album::new(Provider::Spotify, (*album).id.to_string())
            .update_with_sp_album(album)
            .clone()
    }

    pub fn from_am_album(album: &apple_music::Album) -> Album {
        Album::new(Provider::AppleMusic, (*album).id.to_string())
            .update_with_am_album(album)
            .clone()
    }

    fn add_tracks(&mut self, tracks: Vec<Track>) {
        self.tracks = tracks.iter().map(|t| {
            let mut t = t.clone();
            if let Ok(new_track) = Track::find_or_create(t.provider,
                                                         t.identifier.to_string()) {
                t.id      = new_track.id;
                let _     = self.add_track(&t);
            };
            t
        }).collect::<Vec<_>>();
    }

    pub fn update_with_sp_album(&mut self, album: &spotify::Album) -> &mut Album {
        self.provider       = Provider::Spotify;
        self.identifier     = album.id.to_string();
        if album.artists.len() > 0 {
            self.owner_id   = Some(album.artists[0].id.clone());
            self.owner_name = Some(album.artists[0].name.clone());
        }
        self.url            = album.uri.clone();
        self.title          = album.name.clone();
        self.description    = None;
        self.state          = State::Alive;
        self.published_at   = Utc::now().naive_utc();
        if album.images.len() > 0 {
            self.artwork_url   = Some(album.images[0].url.clone());
            self.thumbnail_url = Some(album.images[0].url.clone());
        }
        if album.images.len() > 1 {
            self.thumbnail_url = Some(album.images[1].url.clone());
        }
        let artists = album.artists
            .iter()
            .map(|artist| {
                Artist::find_or_create(self.provider, artist.id.to_string())
                    .map(|mut a| a.update_with_sp_artist(&artist).clone())
            })
            .filter(|r| r.is_ok())
            .map(|r| r.unwrap())
            .collect::<Vec<Artist>>();
        for mut a in artists {
            let _ = a.save();
            let _ = self.add_artist(a);
        }
        let tracks = album.tracks.clone()
            .map(|t| t.items).unwrap_or(vec![]).iter()
            .map(|ref t| Track::from_sp_track(t))
            .map(|ref mut t| t.update_with_sp_album(&album).clone())
            .collect::<Vec<_>>();
        self.add_tracks(tracks);
        self
    }

    pub fn update_with_am_album(&mut self, album: &apple_music::Album) -> &mut Album {
        self.provider      = Provider::AppleMusic;
        self.identifier    = album.id.to_string();
        self.owner_id      = Some(album.album_artist.to_string());
        self.owner_name    = Some(album.album_artist.to_string());
        self.url           = album.music_url.to_string();
        self.title         = album.title.to_string();
        self.description   = None;
        self.thumbnail_url = Some(album.artwork_url.to_string());
        self.artwork_url   = Some(album.artwork_url.to_string());
        self.state         = State::Alive;
        if let Ok(mut artist) = Artist::find_or_create(self.provider,
                                                       album.album_artist.to_string()) {
            artist.name  = album.album_artist.to_string();
            let _ = artist.save();
            let _ = self.add_artist(artist);
        }
        self
    }

    pub fn disable(&mut self) -> &mut Album {
        self.state = State::Dead;
        self
    }

    pub fn delete(&self) -> Result<(), Error> {
        let conn = try!(conn());
        let stmt = try!(conn.prepare("DELETE FROM albums WHERE id=$1"));
        let result = stmt.query(&[&self.id]);
        match result {
            Ok(_)  => Ok(()),
            Err(_) => Err(Error::Unexpected)
        }
    }
}

#[cfg(test)]
mod test {
    use model::enclosure::Enclosure;
    use model::Model;
    use super::Album;
    use Provider;
    #[test]
    fn test_new() {
        let album = Album::new(Provider::YouTube,
                               "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string());
        assert_eq!(album.provider, Provider::YouTube);
        assert_eq!(&album.identifier, "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX")
    }
    #[test]
    fn test_find_of_create() {
        let identifier = "PLy8LZ8FM-o0ViuGAF68RAaXkQ8V-3dbTX".to_string();
        let result     = Album::find_or_create(Provider::YouTube, identifier);
        assert!(result.is_ok());
    }
    #[test]
    fn test_delete() {
        let identifier = "test_delete".to_string();
        let album      = Album::find_or_create(Provider::YouTube, identifier).unwrap();
        let result     = album.delete();
        assert!(result.is_ok());
    }
    #[test]
    fn test_save() {
        let id = "test_save";
        let mut album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        album.title   = "title".to_string();
        let result    = album.save();
        assert!(result.is_ok());
        let album = Album::find_or_create(Provider::YouTube, id.to_string()).unwrap();
        assert_eq!(&album.title, "title");
    }
}
<|endoftext|>


<commit_before>use crate::StrError;

/// Computes the point-to-point distance
pub fn point_point_distance(a: &[f64], b: &[f64]) -> Result<f64, StrError> {
    let ndim = a.len();
    if ndim < 2 || ndim > 3 {
        return Err("a.len() == ndim must be 2 or 3");
    }
    if b.len() != ndim {
        return Err("b.len() must equal a.len() == ndim");
    }
    let distance: f64;
    if ndim == 2 {
        distance = f64::sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]));
    } else {
        distance =
            f64::sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + (b[2] - a[2]) * (b[2] - a[2]));
    }
    Ok(distance)
}

/// Computes the distance between a point and a segment
///
/// ```text
///                 c
///                /^
/// ⇀             / | ⇀   ⇀   ⇀
/// x = vec(c-a) /  | q = x - p
///             /   |
///            /    |
///           a----->-------------b
///             ⇀              ⇀
///             p = projection(x)
///
///           a-------------------b
///               ⇀
///               n = vec(b-c)
/// ```
///
/// ```text
/// x := c - a
/// n := b - a
/// p := ((x ⋅ n) / (n ⋅ n)) * n
/// q := x - p
/// distance := norm(q)
/// ```
pub fn point_segment_distance(a: &[f64], b: &[f64], c: &[f64]) -> Result<f64, StrError> {
    let ndim = a.len();
    if ndim < 2 || ndim > 3 {
        return Err("a.len() == ndim must be 2 or 3");
    }
    if b.len() != ndim {
        return Err("b.len() must equal a.len() == ndim");
    }
    if c.len() != ndim {
        return Err("c.len() must equal a.len() == ndim");
    }
    let distance: f64;
    if ndim == 2 {
        // n ⋅ n  with n = b - a
        let n_dot_n = (b[0] - a[0]) + (b[1] - a[1]);
        if n_dot_n <= f64::EPSILON {
            return Err("segment is too short");
        }
        // x ⋅ n  with x = c - a
        let x_dot_n = (c[0] - a[0]) * (b[0] - a[0]) + (c[1] - a[1]) * (b[1] - a[1]);
        // q = x - p = (c-a) - (b-a) * (x⋅n)/(n⋅n)
        let q0 = (c[0] - a[0]) - (b[0] - a[0]) * x_dot_n / n_dot_n;
        let q1 = (c[1] - a[1]) - (b[1] - a[1]) * x_dot_n / n_dot_n;
        // norm(q)
        distance = f64::sqrt(q0 * q0 + q1 * q1);
    } else {
        // n ⋅ n  with n = b - a
        let n_dot_n = (b[0] - a[0]) + (b[1] - a[1]) + (b[2] - a[2]);
        if n_dot_n <= f64::EPSILON {
            return Err("segment is too short");
        }
        // x ⋅ n  with x = c - a
        let x_dot_n = (c[0] - a[0]) * (b[0] - a[0]) + (c[1] - a[1]) * (b[1] - a[1]) + (c[2] - a[2]) * (b[2] - a[2]);
        // q = x - p = (c-a) - (b-a) * (x⋅n)/(n⋅n)
        let q0 = (c[0] - a[0]) - (b[0] - a[0]) * x_dot_n / n_dot_n;
        let q1 = (c[1] - a[1]) - (b[1] - a[1]) * x_dot_n / n_dot_n;
        let q2 = (c[2] - a[2]) - (b[2] - a[2]) * x_dot_n / n_dot_n;
        // norm(q)
        distance = f64::sqrt(q0 * q0 + q1 * q1 + q2 * q2);
    }
    Ok(distance)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::{SQRT_2, SQRT_2_BY_3, SQRT_3};
    use russell_chk::assert_approx_eq;

    #[test]
    fn point_point_distance_fails_on_wrong_input() {
        assert_eq!(
            point_point_distance(&[0.0], &[1.0, 1.0]).err(),
            Some("a.len() == ndim must be 2 or 3")
        );
        assert_eq!(
            point_point_distance(&[0.0, 0.0], &[1.0]).err(),
            Some("b.len() must equal a.len() == ndim")
        );
    }

    #[test]
    fn point_point_distance_2d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0];
        let b = &[1.0, 0.0];
        assert_approx_eq!(point_point_distance(a, b)?, 1.0, 1e-15);

        let b = &[0.0, 2.0];
        assert_approx_eq!(point_point_distance(a, b)?, 2.0, 1e-15);

        let a = &[1.0, 1.0];
        let b = &[2.0, 2.0];
        assert_approx_eq!(point_point_distance(a, b)?, SQRT_2, 1e-15);
        Ok(())
    }

    #[test]
    fn point_point_distance_3d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0, 0.0];
        let b = &[1.0, 0.0, 0.0];
        assert_approx_eq!(point_point_distance(a, b)?, 1.0, 1e-15);

        let b = &[0.0, 2.0, 0.0];
        assert_approx_eq!(point_point_distance(a, b)?, 2.0, 1e-15);

        let b = &[0.0, 0.0, 3.0];
        assert_approx_eq!(point_point_distance(a, b)?, 3.0, 1e-15);

        let a = &[1.0, 1.0, 1.0];
        let b = &[2.0, 2.0, 2.0];
        assert_approx_eq!(point_point_distance(a, b)?, SQRT_3, 1e-15);
        Ok(())
    }

    #[test]
    fn point_segment_distance_fails_on_wrong_input() {
        assert_eq!(
            point_segment_distance(&[0.0], &[1.0, 1.0], &[2.0, 2.0]).err(),
            Some("a.len() == ndim must be 2 or 3")
        );
        assert_eq!(
            point_segment_distance(&[0.0, 0.0], &[1.0], &[2.0, 2.0]).err(),
            Some("b.len() must equal a.len() == ndim")
        );
        assert_eq!(
            point_segment_distance(&[0.0, 0.0], &[1.0, 1.0], &[2.0]).err(),
            Some("c.len() must equal a.len() == ndim")
        );
    }

    #[test]
    fn point_segment_distance_2d_fails_on_short_segment() -> Result<(), StrError> {
        let a = &[0.0, 0.0];
        let b = &[0.0, f64::EPSILON];
        let c = &[1.0, 1.0];
        assert_eq!(point_segment_distance(a, b, c).err(), Some("segment is too short"));
        Ok(())
    }

    #[test]
    fn point_segment_distance_3d_fails_on_short_segment() -> Result<(), StrError> {
        let a = &[0.0, 0.0, 0.0];
        let b = &[0.0, 0.0, f64::EPSILON];
        let c = &[1.0, 1.0, 1.0];
        assert_eq!(point_segment_distance(a, b, c).err(), Some("segment is too short"));
        Ok(())
    }

    #[test]
    fn point_segment_distance_2d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0];
        let b = &[1.0, 1.0];
        let c = &a.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &b.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &[1.0, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2 / 2.0, 1e-15);

        let c = &[0.0, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2 / 2.0, 1e-15);

        let c = &[0.5, 0.5];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let a = &[0.0, 0.0];
        let b = &[1.0, 0.0];
        let c = &[1.0, 0.5];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[0.0, 0.0];
        let b = &[0.0, 1.0];
        let c = &[0.5, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);
        Ok(())
    }

    #[test]
    fn point_segment_distance_3d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0, 0.0];
        let b = &[1.0, 1.0, 1.0];
        let c = &a.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &b.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &[1.0, 1.0, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2_BY_3, 1e-15);

        let c = &[0.0, 1.0, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2_BY_3, 1e-15);

        let c = &[1.0, 0.0, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2_BY_3, 1e-15);

        let c = &[0.5, 0.5, 0.5];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let a = &[0.0, 0.0, 0.0];
        let b = &[1.0, 0.0, 0.0];
        let c = &[1.0, 0.5, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[0.0, 0.0, 0.0];
        let b = &[0.0, 1.0, 0.0];
        let c = &[0.5, 1.0, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[0.0, 0.0, 0.0];
        let b = &[0.0, 0.0, 1.0];
        let c = &[0.0, 0.5, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);
        Ok(())
    }
}
<commit_msg>Fix bug: n_dot_n was incorrect<commit_after>use crate::StrError;

/// Computes the point-to-point distance
pub fn point_point_distance(a: &[f64], b: &[f64]) -> Result<f64, StrError> {
    let ndim = a.len();
    if ndim < 2 || ndim > 3 {
        return Err("a.len() == ndim must be 2 or 3");
    }
    if b.len() != ndim {
        return Err("b.len() must equal a.len() == ndim");
    }
    let distance: f64;
    if ndim == 2 {
        distance = f64::sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]));
    } else {
        distance =
            f64::sqrt((b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + (b[2] - a[2]) * (b[2] - a[2]));
    }
    Ok(distance)
}

/// Computes the distance between a point and a segment
///
/// ```text
///                 c
///                /^
/// ⇀             / | ⇀   ⇀   ⇀
/// x = vec(c-a) /  | q = x - p
///             /   |
///            /    |
///           a----->-------------b
///             ⇀              ⇀
///             p = projection(x)
///
///           a-------------------b
///               ⇀
///               n = vec(b-c)
/// ```
///
/// ```text
/// x := c - a
/// n := b - a
/// p := ((x ⋅ n) / (n ⋅ n)) * n
/// q := x - p
/// distance := norm(q)
/// ```
pub fn point_segment_distance(a: &[f64], b: &[f64], c: &[f64]) -> Result<f64, StrError> {
    let ndim = a.len();
    if ndim < 2 || ndim > 3 {
        return Err("a.len() == ndim must be 2 or 3");
    }
    if b.len() != ndim {
        return Err("b.len() must equal a.len() == ndim");
    }
    if c.len() != ndim {
        return Err("c.len() must equal a.len() == ndim");
    }
    let distance: f64;
    if ndim == 2 {
        // n ⋅ n  with n = b - a
        let n_dot_n = (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]);
        if n_dot_n <= f64::EPSILON {
            return Err("segment is too short");
        }
        // x ⋅ n  with x = c - a
        let x_dot_n = (c[0] - a[0]) * (b[0] - a[0]) + (c[1] - a[1]) * (b[1] - a[1]);
        // q = x - p = (c-a) - (b-a) * (x⋅n)/(n⋅n)
        let q0 = (c[0] - a[0]) - (b[0] - a[0]) * x_dot_n / n_dot_n;
        let q1 = (c[1] - a[1]) - (b[1] - a[1]) * x_dot_n / n_dot_n;
        // norm(q)
        distance = f64::sqrt(q0 * q0 + q1 * q1);
    } else {
        // n ⋅ n  with n = b - a
        let n_dot_n = (b[0] - a[0]) * (b[0] - a[0]) + (b[1] - a[1]) * (b[1] - a[1]) + (b[2] - a[2]) * (b[2] - a[2]);
        if n_dot_n <= f64::EPSILON {
            return Err("segment is too short");
        }
        // x ⋅ n  with x = c - a
        let x_dot_n = (c[0] - a[0]) * (b[0] - a[0]) + (c[1] - a[1]) * (b[1] - a[1]) + (c[2] - a[2]) * (b[2] - a[2]);
        // q = x - p = (c-a) - (b-a) * (x⋅n)/(n⋅n)
        let q0 = (c[0] - a[0]) - (b[0] - a[0]) * x_dot_n / n_dot_n;
        let q1 = (c[1] - a[1]) - (b[1] - a[1]) * x_dot_n / n_dot_n;
        let q2 = (c[2] - a[2]) - (b[2] - a[2]) * x_dot_n / n_dot_n;
        // norm(q)
        distance = f64::sqrt(q0 * q0 + q1 * q1 + q2 * q2);
    }
    Ok(distance)
}

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

#[cfg(test)]
mod tests {
    use super::*;
    use crate::util::{SQRT_2, SQRT_2_BY_3, SQRT_3};
    use russell_chk::assert_approx_eq;

    #[test]
    fn point_point_distance_fails_on_wrong_input() {
        assert_eq!(
            point_point_distance(&[0.0], &[1.0, 1.0]).err(),
            Some("a.len() == ndim must be 2 or 3")
        );
        assert_eq!(
            point_point_distance(&[0.0, 0.0], &[1.0]).err(),
            Some("b.len() must equal a.len() == ndim")
        );
    }

    #[test]
    fn point_point_distance_2d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0];
        let b = &[1.0, 0.0];
        assert_approx_eq!(point_point_distance(a, b)?, 1.0, 1e-15);

        let b = &[0.0, 2.0];
        assert_approx_eq!(point_point_distance(a, b)?, 2.0, 1e-15);

        let a = &[1.0, 1.0];
        let b = &[2.0, 2.0];
        assert_approx_eq!(point_point_distance(a, b)?, SQRT_2, 1e-15);
        Ok(())
    }

    #[test]
    fn point_point_distance_3d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0, 0.0];
        let b = &[1.0, 0.0, 0.0];
        assert_approx_eq!(point_point_distance(a, b)?, 1.0, 1e-15);

        let b = &[0.0, 2.0, 0.0];
        assert_approx_eq!(point_point_distance(a, b)?, 2.0, 1e-15);

        let b = &[0.0, 0.0, 3.0];
        assert_approx_eq!(point_point_distance(a, b)?, 3.0, 1e-15);

        let a = &[1.0, 1.0, 1.0];
        let b = &[2.0, 2.0, 2.0];
        assert_approx_eq!(point_point_distance(a, b)?, SQRT_3, 1e-15);
        Ok(())
    }

    #[test]
    fn point_segment_distance_fails_on_wrong_input() {
        assert_eq!(
            point_segment_distance(&[0.0], &[1.0, 1.0], &[2.0, 2.0]).err(),
            Some("a.len() == ndim must be 2 or 3")
        );
        assert_eq!(
            point_segment_distance(&[0.0, 0.0], &[1.0], &[2.0, 2.0]).err(),
            Some("b.len() must equal a.len() == ndim")
        );
        assert_eq!(
            point_segment_distance(&[0.0, 0.0], &[1.0, 1.0], &[2.0]).err(),
            Some("c.len() must equal a.len() == ndim")
        );
    }

    #[test]
    fn point_segment_distance_2d_fails_on_short_segment() -> Result<(), StrError> {
        let a = &[0.0, 0.0];
        let b = &[0.0, f64::EPSILON];
        let c = &[1.0, 1.0];
        assert_eq!(point_segment_distance(a, b, c).err(), Some("segment is too short"));
        Ok(())
    }

    #[test]
    fn point_segment_distance_3d_fails_on_short_segment() -> Result<(), StrError> {
        let a = &[0.0, 0.0, 0.0];
        let b = &[0.0, 0.0, f64::EPSILON];
        let c = &[1.0, 1.0, 1.0];
        assert_eq!(point_segment_distance(a, b, c).err(), Some("segment is too short"));
        Ok(())
    }

    #[test]
    fn point_segment_distance_2d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0];
        let b = &[2.0, 2.0];
        let c = &a.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &b.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &[1.0, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2 / 2.0, 1e-15);

        let c = &[0.0, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2 / 2.0, 1e-15);

        let c = &[0.5, 0.5];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let a = &[0.0, 0.0];
        let b = &[8.0, 0.0];
        let c = &[1.0, 0.5];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[0.0, 0.0];
        let b = &[0.0, 3.0];
        let c = &[0.5, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[0.6, 0.0];
        let b = &[0.6, 1.8];
        let c = &[0.3, 1.2];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.3);
        Ok(())
    }

    #[test]
    fn point_segment_distance_3d_works() -> Result<(), StrError> {
        let a = &[0.0, 0.0, 0.0];
        let b = &[2.0, 2.0, 2.0];
        let c = &a.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &b.clone();
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let c = &[1.0, 1.0, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2_BY_3, 1e-15);

        let c = &[0.0, 1.0, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2_BY_3, 1e-15);

        let c = &[1.0, 0.0, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_approx_eq!(distance, SQRT_2_BY_3, 1e-15);

        let c = &[0.5, 0.5, 0.5];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.0);

        let a = &[0.0, 0.0, 0.0];
        let b = &[2.0, 0.0, 0.0];
        let c = &[1.0, 0.5, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[0.0, 0.0, 0.0];
        let b = &[0.0, 3.0, 0.0];
        let c = &[0.5, 1.0, 0.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[0.0, 0.0, 0.0];
        let b = &[0.0, 0.0, 4.0];
        let c = &[0.0, 0.5, 1.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 0.5);

        let a = &[1.0, 2.0, 1.0];
        let b = &[1.0, 2.0, 5.0];
        let c = &[1.0, 1.0, 2.0];
        let distance = point_segment_distance(a, b, c)?;
        assert_eq!(distance, 1.0);
        Ok(())
    }
}
<|endoftext|>