<commit_before>use text_format::lexer::Lexer;
use text_format::lexer::TokenWithLocation;
use text_format::lexer::ParserLanguage;
use text_format::lexer::Loc;
use text_format::lexer::Token;
use text_format::lexer::LexerError;
use text_format::lexer::StrLit;
use text_format::lexer::StrLitDecodeError;

#[derive(Debug)]
pub enum TokenizerError {
    LexerError(LexerError),
    StrLitDecodeError(StrLitDecodeError),
    InternalError,
    IncorrectInput, // TODO: too broad
    UnexpectedEof,
    ExpectStrLit,
    ExpectIntLit,
    ExpectFloatLit,
    ExpectIdent,
    ExpectNamedIdent(String),
    ExpectChar(char),
}

pub type TokenizerResult<R> = Result<R, TokenizerError>;

impl From<LexerError> for TokenizerError {
    fn from(e: LexerError) -> Self {
        TokenizerError::LexerError(e)
    }
}

impl From<StrLitDecodeError> for TokenizerError {
    fn from(e: StrLitDecodeError) -> Self {
        TokenizerError::StrLitDecodeError(e)
    }
}

#[derive(Clone)]
pub struct Tokenizer<'a> {
    lexer: Lexer<'a>,
    next_token: Option<TokenWithLocation>,
}

impl<'a> Tokenizer<'a> {
    pub fn new(input: &'a str, comment_style: ParserLanguage) -> Tokenizer<'a> {
        Tokenizer {
            lexer: Lexer::new(input, comment_style),
            next_token: None,
        }
    }

    pub fn loc(&self) -> Loc {
        // TODO: last known token loc
        self.next_token.clone().map_or(self.lexer.loc, |n| n.loc)
    }

    fn lookahead(&mut self) -> TokenizerResult<Option<&Token>> {
        Ok(match self.next_token {
            Some(ref token) => Some(&token.token),
            None => {
                self.next_token = self.lexer.next_token()?;
                match self.next_token {
                    Some(ref token) => Some(&token.token),
                    None => None,
                }
            }
        })
    }

    pub fn lookahead_some(&mut self) -> TokenizerResult<&Token> {
        match self.lookahead()? {
            Some(token) => Ok(token),
            None => Err(TokenizerError::UnexpectedEof),
        }
    }

    fn next(&mut self) -> TokenizerResult<Option<Token>> {
        self.lookahead()?;
        Ok(self.next_token.take().map(|TokenWithLocation { token, .. }| token))
    }

    pub fn next_some(&mut self) -> TokenizerResult<Token> {
        match self.next()? {
            Some(token) => Ok(token),
            None => Err(TokenizerError::UnexpectedEof),
        }
    }

    /// Can be called only after lookahead, otherwise it's error
    pub fn advance(&mut self) -> TokenizerResult<Token> {
        self.next_token.take()
            .map(|TokenWithLocation { token, .. }| token)
            .ok_or(TokenizerError::InternalError)
    }

    /// No more tokens
    pub fn syntax_eof(&mut self) -> TokenizerResult<bool> {
        Ok(self.lookahead()?.is_none())
    }

    pub fn next_token_if_map<P, R>(&mut self, p: P) -> TokenizerResult<Option<R>>
        where P : FnOnce(&Token) -> Option<R>
    {
        self.lookahead()?;
        let v = match self.next_token {
            Some(ref token) => {
                match p(&token.token) {
                    Some(v) => v,
                    None => return Ok(None),
                }
            }
            _ => return Ok(None),
        };
        self.next_token = None;
        Ok(Some(v))
    }

    pub fn next_token_check_map<P, R, E>(&mut self, p: P) -> Result<R, E>
        where
            P : FnOnce(&Token) -> Result<R, E>,
            E : From<TokenizerError>,
    {
        self.lookahead()?;
        let r = match self.next_token {
            Some(ref token) => p(&token.token)?,
            None => return Err(TokenizerError::UnexpectedEof.into()),
        };
        self.next_token = None;
        Ok(r)
    }

    fn next_token_if<P>(&mut self, p: P) -> TokenizerResult<Option<Token>>
        where P : FnOnce(&Token) -> bool
    {
        self.next_token_if_map(|token| if p(token) { Some(token.clone()) } else { None })
    }

    pub fn next_ident_if_in(&mut self, idents: &[&str]) -> TokenizerResult<Option<String>> {
        let v = match self.lookahead()? {
            Some(&Token::Ident(ref next)) => {
                if idents.into_iter().find(|&i| i == next).is_some() {
                    next.clone()
                } else {
                    return Ok(None);
                }
            }
            _ => return Ok(None),
        };
        self.advance()?;
        Ok(Some(v))
    }

    pub fn next_ident_if_eq(&mut self, word: &str) -> TokenizerResult<bool> {
        Ok(self.next_ident_if_in(&[word])? != None)
    }

    pub fn next_ident_expect_eq(&mut self, word: &str) -> TokenizerResult<()> {
        if self.next_ident_if_eq(word)? {
            Ok(())
        } else {
            Err(TokenizerError::ExpectNamedIdent(word.to_owned()))
        }
    }

    pub fn next_ident_if_eq_error(&mut self, word: &str) -> TokenizerResult<()> {
        if self.clone().next_ident_if_eq(word)? {
            return Err(TokenizerError::IncorrectInput);
        }
        Ok(())
    }

    pub fn next_symbol_if_eq(&mut self, symbol: char) -> TokenizerResult<bool> {
        Ok(self.next_token_if(|token| match token {
            &Token::Symbol(c) if c == symbol => true,
            _ => false,
        })? != None)
    }

    pub fn next_symbol_expect_eq(&mut self, symbol: char) -> TokenizerResult<()> {
        if self.lookahead_is_symbol(symbol)? {
            self.advance()?;
            Ok(())
        } else {
            Err(TokenizerError::ExpectChar(symbol))
        }
    }

    pub fn lookahead_is_str_lit(&mut self) -> TokenizerResult<bool> {
        Ok(match self.lookahead()? {
            Some(&Token::StrLit(..)) => true,
            _ => false,
        })
    }

    pub fn lookahead_is_int_lit(&mut self) -> TokenizerResult<bool> {
        Ok(match self.lookahead()? {
            Some(&Token::IntLit(..)) => true,
            _ => false,
        })
    }

    pub fn lookahead_is_json_number(&mut self) -> TokenizerResult<bool> {
        Ok(match self.lookahead()? {
            Some(&Token::JsonNumber(..)) => true,
            _ => false,
        })
    }

    pub fn lookahead_if_symbol(&mut self) -> TokenizerResult<Option<char>> {
        Ok(match self.lookahead()? {
            Some(&Token::Symbol(c)) => Some(c),
            _ => None,
        })
    }

    pub fn lookahead_is_symbol(&mut self, symbol: char) -> TokenizerResult<bool> {
        Ok(self.lookahead_if_symbol()? == Some(symbol))
    }

    pub fn next_ident(&mut self) -> TokenizerResult<String> {
        self.next_token_check_map(|token| {
            match token {
                &Token::Ident(ref ident) => Ok(ident.clone()),
                _ => Err(TokenizerError::ExpectIdent),
            }
        })
    }

    pub fn next_str_lit(&mut self) -> TokenizerResult<StrLit> {
        self.next_token_check_map(|token| {
            match token {
                &Token::StrLit(ref str_lit) => Ok(str_lit.clone()),
                _ => Err(TokenizerError::ExpectStrLit),
            }
        })
    }

    pub fn next_int_lit(&mut self) -> TokenizerResult<u64> {
        self.next_token_check_map(|token| {
            match token {
                &Token::IntLit(v) => Ok(v),
                _ => Err(TokenizerError::ExpectIntLit),
            }
        })
    }

    pub fn next_float_lit(&mut self) -> TokenizerResult<f64> {
        self.next_token_check_map(|token| {
            match token {
                &Token::FloatLit(v) => Ok(v),
                _ => Err(TokenizerError::ExpectFloatLit),
            }
        })
    }

}

#[cfg(test)]
mod test {

    use super::*;

    fn tokenize<P, R>(input: &str, what: P) -> R
        where P : FnOnce(&mut Tokenizer) -> TokenizerResult<R>
    {
        let mut tokenizer = Tokenizer::new(input, ParserLanguage::Proto);
        let r = what(&mut tokenizer)
            .expect(&format!("parse failed at {}", tokenizer.loc()));
        let eof = tokenizer.syntax_eof().expect(&format!("check eof failed at {}", tokenizer.loc()));
        assert!(eof, "{}", tokenizer.loc());
        r
    }

    #[test]
    fn test_ident() {
        let msg = r#"  aabb_c  "#;
        let mess = tokenize(msg, |p| p.next_ident().map(|s| s.to_owned()));
        assert_eq!("aabb_c", mess);
    }

    #[test]
    fn test_str_lit() {
        let msg = r#"  "a\nb"  "#;
        let mess = tokenize(msg, |p| p.next_str_lit());
        assert_eq!(StrLit { escaped: r#"a\nb"#.to_owned() }, mess);
    }

}
<commit_msg>Tokenizer better location reporting<commit_after>use text_format::lexer::Lexer;
use text_format::lexer::TokenWithLocation;
use text_format::lexer::ParserLanguage;
use text_format::lexer::Loc;
use text_format::lexer::Token;
use text_format::lexer::LexerError;
use text_format::lexer::StrLit;
use text_format::lexer::StrLitDecodeError;

#[derive(Debug)]
pub enum TokenizerError {
    LexerError(LexerError),
    StrLitDecodeError(StrLitDecodeError),
    InternalError,
    IncorrectInput, // TODO: too broad
    UnexpectedEof,
    ExpectStrLit,
    ExpectIntLit,
    ExpectFloatLit,
    ExpectIdent,
    ExpectNamedIdent(String),
    ExpectChar(char),
}

pub type TokenizerResult<R> = Result<R, TokenizerError>;

impl From<LexerError> for TokenizerError {
    fn from(e: LexerError) -> Self {
        TokenizerError::LexerError(e)
    }
}

impl From<StrLitDecodeError> for TokenizerError {
    fn from(e: StrLitDecodeError) -> Self {
        TokenizerError::StrLitDecodeError(e)
    }
}

#[derive(Clone)]
pub struct Tokenizer<'a> {
    lexer: Lexer<'a>,
    next_token: Option<TokenWithLocation>,
    last_token_loc: Option<Loc>,
}

impl<'a> Tokenizer<'a> {
    pub fn new(input: &'a str, comment_style: ParserLanguage) -> Tokenizer<'a> {
        Tokenizer {
            lexer: Lexer::new(input, comment_style),
            next_token: None,
            last_token_loc: None,
        }
    }

    pub fn loc(&self) -> Loc {
        // After lookahead return the location of the next token
        self.next_token.as_ref().map(|t| t.loc.clone())
            // After token consumed return the location of that token
            .or(self.last_token_loc.clone())
            // Otherwise return the position of lexer
            .unwrap_or(self.lexer.loc)
    }

    fn lookahead(&mut self) -> TokenizerResult<Option<&Token>> {
        Ok(match self.next_token {
            Some(ref token) => Some(&token.token),
            None => {
                self.next_token = self.lexer.next_token()?;
                self.last_token_loc = self.next_token.as_ref().map(|t| t.loc.clone());
                match self.next_token {
                    Some(ref token) => Some(&token.token),
                    None => None,
                }
            }
        })
    }

    pub fn lookahead_some(&mut self) -> TokenizerResult<&Token> {
        match self.lookahead()? {
            Some(token) => Ok(token),
            None => Err(TokenizerError::UnexpectedEof),
        }
    }

    fn next(&mut self) -> TokenizerResult<Option<Token>> {
        self.lookahead()?;
        Ok(self.next_token.take().map(|TokenWithLocation { token, .. }| token))
    }

    pub fn next_some(&mut self) -> TokenizerResult<Token> {
        match self.next()? {
            Some(token) => Ok(token),
            None => Err(TokenizerError::UnexpectedEof),
        }
    }

    /// Can be called only after lookahead, otherwise it's error
    pub fn advance(&mut self) -> TokenizerResult<Token> {
        self.next_token.take()
            .map(|TokenWithLocation { token, .. }| token)
            .ok_or(TokenizerError::InternalError)
    }

    /// No more tokens
    pub fn syntax_eof(&mut self) -> TokenizerResult<bool> {
        Ok(self.lookahead()?.is_none())
    }

    pub fn next_token_if_map<P, R>(&mut self, p: P) -> TokenizerResult<Option<R>>
        where P : FnOnce(&Token) -> Option<R>
    {
        self.lookahead()?;
        let v = match self.next_token {
            Some(ref token) => {
                match p(&token.token) {
                    Some(v) => v,
                    None => return Ok(None),
                }
            }
            _ => return Ok(None),
        };
        self.next_token = None;
        Ok(Some(v))
    }

    pub fn next_token_check_map<P, R, E>(&mut self, p: P) -> Result<R, E>
        where
            P : FnOnce(&Token) -> Result<R, E>,
            E : From<TokenizerError>,
    {
        self.lookahead()?;
        let r = match self.next_token {
            Some(ref token) => p(&token.token)?,
            None => return Err(TokenizerError::UnexpectedEof.into()),
        };
        self.next_token = None;
        Ok(r)
    }

    fn next_token_if<P>(&mut self, p: P) -> TokenizerResult<Option<Token>>
        where P : FnOnce(&Token) -> bool
    {
        self.next_token_if_map(|token| if p(token) { Some(token.clone()) } else { None })
    }

    pub fn next_ident_if_in(&mut self, idents: &[&str]) -> TokenizerResult<Option<String>> {
        let v = match self.lookahead()? {
            Some(&Token::Ident(ref next)) => {
                if idents.into_iter().find(|&i| i == next).is_some() {
                    next.clone()
                } else {
                    return Ok(None);
                }
            }
            _ => return Ok(None),
        };
        self.advance()?;
        Ok(Some(v))
    }

    pub fn next_ident_if_eq(&mut self, word: &str) -> TokenizerResult<bool> {
        Ok(self.next_ident_if_in(&[word])? != None)
    }

    pub fn next_ident_expect_eq(&mut self, word: &str) -> TokenizerResult<()> {
        if self.next_ident_if_eq(word)? {
            Ok(())
        } else {
            Err(TokenizerError::ExpectNamedIdent(word.to_owned()))
        }
    }

    pub fn next_ident_if_eq_error(&mut self, word: &str) -> TokenizerResult<()> {
        if self.clone().next_ident_if_eq(word)? {
            return Err(TokenizerError::IncorrectInput);
        }
        Ok(())
    }

    pub fn next_symbol_if_eq(&mut self, symbol: char) -> TokenizerResult<bool> {
        Ok(self.next_token_if(|token| match token {
            &Token::Symbol(c) if c == symbol => true,
            _ => false,
        })? != None)
    }

    pub fn next_symbol_expect_eq(&mut self, symbol: char) -> TokenizerResult<()> {
        if self.lookahead_is_symbol(symbol)? {
            self.advance()?;
            Ok(())
        } else {
            Err(TokenizerError::ExpectChar(symbol))
        }
    }

    pub fn lookahead_is_str_lit(&mut self) -> TokenizerResult<bool> {
        Ok(match self.lookahead()? {
            Some(&Token::StrLit(..)) => true,
            _ => false,
        })
    }

    pub fn lookahead_is_int_lit(&mut self) -> TokenizerResult<bool> {
        Ok(match self.lookahead()? {
            Some(&Token::IntLit(..)) => true,
            _ => false,
        })
    }

    pub fn lookahead_is_json_number(&mut self) -> TokenizerResult<bool> {
        Ok(match self.lookahead()? {
            Some(&Token::JsonNumber(..)) => true,
            _ => false,
        })
    }

    pub fn lookahead_if_symbol(&mut self) -> TokenizerResult<Option<char>> {
        Ok(match self.lookahead()? {
            Some(&Token::Symbol(c)) => Some(c),
            _ => None,
        })
    }

    pub fn lookahead_is_symbol(&mut self, symbol: char) -> TokenizerResult<bool> {
        Ok(self.lookahead_if_symbol()? == Some(symbol))
    }

    pub fn next_ident(&mut self) -> TokenizerResult<String> {
        self.next_token_check_map(|token| {
            match token {
                &Token::Ident(ref ident) => Ok(ident.clone()),
                _ => Err(TokenizerError::ExpectIdent),
            }
        })
    }

    pub fn next_str_lit(&mut self) -> TokenizerResult<StrLit> {
        self.next_token_check_map(|token| {
            match token {
                &Token::StrLit(ref str_lit) => Ok(str_lit.clone()),
                _ => Err(TokenizerError::ExpectStrLit),
            }
        })
    }

    pub fn next_int_lit(&mut self) -> TokenizerResult<u64> {
        self.next_token_check_map(|token| {
            match token {
                &Token::IntLit(v) => Ok(v),
                _ => Err(TokenizerError::ExpectIntLit),
            }
        })
    }

    pub fn next_float_lit(&mut self) -> TokenizerResult<f64> {
        self.next_token_check_map(|token| {
            match token {
                &Token::FloatLit(v) => Ok(v),
                _ => Err(TokenizerError::ExpectFloatLit),
            }
        })
    }

}

#[cfg(test)]
mod test {

    use super::*;

    fn tokenize<P, R>(input: &str, what: P) -> R
        where P : FnOnce(&mut Tokenizer) -> TokenizerResult<R>
    {
        let mut tokenizer = Tokenizer::new(input, ParserLanguage::Proto);
        let r = what(&mut tokenizer)
            .expect(&format!("parse failed at {}", tokenizer.loc()));
        let eof = tokenizer.syntax_eof().expect(&format!("check eof failed at {}", tokenizer.loc()));
        assert!(eof, "{}", tokenizer.loc());
        r
    }

    #[test]
    fn test_ident() {
        let msg = r#"  aabb_c  "#;
        let mess = tokenize(msg, |p| p.next_ident().map(|s| s.to_owned()));
        assert_eq!("aabb_c", mess);
    }

    #[test]
    fn test_str_lit() {
        let msg = r#"  "a\nb"  "#;
        let mess = tokenize(msg, |p| p.next_str_lit());
        assert_eq!(StrLit { escaped: r#"a\nb"#.to_owned() }, mess);
    }

}
<|endoftext|>


<commit_before>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;

use self::github::notification::PushNotification;
use self::github::clone_url::CloneUrl;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: CloneUrl,
    pub branch: String,
    build_id: i32
}

/// Type A is some key
pub trait Database : Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;

    use super::PendingBuild;

    use self::github::notification::PushNotification;
    use self::github::clone_url::CloneUrl;

    use self::time::{now, Timespec};
    use self::pg_typeprovider::util::Joinable;

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;

    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    pg_table!(builds)
    pg_table!(commits)
    pg_table!(users)
    pg_table!(assignments)
    pg_table!(submissions)

    pub struct PostgresDatabase {
        db: Connection
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(loc, &SslMode::None).ok().map(|db| {
                PostgresDatabase {
                    db: db
                }
            })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        let delete_table = |s: &str| {
                            conn.execute(
                                format!("DELETE FROM {}", s).as_slice(),
                                &[]).unwrap();
                        };
                        delete_table("builds");
                        delete_table("commits");
                        delete_table("submissions");
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&self.db)
        }
    }

    fn get_one_build(conn: &GenericConnection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &GenericConnection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    trait ToPendingBuild {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild;
    }

    impl ToPendingBuild for Build {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild {
            let commit = CommitSearch::new()
                .where_id(self.commit_id)
                .search(conn, Some(1))
                .pop()
                .unwrap(); // should be in there
            PendingBuild {
                clone_url: CloneUrl::new_from_str(
                    commit.clone_url.as_slice()).unwrap(),
                branch: commit.branch_name,
                build_id: self.id
            }
        }
    }

    fn get_user_by_github_username(conn: &GenericConnection,
                                   name: &str) -> Option<User> {
        UserSearch::new()
            .where_github_username(name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn get_assignment_by_git_project_name(conn: &GenericConnection,
                                          project_name: &str) -> Option<Assignment> {
        AssignmentSearch::new()
            .where_git_project_name(project_name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn insert_submission(conn: &GenericConnection,
                         user: &User,
                         assignment: &Assignment) -> Submission {
        let current_time = now().to_timespec();
        SubmissionInsert {
            user_id: user.id,
            assignment_id: assignment.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone()
        }.insert(conn);
        SubmissionSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_commit(conn: &GenericConnection,
                     user: &User,
                     assignment: &Assignment,
                     submission: &Submission,
                     pn: &PushNotification) -> Commit {
        let current_time = now().to_timespec();
        CommitInsert {
            assignment_id: assignment.id,
            user_id: user.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone(),
            submission_id: submission.id,
            branch_name: pn.branch.clone(),
            clone_url: pn.clone_url.url.to_string()
        }.insert(conn);
        CommitSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_build(conn: &GenericConnection,
                    user: &User,
                    assignment: &Assignment,
                    commit: &Commit) {
        let current_time = now().to_timespec();
        BuildInsert {
            commit_id: commit.id,
            user_id: user.id,
            assignment_id: assignment.id,
            course_id: assignment.course_id,
            created_at: current_time.clone(),
            updated_at: current_time,
            status: (&Pending).to_int(),
            results: "".to_string()
        }.insert(conn);
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            self.with_connection(|conn| {
                let trans = conn.transaction().unwrap();
                let op_user = get_user_by_github_username(
                    &trans, entry.clone_url.username());
                let op_assignment = get_assignment_by_git_project_name(
                    &trans, entry.clone_url.project_name());
                match (op_user, op_assignment) {
                    (Some(user), Some(assignment)) => {
                        let submission = insert_submission(&trans,
                                                           &user,
                                                           &assignment);
                        let commit = insert_commit(&trans,
                                                   &user,
                                                   &assignment,
                                                   &submission,
                                                   &entry);
                        insert_build(&trans, &user, &assignment, &commit);
                        trans.commit().unwrap();
                    },
                    _ => {
                        trans.set_rollback();
                        trans.finish().unwrap();
                    }
                }
            });
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            self.with_connection(|conn| {
                loop {
                    match get_one_build(conn) {
                        Some(b) => {
                            if try_lock_build(conn, &b) {
                                return Some(b.to_pending_build(conn));
                            }
                        },
                        None => { return None; }
                    }
                }

                // Code below uses a transaction to accomplish the same
                // thing.  In small tests there doesn't seem to be any
                // difference in timing, so I'm defaulting to the simpler,
                // lock-free approach above
                // loop {
                //     let start_time = current_time_millis();
                //     let trans = conn.transaction().unwrap();
                //     match get_one_build(&trans) {
                //         Some(b) => {
                //             let res = try_lock_build(&trans, &b);
                //             assert!(res);
                //             if trans.commit().is_ok() {
                //                 let end_time = current_time_millis();
                //                 println!("TIME TAKEN: {}ms", end_time - start_time);
                //                 return Some(b.to_pending_build(conn))
                //             }
                //         },
                //         None => { return None; }
                //     }
                // }
            })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            let num_updated =
                BuildUpdate::new()
                .status_to((&Done).to_int())
                .results_to(results.consume_to_json().to_string())
                .where_id(entry.build_id)
                // not using with_connection to avoid copying results
                .update(&self.db);
            assert_eq!(num_updated, 1);
        }
    }
}
<commit_msg>Now passes connect strings explicitly to pg_typeprovider.<commit_after>// Abstraction of the database.
// For the purposes of the backend, the only necessary
// actions are the following:
//
// 1. Put a pending build into the database.
// 2. Grab a pending build from the database, which
//    will immediately undergo being built.
// 3. Put test results into a database, given what
//    build was pending

extern crate postgres;
#[phase(plugin)]
extern crate pg_typeprovider;
extern crate github;

use self::github::notification::PushNotification;
use self::github::clone_url::CloneUrl;
use builder::BuildResult;

use self::EntryStatus::{Pending, InProgress, Done};

pub struct PendingBuild {
    pub clone_url: CloneUrl,
    pub branch: String,
    build_id: i32
}

/// Type A is some key
pub trait Database : Send {
    fn add_pending(&self, entry: PushNotification);

    /// Optionally gets a pending build from the database.
    /// If `Some` is returned, it will not be returned again.
    /// If `None` is returned, it is expected that the caller will sleep.
    fn get_pending(&self) -> Option<PendingBuild>;

    fn add_test_results(&self, entry: &PendingBuild, results: BuildResult);
}

pub enum EntryStatus {
    Pending,
    InProgress,
    Done
}

impl EntryStatus {
    pub fn to_int(&self) -> i32 {
        match *self {
            Pending => 0,
            InProgress => 1,
            Done => 2
        }
    }
}

pub mod postgres_db {
    extern crate time;
    extern crate pg_typeprovider;
    extern crate github;
    extern crate openssl;

    use super::PendingBuild;

    use self::github::notification::PushNotification;
    use self::github::clone_url::CloneUrl;

    use self::time::{now, Timespec};
    use self::pg_typeprovider::util::Joinable;

    use self::openssl::ssl::{SslContext, SslMethod};

    use super::postgres::{Connection, GenericConnection, SslMode, ToSql};

    use builder::BuildResult;

    use super::EntryStatus::{Pending, InProgress, Done};
    use super::Database;

    // TODO: these must all be string literals, so we can't
    // simply define a constant.  Defining a macro is also not
    // enough, since pg_table! will not expand passed macros (yet),
    // so it sees only a macro invocation.
    pg_table!(builds, "postgres://jroesch@localhost/gradr-test")
    pg_table!(commits, "postgres://jroesch@localhost/gradr-test")
    pg_table!(users, "postgres://jroesch@localhost/gradr-test")
    pg_table!(assignments, "postgres://jroesch@localhost/gradr-test")
    pg_table!(submissions, "postgres://jroesch@localhost/gradr-test")

    pub struct PostgresDatabase {
        db: Connection
    }

    impl PostgresDatabase {
        pub fn new(loc: &str) -> Option<PostgresDatabase> {
            Connection::connect(
                loc,
                &SslMode::Require(
                    SslContext::new(SslMethod::Sslv23).unwrap())).ok().map(|db| {
                    PostgresDatabase {
                        db: db
                    }
                })
        }

        pub fn new_testing() -> Option<PostgresDatabase> {
            let retval = PostgresDatabase::new(
                "postgres://jroesch@localhost/gradr-test");
            match retval {
                Some(ref db) => {
                    db.with_connection(|conn| {
                        let delete_table = |s: &str| {
                            conn.execute(
                                format!("DELETE FROM {}", s).as_slice(),
                                &[]).unwrap();
                        };
                        delete_table("builds");
                        delete_table("commits");
                        delete_table("submissions");
                    })
                },
                None => ()
            };
            retval
        }

        pub fn with_connection<A>(&self, f: |&Connection| -> A) -> A {
            f(&self.db)
        }
    }

    fn get_one_build(conn: &GenericConnection) -> Option<Build> {
        BuildSearch::new()
            .where_status((&Pending).to_int())
            .search(conn, Some(1)).pop()
    }

    // returns true if it was able to lock it, else false
    fn try_lock_build(conn: &GenericConnection, b: &Build) -> bool {
        BuildUpdate::new()
            .status_to((&InProgress).to_int())
            .where_id(b.id)
            .where_status((&Pending).to_int())
            .update(conn) == 1
    }

    trait ToPendingBuild {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild;
    }

    impl ToPendingBuild for Build {
        fn to_pending_build(&self, conn: &GenericConnection) -> PendingBuild {
            let commit = CommitSearch::new()
                .where_id(self.commit_id)
                .search(conn, Some(1))
                .pop()
                .unwrap(); // should be in there
            PendingBuild {
                clone_url: CloneUrl::new_from_str(
                    commit.clone_url.as_slice()).unwrap(),
                branch: commit.branch_name,
                build_id: self.id
            }
        }
    }

    fn get_user_by_github_username(conn: &GenericConnection,
                                   name: &str) -> Option<User> {
        UserSearch::new()
            .where_github_username(name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn get_assignment_by_git_project_name(conn: &GenericConnection,
                                          project_name: &str) -> Option<Assignment> {
        AssignmentSearch::new()
            .where_git_project_name(project_name.to_string())
            .search(conn, Some(1))
            .pop()
    }

    fn insert_submission(conn: &GenericConnection,
                         user: &User,
                         assignment: &Assignment) -> Submission {
        let current_time = now().to_timespec();
        SubmissionInsert {
            user_id: user.id,
            assignment_id: assignment.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone()
        }.insert(conn);
        SubmissionSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_commit(conn: &GenericConnection,
                     user: &User,
                     assignment: &Assignment,
                     submission: &Submission,
                     pn: &PushNotification) -> Commit {
        let current_time = now().to_timespec();
        CommitInsert {
            assignment_id: assignment.id,
            user_id: user.id,
            created_at: current_time.clone(),
            updated_at: current_time.clone(),
            submission_id: submission.id,
            branch_name: pn.branch.clone(),
            clone_url: pn.clone_url.url.to_string()
        }.insert(conn);
        CommitSearch::new()
            .where_created_at(current_time)
            .search(conn, Some(1))
            .pop()
            .unwrap()
    }

    fn insert_build(conn: &GenericConnection,
                    user: &User,
                    assignment: &Assignment,
                    commit: &Commit) {
        let current_time = now().to_timespec();
        BuildInsert {
            commit_id: commit.id,
            user_id: user.id,
            assignment_id: assignment.id,
            course_id: assignment.course_id,
            created_at: current_time.clone(),
            updated_at: current_time,
            status: (&Pending).to_int(),
            results: "".to_string()
        }.insert(conn);
    }

    impl Database for PostgresDatabase {
        fn add_pending(&self, entry: PushNotification) {
            self.with_connection(|conn| {
                let trans = conn.transaction().unwrap();
                let op_user = get_user_by_github_username(
                    &trans, entry.clone_url.username());
                let op_assignment = get_assignment_by_git_project_name(
                    &trans, entry.clone_url.project_name());
                match (op_user, op_assignment) {
                    (Some(user), Some(assignment)) => {
                        let submission = insert_submission(&trans,
                                                           &user,
                                                           &assignment);
                        let commit = insert_commit(&trans,
                                                   &user,
                                                   &assignment,
                                                   &submission,
                                                   &entry);
                        insert_build(&trans, &user, &assignment, &commit);
                        trans.commit().unwrap();
                    },
                    _ => {
                        trans.set_rollback();
                        trans.finish().unwrap();
                    }
                }
            });
        }

        fn get_pending(&self) -> Option<PendingBuild> {
            self.with_connection(|conn| {
                loop {
                    match get_one_build(conn) {
                        Some(b) => {
                            if try_lock_build(conn, &b) {
                                return Some(b.to_pending_build(conn));
                            }
                        },
                        None => { return None; }
                    }
                }

                // Code below uses a transaction to accomplish the same
                // thing.  In small tests there doesn't seem to be any
                // difference in timing, so I'm defaulting to the simpler,
                // lock-free approach above
                // loop {
                //     let start_time = current_time_millis();
                //     let trans = conn.transaction().unwrap();
                //     match get_one_build(&trans) {
                //         Some(b) => {
                //             let res = try_lock_build(&trans, &b);
                //             assert!(res);
                //             if trans.commit().is_ok() {
                //                 let end_time = current_time_millis();
                //                 println!("TIME TAKEN: {}ms", end_time - start_time);
                //                 return Some(b.to_pending_build(conn))
                //             }
                //         },
                //         None => { return None; }
                //     }
                // }
            })
        }

        fn add_test_results(&self, entry: &PendingBuild, results: BuildResult) {
            let num_updated =
                BuildUpdate::new()
                .status_to((&Done).to_int())
                .results_to(results.consume_to_json().to_string())
                .where_id(entry.build_id)
                // not using with_connection to avoid copying results
                .update(&self.db);
            assert_eq!(num_updated, 1);
        }
    }
}
<|endoftext|>


<commit_before><commit_msg>try to fix build on non-apple systems<commit_after><|endoftext|>


<commit_before>use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;
use itertools::Itertools;

#[derive(Debug,Hash,PartialEq,Eq,Clone,Copy)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn manhattan_distance(&self, other: Point) -> u32 {
        ((self.x - other.x).abs() + (self.y - other.y).abs()) as u32
    }
}

impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "({},{})", self.x, self.y)
    }
}

#[derive(Debug)]
struct Segment {
    start: Point, // lower-left most point (x & y are smaller/negative)
    end: Point, // upper-right most point (x & y are larger)
}

impl Segment {
    fn new(a: Point, b: Point) -> Segment {
        if a == b {
            panic!("Points in a segment cannot be equal");
        } else if a.x == b.x {
            if a.y < b.y {
                Segment { start: a, end: b }
            } else if b.y < a.y {
                Segment { start: b, end: a }
            } else {
                panic!("Points cannot be equal");
            }
        } else if a.y == b.y {
            if a.x < b.x {
                Segment { start: a, end: b }
            } else if b.x < a.x {
                Segment { start: b, end: a }
            } else {
                panic!("Points cannot be equal");
            }
        }
        else {
            panic!("Segments cannot be diagonal");
        }
    }
    fn horizontal(&self) -> bool {
        self.start.y == self.end.y
    }
    fn vertical(&self) -> bool {
        self.start.x == self.end.x
    }
    fn intersect(&self, other: &Segment) -> Option<Point> {
        fn helper(horizontal: &Segment, vertical: &Segment) -> Option<Point> {
            let vert_x = vertical.start.x;
            let hor_y = horizontal.start.y;
            if horizontal.start.x <= vert_x &&
                horizontal.end.x >= vert_x &&
                vertical.start.y <= hor_y &&
                vertical.end.y >= hor_y {
                    return Some(Point { x: vert_x, y: hor_y });
                }
            return None;
        }
        if self.horizontal() {
            if other.horizontal() {
                return None;
            } else {
                return helper(self, other);
            }
        } else if self.vertical() {
            if other.vertical() {
                return None;
            } else {
                return helper(other, self);
            }
        } else {
            panic!("Invalid Segments");
        }
    }
}

impl std::fmt::Display for Segment {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}->{}", self.start, self.end)
    }
}

#[derive(Debug)]
struct Wire {
    segments: Vec<Segment>
}

impl Wire {
    fn from_string(line: &String) -> Wire {
        let mut segments = Vec::<Segment>::new();
        let instructions = line.split(",").map(|instruction| {
            let (direction, length_str) = instruction.split_at(1);
            (direction, length_str.parse::<i32>().unwrap())
        });
        let mut curr_pos = Point{x: 0, y: 0};
        for (direction, length) in instructions {
            let new_pos: Point = match direction {
                "U" => Point { x: curr_pos.x, y: curr_pos.y + length },
                "D" => Point { x: curr_pos.x, y: curr_pos.y - length },
                "R" => Point { x: curr_pos.x + length, y: curr_pos.y },
                "L" => Point { x: curr_pos.x - length, y: curr_pos.y },
                _ => panic!("Unknown direction ({})", direction),
            };
            segments.push(Segment::new(curr_pos, new_pos));
            curr_pos = new_pos;
        }
        Wire { segments }
    }
    fn intersect(&self, other: Wire) -> Vec<Point> {
        self.segments.iter().cartesian_product(other.segments.iter()).filter_map(|(a, b)| {
            a.intersect(b)
        }).filter(|point| *point != Point {x:0,y:0}).collect()
    }
}

fn solve(wire1: Wire, wire2: Wire) -> u32 {
    wire1.intersect(wire2).iter().map(|point| {
        point.manhattan_distance(Point { x: 0, y: 0 })
    }).min().unwrap()
}

fn main() {
    let file = File::open(Path::new("./input")).unwrap();
    let reader = BufReader::new(file);
    for chunk in reader.lines().map(|l| l.unwrap()).chunks(2).into_iter() {
        let (wire1, wire2) = match chunk.collect::<Vec<String>>().as_slice() {
            [line1, line2] => (Wire::from_string(line1), Wire::from_string(line2)),
            _ => panic!("Malformed input file"),
        };
        println!("Part 1: {}", solve(wire1, wire2));
    }
}

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

    #[test]
    fn wire_from_string() {
        let mut wire = Wire::from_string(&"R8,U5,L5,D3".to_string());
        assert_eq!(wire.segments[0].start, Point { x: 0, y: 0 });
        assert_eq!(wire.segments[0].end, Point { x: 8, y: 0 });
        let mut last = wire.segments.last().unwrap();
        assert_eq!(last.start, Point { x: 3, y: 2 });
        assert_eq!(last.end, Point { x: 3, y: 5 });

        wire = Wire::from_string(&"U7,R6,D4,L4".to_string());
        assert_eq!(wire.segments[0].start, Point { x: 0, y: 0 });
        assert_eq!(wire.segments[0].end, Point { x: 0, y: 7 });
        last = wire.segments.last().unwrap();
        assert_eq!(last.start, Point { x: 2, y: 3 });
        assert_eq!(last.end, Point { x: 6, y: 3 });
    }

    #[test]
    fn test_segment() {
        let segment1 = Segment::new(Point {x: 0, y: 0}, Point {x: 5, y: 0});
        assert_eq!(segment1.horizontal(), true);
        assert_eq!(segment1.vertical(), false);
        let segment2 = Segment::new(Point {x: 3, y: -2}, Point {x: 3, y: 4});
        assert_eq!(segment2.vertical(), true);
        assert_eq!(segment2.horizontal(), false);
        let mut intersection: Point = segment1.intersect(&segment2).unwrap();
        let expected: Point = Point { x: 3, y: 0 };
        assert_eq!(intersection, expected);

        intersection = segment2.intersect(&segment1).unwrap();
        assert_eq!(intersection, expected);

        let segment3 = Segment::new(Point {x: 4, y: 3}, Point {x: 4, y: 7});
        let result = segment1.intersect(&segment3);
        assert_eq!(result.is_none(), true);
    }

    #[test]
    fn wire_intersect() {
        let wire1 = Wire::from_string(&"R8,U5,L5,D3".to_string());
        let wire2 = Wire::from_string(&"U7,R6,D4,L4".to_string());
        let intersections: HashSet<Point> = wire1.intersect(wire2).into_iter().collect();
        let expected: HashSet<Point> = vec![Point { x: 3, y: 3 }, Point { x: 6, y: 5 }].into_iter().collect();
        assert_eq!(intersections, expected);
    }

    #[test]
    fn try_given_tests() {
        let file = File::open(Path::new("./test")).unwrap();
        let reader = BufReader::new(file);
        for chunk in reader.lines().map(|l| l.unwrap()).chunks(3).into_iter() {
            let (wire1, wire2, answer) = match chunk.collect::<Vec<String>>().as_slice() {
                [line1, line2, line3] => (Wire::from_string(line1), Wire::from_string(line2), line3.parse::<u32>().unwrap()),
                _ => panic!("Malformed input file"),
            };
            println!("solve({:?}, {:?}) == {}", wire1, wire2, answer);
            assert_eq!(solve(wire1, wire2), answer);
        }
    }
}
<commit_msg>2019/day03: run fmt<commit_after>use itertools::Itertools;
use std::fs::File;
use std::io::{BufRead, BufReader};
use std::path::Path;

#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
struct Point {
    x: i32,
    y: i32,
}

impl Point {
    fn manhattan_distance(&self, other: Point) -> u32 {
        ((self.x - other.x).abs() + (self.y - other.y).abs()) as u32
    }
}

impl std::fmt::Display for Point {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "({},{})", self.x, self.y)
    }
}

#[derive(Debug)]
struct Segment {
    start: Point, // lower-left most point (x & y are smaller/negative)
    end: Point,   // upper-right most point (x & y are larger)
}

impl Segment {
    fn new(a: Point, b: Point) -> Segment {
        if a == b {
            panic!("Points in a segment cannot be equal");
        } else if a.x == b.x {
            if a.y < b.y {
                Segment { start: a, end: b }
            } else if b.y < a.y {
                Segment { start: b, end: a }
            } else {
                panic!("Points cannot be equal");
            }
        } else if a.y == b.y {
            if a.x < b.x {
                Segment { start: a, end: b }
            } else if b.x < a.x {
                Segment { start: b, end: a }
            } else {
                panic!("Points cannot be equal");
            }
        } else {
            panic!("Segments cannot be diagonal");
        }
    }
    fn horizontal(&self) -> bool {
        self.start.y == self.end.y
    }
    fn vertical(&self) -> bool {
        self.start.x == self.end.x
    }
    fn intersect(&self, other: &Segment) -> Option<Point> {
        fn helper(horizontal: &Segment, vertical: &Segment) -> Option<Point> {
            let vert_x = vertical.start.x;
            let hor_y = horizontal.start.y;
            if horizontal.start.x <= vert_x
                && horizontal.end.x >= vert_x
                && vertical.start.y <= hor_y
                && vertical.end.y >= hor_y
            {
                return Some(Point {
                    x: vert_x,
                    y: hor_y,
                });
            }
            return None;
        }
        if self.horizontal() {
            if other.horizontal() {
                return None;
            } else {
                return helper(self, other);
            }
        } else if self.vertical() {
            if other.vertical() {
                return None;
            } else {
                return helper(other, self);
            }
        } else {
            panic!("Invalid Segments");
        }
    }
}

impl std::fmt::Display for Segment {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        write!(f, "{}->{}", self.start, self.end)
    }
}

#[derive(Debug)]
struct Wire {
    segments: Vec<Segment>,
}

impl Wire {
    fn from_string(line: &String) -> Wire {
        let mut segments = Vec::<Segment>::new();
        let instructions = line.split(",").map(|instruction| {
            let (direction, length_str) = instruction.split_at(1);
            (direction, length_str.parse::<i32>().unwrap())
        });
        let mut curr_pos = Point { x: 0, y: 0 };
        for (direction, length) in instructions {
            let new_pos: Point = match direction {
                "U" => Point {
                    x: curr_pos.x,
                    y: curr_pos.y + length,
                },
                "D" => Point {
                    x: curr_pos.x,
                    y: curr_pos.y - length,
                },
                "R" => Point {
                    x: curr_pos.x + length,
                    y: curr_pos.y,
                },
                "L" => Point {
                    x: curr_pos.x - length,
                    y: curr_pos.y,
                },
                _ => panic!("Unknown direction ({})", direction),
            };
            segments.push(Segment::new(curr_pos, new_pos));
            curr_pos = new_pos;
        }
        Wire { segments }
    }
    fn intersect(&self, other: Wire) -> Vec<Point> {
        self.segments
            .iter()
            .cartesian_product(other.segments.iter())
            .filter_map(|(a, b)| a.intersect(b))
            .filter(|point| *point != Point { x: 0, y: 0 })
            .collect()
    }
}

fn solve(wire1: Wire, wire2: Wire) -> u32 {
    wire1
        .intersect(wire2)
        .iter()
        .map(|point| point.manhattan_distance(Point { x: 0, y: 0 }))
        .min()
        .unwrap()
}

fn main() {
    let file = File::open(Path::new("./input")).unwrap();
    let reader = BufReader::new(file);
    for chunk in reader.lines().map(|l| l.unwrap()).chunks(2).into_iter() {
        let (wire1, wire2) = match chunk.collect::<Vec<String>>().as_slice() {
            [line1, line2] => (Wire::from_string(line1), Wire::from_string(line2)),
            _ => panic!("Malformed input file"),
        };
        println!("Part 1: {}", solve(wire1, wire2));
    }
}

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

    #[test]
    fn wire_from_string() {
        let mut wire = Wire::from_string(&"R8,U5,L5,D3".to_string());
        assert_eq!(wire.segments[0].start, Point { x: 0, y: 0 });
        assert_eq!(wire.segments[0].end, Point { x: 8, y: 0 });
        let mut last = wire.segments.last().unwrap();
        assert_eq!(last.start, Point { x: 3, y: 2 });
        assert_eq!(last.end, Point { x: 3, y: 5 });

        wire = Wire::from_string(&"U7,R6,D4,L4".to_string());
        assert_eq!(wire.segments[0].start, Point { x: 0, y: 0 });
        assert_eq!(wire.segments[0].end, Point { x: 0, y: 7 });
        last = wire.segments.last().unwrap();
        assert_eq!(last.start, Point { x: 2, y: 3 });
        assert_eq!(last.end, Point { x: 6, y: 3 });
    }

    #[test]
    fn test_segment() {
        let segment1 = Segment::new(Point { x: 0, y: 0 }, Point { x: 5, y: 0 });
        assert_eq!(segment1.horizontal(), true);
        assert_eq!(segment1.vertical(), false);
        let segment2 = Segment::new(Point { x: 3, y: -2 }, Point { x: 3, y: 4 });
        assert_eq!(segment2.vertical(), true);
        assert_eq!(segment2.horizontal(), false);
        let mut intersection: Point = segment1.intersect(&segment2).unwrap();
        let expected: Point = Point { x: 3, y: 0 };
        assert_eq!(intersection, expected);

        intersection = segment2.intersect(&segment1).unwrap();
        assert_eq!(intersection, expected);

        let segment3 = Segment::new(Point { x: 4, y: 3 }, Point { x: 4, y: 7 });
        let result = segment1.intersect(&segment3);
        assert_eq!(result.is_none(), true);
    }

    #[test]
    fn wire_intersect() {
        let wire1 = Wire::from_string(&"R8,U5,L5,D3".to_string());
        let wire2 = Wire::from_string(&"U7,R6,D4,L4".to_string());
        let intersections: HashSet<Point> = wire1.intersect(wire2).into_iter().collect();
        let expected: HashSet<Point> = vec![Point { x: 3, y: 3 }, Point { x: 6, y: 5 }]
            .into_iter()
            .collect();
        assert_eq!(intersections, expected);
    }

    #[test]
    fn try_given_tests() {
        let file = File::open(Path::new("./test")).unwrap();
        let reader = BufReader::new(file);
        for chunk in reader.lines().map(|l| l.unwrap()).chunks(3).into_iter() {
            let (wire1, wire2, answer) = match chunk.collect::<Vec<String>>().as_slice() {
                [line1, line2, line3] => (
                    Wire::from_string(line1),
                    Wire::from_string(line2),
                    line3.parse::<u32>().unwrap(),
                ),
                _ => panic!("Malformed input file"),
            };
            println!("solve({:?}, {:?}) == {}", wire1, wire2, answer);
            assert_eq!(solve(wire1, wire2), answer);
        }
    }
}
<|endoftext|>


<commit_before>use std::default::Default;
use std::fs;
use std::path::Path;

use core::{Package, Profiles};
use util::{CargoResult, human, ChainError, Config};
use ops::{self, Layout, Context, BuildConfig, Kind, Unit};

pub struct CleanOptions<'a> {
    pub spec: &'a [String],
    pub target: Option<&'a str>,
    pub config: &'a Config,
    pub release: bool,
}

/// Cleans the project from build artifacts.
pub fn clean(manifest_path: &Path, opts: &CleanOptions) -> CargoResult<()> {
    let root = try!(Package::for_path(manifest_path, opts.config));
    let target_dir = opts.config.target_dir(&root);

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        let target_dir = target_dir.into_path_unlocked();
        return rm_rf(&target_dir);
    }

    let (resolve, packages) = try!(ops::fetch(manifest_path, opts.config));

    let dest = if opts.release {"release"} else {"debug"};
    let host_layout = try!(Layout::new(opts.config, &root, None, dest));
    let target_layout = match opts.target {
        Some(target) => {
            Some(try!(Layout::new(opts.config, &root, Some(target), dest)))
        }
        None => None,
    };

    let cx = try!(Context::new(&resolve, &packages, opts.config,
                               host_layout, target_layout,
                               BuildConfig::default(),
                               root.manifest().profiles()));

    // resolve package specs and remove the corresponding packages
    for spec in opts.spec {
        // Translate the spec to a Package
        let pkgid = try!(resolve.query(spec));
        let pkg = try!(packages.get(&pkgid));

        // And finally, clean everything out!
        for target in pkg.targets() {
            for kind in [Kind::Host, Kind::Target].iter() {
                let layout = cx.layout(&pkg, *kind);
                try!(rm_rf(&layout.proxy().fingerprint(&pkg)));
                try!(rm_rf(&layout.build(&pkg)));
                let Profiles {
                    ref release, ref dev, ref test, ref bench, ref doc,
                    ref custom_build,
                } = *root.manifest().profiles();
                for profile in [release, dev, test, bench, doc, custom_build].iter() {
                    let unit = Unit {
                        pkg: &pkg,
                        target: target,
                        profile: profile,
                        kind: *kind,
                    };
                    let root = cx.out_dir(&unit);
                    for filename in try!(cx.target_filenames(&unit)).iter() {
                        try!(rm_rf(&root.join(&filename)));
                    }
                }
            }
        }
    }

    Ok(())
}

fn rm_rf(path: &Path) -> CargoResult<()> {
    let m = fs::metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        try!(fs::remove_dir_all(path).chain_error(|| {
            human("could not remove build directory")
        }));
    } else if m.is_ok() {
        try!(fs::remove_file(path).chain_error(|| {
            human("failed to remove build artifact")
        }));
    }
    Ok(())
}
<commit_msg>Don't fetch all crates on clean<commit_after>use std::default::Default;
use std::fs;
use std::path::Path;

use core::{Package, Profiles};
use core::registry::PackageRegistry;
use util::{CargoResult, human, ChainError, Config};
use ops::{self, Layout, Context, BuildConfig, Kind, Unit};

pub struct CleanOptions<'a> {
    pub spec: &'a [String],
    pub target: Option<&'a str>,
    pub config: &'a Config,
    pub release: bool,
}

/// Cleans the project from build artifacts.
pub fn clean(manifest_path: &Path, opts: &CleanOptions) -> CargoResult<()> {
    let root = try!(Package::for_path(manifest_path, opts.config));
    let target_dir = opts.config.target_dir(&root);

    // If we have a spec, then we need to delete some packages, otherwise, just
    // remove the whole target directory and be done with it!
    //
    // Note that we don't bother grabbing a lock here as we're just going to
    // blow it all away anyway.
    if opts.spec.is_empty() {
        let target_dir = target_dir.into_path_unlocked();
        return rm_rf(&target_dir);
    }

    let mut registry = PackageRegistry::new(opts.config);
    let resolve = try!(ops::resolve_pkg(&mut registry, &root, opts.config));
    let packages = ops::get_resolved_packages(&resolve, registry);

    let dest = if opts.release {"release"} else {"debug"};
    let host_layout = try!(Layout::new(opts.config, &root, None, dest));
    let target_layout = match opts.target {
        Some(target) => {
            Some(try!(Layout::new(opts.config, &root, Some(target), dest)))
        }
        None => None,
    };

    let cx = try!(Context::new(&resolve, &packages, opts.config,
                               host_layout, target_layout,
                               BuildConfig::default(),
                               root.manifest().profiles()));

    // resolve package specs and remove the corresponding packages
    for spec in opts.spec {
        // Translate the spec to a Package
        let pkgid = try!(resolve.query(spec));
        let pkg = try!(packages.get(&pkgid));

        // And finally, clean everything out!
        for target in pkg.targets() {
            for kind in [Kind::Host, Kind::Target].iter() {
                let layout = cx.layout(&pkg, *kind);
                try!(rm_rf(&layout.proxy().fingerprint(&pkg)));
                try!(rm_rf(&layout.build(&pkg)));
                let Profiles {
                    ref release, ref dev, ref test, ref bench, ref doc,
                    ref custom_build,
                } = *root.manifest().profiles();
                for profile in [release, dev, test, bench, doc, custom_build].iter() {
                    let unit = Unit {
                        pkg: &pkg,
                        target: target,
                        profile: profile,
                        kind: *kind,
                    };
                    let root = cx.out_dir(&unit);
                    for filename in try!(cx.target_filenames(&unit)).iter() {
                        try!(rm_rf(&root.join(&filename)));
                    }
                }
            }
        }
    }

    Ok(())
}

fn rm_rf(path: &Path) -> CargoResult<()> {
    let m = fs::metadata(path);
    if m.as_ref().map(|s| s.is_dir()).unwrap_or(false) {
        try!(fs::remove_dir_all(path).chain_error(|| {
            human("could not remove build directory")
        }));
    } else if m.is_ok() {
        try!(fs::remove_file(path).chain_error(|| {
            human("failed to remove build artifact")
        }));
    }
    Ok(())
}
<|endoftext|>


<commit_before>use std::io::{Result, Write};

use env::Env;
use library;
use nameutil;
use super::ffi_type::*;
use traits::*;

pub fn generate_classes_funcs<W: Write>(w: &mut W, env: &Env, classes: &[&library::Class]) -> Result<()> {
    for klass in classes {
        try!(generate_object_funcs(w, env, &klass.c_type,
            &klass.glib_get_type, &klass.functions));
    }

    Ok(())
}

pub fn generate_interfaces_funcs<W: Write>(w: &mut W, env: &Env, interfaces: &[&library::Interface]) -> Result<()> {
    for interface in interfaces {
        try!(generate_object_funcs(w, env,  &interface.c_type,
            &interface.glib_get_type, &interface.functions));
    }

    Ok(())
}

pub fn generate_other_funcs<W: Write>(w: &mut W, env: &Env, functions: &[library::Function]) -> Result<()> {
    generate_object_funcs(w, env,  "Other functions", "intern", functions)
}

fn generate_object_funcs<W: Write>(w: &mut W, env: &Env, c_type: &str,
    glib_get_type: &str, functions: &[library::Function]) -> Result<()> {
    let write_get_type = glib_get_type != "intern";
    if write_get_type && !functions.is_empty() {
        try!(writeln!(w, ""));
        try!(writeln!(w, "    //========================================================================="));
        try!(writeln!(w, "    // {}", c_type));
        try!(writeln!(w, "    //========================================================================="));
    }
    if write_get_type {
        try!(writeln!(w, "    pub fn {:<36}() -> GType;", glib_get_type));
    }

    for func in functions {
        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(w, "    {}pub fn {:<36}{};",
                      comment, func.c_identifier.as_ref().unwrap(), sig));
    }

    Ok(())
}

pub fn generate_callbacks<W: Write>(w: &mut W, env: &Env, callbacks: &[&library::Function]) -> Result<()> {
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(w, "{}pub type {} = unsafe extern \"C\" fn{};",
                      comment, func.c_identifier.as_ref().unwrap(), sig));
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c { commented = true; }
    }

    (commented, format!("({}){}", parameter_strs.connect(", "), ret_str))
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() { return (false, String::new()) }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.as_str()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let &library::Type::Fundamental(library::Fundamental::VarArgs) = env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.as_str().into()
    }
    else {
        format!("{}: {}", nameutil::mangle_keywords(&*par.name), ffi_type.as_str())
    };
    (commented, res)
}
<commit_msg>Fix generating object comment<commit_after>use std::io::{Result, Write};

use env::Env;
use library;
use nameutil;
use super::ffi_type::*;
use traits::*;

pub fn generate_classes_funcs<W: Write>(w: &mut W, env: &Env, classes: &[&library::Class]) -> Result<()> {
    for klass in classes {
        try!(generate_object_funcs(w, env, &klass.c_type,
            &klass.glib_get_type, &klass.functions));
    }

    Ok(())
}

pub fn generate_interfaces_funcs<W: Write>(w: &mut W, env: &Env, interfaces: &[&library::Interface]) -> Result<()> {
    for interface in interfaces {
        try!(generate_object_funcs(w, env,  &interface.c_type,
            &interface.glib_get_type, &interface.functions));
    }

    Ok(())
}

pub fn generate_other_funcs<W: Write>(w: &mut W, env: &Env, functions: &[library::Function]) -> Result<()> {
    generate_object_funcs(w, env,  "Other functions", "intern", functions)
}

fn generate_object_funcs<W: Write>(w: &mut W, env: &Env, c_type: &str,
    glib_get_type: &str, functions: &[library::Function]) -> Result<()> {
    let write_get_type = glib_get_type != "intern";
    if write_get_type || !functions.is_empty() {
        try!(writeln!(w, ""));
        try!(writeln!(w, "    //========================================================================="));
        try!(writeln!(w, "    // {}", c_type));
        try!(writeln!(w, "    //========================================================================="));
    }
    if write_get_type {
        try!(writeln!(w, "    pub fn {:<36}() -> GType;", glib_get_type));
    }

    for func in functions {
        let (commented, sig) = function_signature(env, func, false);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(w, "    {}pub fn {:<36}{};",
                      comment, func.c_identifier.as_ref().unwrap(), sig));
    }

    Ok(())
}

pub fn generate_callbacks<W: Write>(w: &mut W, env: &Env, callbacks: &[&library::Function]) -> Result<()> {
    for func in callbacks {
        let (commented, sig) = function_signature(env, func, true);
        let comment = if commented { "//" } else { "" };
        try!(writeln!(w, "{}pub type {} = unsafe extern \"C\" fn{};",
                      comment, func.c_identifier.as_ref().unwrap(), sig));
    }

    Ok(())
}

pub fn function_signature(env: &Env, func: &library::Function, bare: bool) -> (bool, String) {
    let (mut commented, ret_str) = function_return_value(env, func);

    let mut parameter_strs: Vec<String> = Vec::new();
    for par in &func.parameters {
        let (c, par_str) = function_parameter(env, par, bare);
        parameter_strs.push(par_str);
        if c { commented = true; }
    }

    (commented, format!("({}){}", parameter_strs.connect(", "), ret_str))
}

fn function_return_value(env: &Env, func: &library::Function) -> (bool, String) {
    if func.ret.typ == Default::default() { return (false, String::new()) }
    let ffi_type = ffi_type(env, func.ret.typ, &func.ret.c_type);
    let commented = ffi_type.is_err();
    (commented, format!(" -> {}", ffi_type.as_str()))
}

fn function_parameter(env: &Env, par: &library::Parameter, bare: bool) -> (bool, String) {
    if let &library::Type::Fundamental(library::Fundamental::VarArgs) = env.library.type_(par.typ) {
        return (false, "...".into());
    }
    let ffi_type = ffi_type(env, par.typ, &par.c_type);
    let commented = ffi_type.is_err();
    let res = if bare {
        ffi_type.as_str().into()
    }
    else {
        format!("{}: {}", nameutil::mangle_keywords(&*par.name), ffi_type.as_str())
    };
    (commented, res)
}
<|endoftext|>


<commit_before>use rustc_serialize::{Encodable, Decodable, Decoder};
use std::collections::HashMap;
use toml::encode_str;

use yxorp::messages::Command;

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyInstance {
  ip_address: String,
  port:       u16,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, HttpProxyFront>
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct TlsProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  cert_path:  String,
  key_path:   String,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct TlsProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, TlsProxyFront>
}

impl HttpProxy {
  fn new(ip: String, port: u16) -> HttpProxy {
    HttpProxy {
      ip_address: ip,
      port:       port,
      fronts:     HashMap::new(),
    }
  }

  fn handle_command(&mut self, command: &Command) {
    match command {
      &Command::AddHttpFront(ref front) => {
        let f = HttpProxyFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
          port:       front.port,
          instances:  Vec::new()
        };
        self.fronts.insert(front.app_id.clone(), f);
      },
      &Command::RemoveHttpFront(ref front) => {
        self.fronts.remove(&front.app_id);
      },
      &Command::AddInstance(ref instance)  => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let inst = HttpProxyInstance {
            ip_address: instance.ip_address.clone(),
            port:       instance.port,
          };
          front.instances.push(inst);
        }
      },
      &Command::RemoveInstance(ref instance) => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let mut v = Vec::new();
          for el in front.instances.drain(..) {
            if el.ip_address != instance.ip_address || el.port != instance.port {
              v.push(el);
            }
          }
          front.instances = v;
        }
      }
      _ => {}
    }
  }

  fn encode(&self) -> String {
    encode_str(self)
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use yxorp::messages::{Command,HttpFront,Instance};
  use toml::decode_str;

  #[test]
  fn serialize() {
    let mut state = HttpProxy::new(String::from("127.0.0.1"), 80);
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/"), port: 8080 }));
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc"), port: 80 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_command(&Command::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    //assert!(false);
  }
}
<commit_msg>drain does not work yet on stable rust<commit_after>use rustc_serialize::{Encodable, Decodable, Decoder};
use std::collections::HashMap;
use toml::encode_str;

use yxorp::messages::Command;

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyInstance {
  ip_address: String,
  port:       u16,
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct HttpProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct HttpProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, HttpProxyFront>
}

#[derive(Debug,Clone,PartialEq,Eq,Hash, RustcDecodable, RustcEncodable)]
pub struct TlsProxyFront {
  app_id:     String,
  hostname:   String,
  path_begin: String,
  port:       u16,
  cert_path:  String,
  key_path:   String,
  instances:  Vec<HttpProxyInstance>
}

#[derive(Debug,Clone,PartialEq,Eq,RustcDecodable, RustcEncodable)]
pub struct TlsProxy {
  ip_address: String,
  port:       u16,
  fronts:     HashMap<String, TlsProxyFront>
}

impl HttpProxy {
  fn new(ip: String, port: u16) -> HttpProxy {
    HttpProxy {
      ip_address: ip,
      port:       port,
      fronts:     HashMap::new(),
    }
  }

  fn handle_command(&mut self, command: &Command) {
    match command {
      &Command::AddHttpFront(ref front) => {
        let f = HttpProxyFront {
          app_id:     front.app_id.clone(),
          hostname:   front.hostname.clone(),
          path_begin: front.path_begin.clone(),
          port:       front.port,
          instances:  Vec::new()
        };
        self.fronts.insert(front.app_id.clone(), f);
      },
      &Command::RemoveHttpFront(ref front) => {
        self.fronts.remove(&front.app_id);
      },
      &Command::AddInstance(ref instance)  => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let inst = HttpProxyInstance {
            ip_address: instance.ip_address.clone(),
            port:       instance.port,
          };
          front.instances.push(inst);
        }
      },
      &Command::RemoveInstance(ref instance) => {
        if let Some(front) = self.fronts.get_mut(&instance.app_id) {
          let mut v = Vec::new();
          for el in front.instances.iter() {
            if el.ip_address != instance.ip_address || el.port != instance.port {
              v.push(el.clone());
            }
          }
          front.instances = v;
        }
      }
      _ => {}
    }
  }

  fn encode(&self) -> String {
    encode_str(self)
  }
}

#[cfg(test)]
mod tests {
  use super::*;
  use yxorp::messages::{Command,HttpFront,Instance};
  use toml::decode_str;

  #[test]
  fn serialize() {
    let mut state = HttpProxy::new(String::from("127.0.0.1"), 80);
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_1"), hostname: String::from("lolcatho.st:8080"), path_begin: String::from("/"), port: 8080 }));
    state.handle_command(&Command::AddHttpFront(HttpFront { app_id: String::from("app_2"), hostname: String::from("test.local"), path_begin: String::from("/abc"), port: 80 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.1"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("127.0.0.2"), port: 1027 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_2"), ip_address: String::from("192.167.1.2"), port: 1026 }));
    state.handle_command(&Command::AddInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));
    state.handle_command(&Command::RemoveInstance(Instance { app_id: String::from("app_1"), ip_address: String::from("192.168.1.3"), port: 1027 }));

    let encoded = state.encode();
    println!("serialized:\n{}", encoded);

    let new_state: Option<HttpProxy> = decode_str(&encoded);
    println!("deserialized:\n{:?}", new_state);
    assert_eq!(new_state, Some(state));
    //assert!(false);
  }
}
<|endoftext|>


<commit_before>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::path::{Path, PathBuf};

use bindgen::cargo::cargo_expand;
use bindgen::cargo::cargo_lock::{self, Lock};
use bindgen::cargo::cargo_metadata::{self, Metadata};
use bindgen::cargo::cargo_toml;

fn parse_dep_string(dep_string: &str) -> (&str, &str) {
    let split: Vec<&str> = dep_string.split_whitespace().collect();

    (split[0], split[1])
}

pub struct PackageRef {
    pub name: String,
    pub version: String,
}

pub struct Cargo {
    manifest_path: PathBuf,
    binding_crate_name: String,

    lock: Lock,
    metadata: Metadata,
}

impl Cargo {
    pub fn load(crate_dir: &Path, binding_crate_name: Option<&str>) -> Result<Cargo, String> {
        let toml_path = crate_dir.join("Cargo.toml");
        let lock_path = crate_dir.join("Cargo.lock");

        let lock = cargo_lock::lock(&lock_path)
                              .map_err(|x| format!("couldn't load {:?}: {:?}", lock_path, x))?;
        let metadata = cargo_metadata::metadata(&toml_path)
                                      .map_err(|_| format!("couldn't execute `cargo metadata` with manifest {:?}.", toml_path))?;

        // Use the specified binding crate name or infer it from the manifest
        let manifest = cargo_toml::manifest(&toml_path)
                                  .map_err(|_| format!("couldn't load {:?}.", toml_path))?;

        let binding_crate_name = binding_crate_name.map_or(manifest.package.name.clone(),
                                                           |x| x.to_owned());

        Ok(Cargo {
            manifest_path: toml_path,
            binding_crate_name: binding_crate_name,
            lock: lock,
            metadata: metadata,
        })
    }

    pub fn binding_crate_name(&self) -> &str {
        &self.binding_crate_name
    }
    pub fn binding_crate_ref(&self) -> PackageRef {
        self.find_pkg_ref(&self.binding_crate_name).unwrap()
    }

    fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
        for package in &self.metadata.packages {
            if package.name == package_name {
                return Some(PackageRef {
                    name: package_name.to_owned(),
                    version: package.version.clone(),
                });
            }
        }
        None
    }

    pub fn find_dep_ref(&self, package: &PackageRef, dependency_name: &str) -> Option<PackageRef> {
        for lock_package in &self.lock.package {
            if lock_package.name == package.name &&
               lock_package.version == package.version {
                if let Some(ref deps) = lock_package.dependencies {
                    for dep in deps {
                        let (name, version) = parse_dep_string(dep);

                        if name == dependency_name {
                            return Some(PackageRef {
                                name: name.to_owned(),
                                version: version.to_owned(),
                            });
                        }
                    }
                }
                return None;
            }
        }
        None
    }

    pub fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name &&
               meta_package.version == package.version {
                return Path::new(&meta_package.manifest_path).parent()
                                                             .map(|x| x.to_owned());
            }
        }
        None
    }

    pub fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
        let kind_lib = String::from("lib");
        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name &&
               meta_package.version == package.version {
                for target in &meta_package.targets {
                    if target.kind.contains(&kind_lib) {
                        return Some(PathBuf::from(&target.src_path));
                    }
                }
                break;
            }
        }
        None
    }

    pub fn expand_crate(&self, package: &PackageRef) -> Result<String, String> {
        cargo_expand::expand(&self.manifest_path, &package.name, &package.version)
    }
}
<commit_msg>Search for staticlib crate types in addition to lib crate types<commit_after>/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use std::path::{Path, PathBuf};

use bindgen::cargo::cargo_expand;
use bindgen::cargo::cargo_lock::{self, Lock};
use bindgen::cargo::cargo_metadata::{self, Metadata};
use bindgen::cargo::cargo_toml;

fn parse_dep_string(dep_string: &str) -> (&str, &str) {
    let split: Vec<&str> = dep_string.split_whitespace().collect();

    (split[0], split[1])
}

pub struct PackageRef {
    pub name: String,
    pub version: String,
}

pub struct Cargo {
    manifest_path: PathBuf,
    binding_crate_name: String,

    lock: Lock,
    metadata: Metadata,
}

impl Cargo {
    pub fn load(crate_dir: &Path, binding_crate_name: Option<&str>) -> Result<Cargo, String> {
        let toml_path = crate_dir.join("Cargo.toml");
        let lock_path = crate_dir.join("Cargo.lock");

        let lock = cargo_lock::lock(&lock_path)
                              .map_err(|x| format!("couldn't load {:?}: {:?}", lock_path, x))?;
        let metadata = cargo_metadata::metadata(&toml_path)
                                      .map_err(|_| format!("couldn't execute `cargo metadata` with manifest {:?}.", toml_path))?;

        // Use the specified binding crate name or infer it from the manifest
        let manifest = cargo_toml::manifest(&toml_path)
                                  .map_err(|_| format!("couldn't load {:?}.", toml_path))?;

        let binding_crate_name = binding_crate_name.map_or(manifest.package.name.clone(),
                                                           |x| x.to_owned());

        Ok(Cargo {
            manifest_path: toml_path,
            binding_crate_name: binding_crate_name,
            lock: lock,
            metadata: metadata,
        })
    }

    pub fn binding_crate_name(&self) -> &str {
        &self.binding_crate_name
    }
    pub fn binding_crate_ref(&self) -> PackageRef {
        self.find_pkg_ref(&self.binding_crate_name).unwrap()
    }

    fn find_pkg_ref(&self, package_name: &str) -> Option<PackageRef> {
        for package in &self.metadata.packages {
            if package.name == package_name {
                return Some(PackageRef {
                    name: package_name.to_owned(),
                    version: package.version.clone(),
                });
            }
        }
        None
    }

    pub fn find_dep_ref(&self, package: &PackageRef, dependency_name: &str) -> Option<PackageRef> {
        for lock_package in &self.lock.package {
            if lock_package.name == package.name &&
               lock_package.version == package.version {
                if let Some(ref deps) = lock_package.dependencies {
                    for dep in deps {
                        let (name, version) = parse_dep_string(dep);

                        if name == dependency_name {
                            return Some(PackageRef {
                                name: name.to_owned(),
                                version: version.to_owned(),
                            });
                        }
                    }
                }
                return None;
            }
        }
        None
    }

    pub fn find_crate_dir(&self, package: &PackageRef) -> Option<PathBuf> {
        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name &&
               meta_package.version == package.version {
                return Path::new(&meta_package.manifest_path).parent()
                                                             .map(|x| x.to_owned());
            }
        }
        None
    }

    pub fn find_crate_src(&self, package: &PackageRef) -> Option<PathBuf> {
        let kind_lib = String::from("lib");
        let kind_staticlib = String::from("staticlib");
        for meta_package in &self.metadata.packages {
            if meta_package.name == package.name &&
               meta_package.version == package.version {
                for target in &meta_package.targets {
                    if target.kind.contains(&kind_lib) ||
                       target.kind.contains(&kind_staticlib) {
                        return Some(PathBuf::from(&target.src_path));
                    }
                }
                break;
            }
        }
        None
    }

    pub fn expand_crate(&self, package: &PackageRef) -> Result<String, String> {
        cargo_expand::expand(&self.manifest_path, &package.name, &package.version)
    }
}
<|endoftext|>