<commit_before>// use std::collections::HashMap;
use std::ffi::CString;
use libc::c_int;

use super::ffi;

use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow};
use types::ToSql;

impl SqliteConnection {
    /// Convenience method to prepare and execute a single SQL statement with named parameter(s).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///   conn.execute_named("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)", &[(":name", &"one")])
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params))
    }

    /// Convenience method to execute a query with named parameter(s) that is expected to return a single row.
    ///
    /// If the query returns more than one row, all rows except the first are ignored.
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn query_named_row<T, F>(&self,
                                 sql: &str,
                                 params: &[(&str, &ToSql)],
                                 f: F)
                                 -> SqliteResult<T>
        where F: FnOnce(SqliteRow) -> T
    {
        let mut stmt = try!(self.prepare(sql));
        let mut rows = try!(stmt.query_named(params));

        match rows.next() {
            Some(row) => row.map(f),
            None => {
                Err(SqliteError {
                    code: ffi::SQLITE_NOTICE,
                    message: "Query did not return a row".to_string(),
                })
            }
        }
    }
}

impl<'conn> SqliteStatement<'conn> {
    // pub fn parameter_names(&self) -> HashMap<String, i32> {
    // let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) };
    // let mut index_by_name = HashMap::with_capacity(n as usize);
    // for i in 1..n+1 {
    // let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) };
    // if !c_name.is_null() {
    // let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() };
    // index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n);
    // }
    // }
    // index_by_name
    // }

    /// Return the index of an SQL parameter given its name.
    ///
    /// # Failure
    ///
    /// Return None if `name` is invalid (NulError) or if no matching parameter is found.
    pub fn parameter_index(&self, name: &str) -> Option<i32> {
        unsafe {
            CString::new(name).ok().and_then(|c_name| {
                match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) {
                    0 => None, // A zero is returned if no matching parameter is found.
                    n => Some(n),
                }
            })

        }
    }

    /// Execute the prepared statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///   let mut stmt = try!(conn.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)"));
    ///   return stmt.execute_named(&[(":name", &"one")]);
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails, the executed statement returns rows (in
    /// which case `query` should be used instead), or the underling SQLite call fails.
    pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        unsafe {
            try!(self.bind_named_parameters(params));
            self.execute_()
        }
    }

    /// Execute the prepared statement with named parameter(s), returning an iterator over the resulting rows.
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteRows};
    /// fn query(conn: &SqliteConnection) -> SqliteResult<()> {
    ///   let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
    ///   let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
    ///   for row in rows {
    ///   // ...
    ///   }
    ///   return Ok(())
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails.
    pub fn query_named<'a>(&'a mut self,
                           params: &[(&str, &ToSql)])
                           -> SqliteResult<SqliteRows<'a>> {
        self.reset_if_needed();

        unsafe {
            try!(self.bind_named_parameters(params));
        }

        self.needs_reset = true;
        Ok(SqliteRows::new(self))
    }

    unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> {
        for &(name, value) in params {
            let i = try!(self.parameter_index(name).ok_or(SqliteError {
                code: ffi::SQLITE_MISUSE,
                message: format!("Invalid parameter name: {}", name),
            }));
            try!(self.conn.decode_result(value.bind_parameter(self.stmt, i)));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;

    #[test]
    fn test_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();

        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
                   1);
        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
                   1);

        assert_eq!(3i32,
                   db.query_named_row("SELECT SUM(x) FROM foo WHERE x > :x",
                                      &[(":x", &0i32)],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_stmt_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)")
                         .unwrap();
        stmt.execute_named(&[(":name", &"one")]).unwrap();

        assert_eq!(1i32,
                   db.query_named_row("SELECT COUNT(*) FROM test WHERE name = :name",
                                      &[(":name", &"one")],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_query_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap();
        stmt.query_named(&[(":name", &"one")]).unwrap();
    }
}
<commit_msg>Add extra assertions around binding named parameters<commit_after>// use std::collections::HashMap;
use std::ffi::CString;
use libc::c_int;

use super::ffi;

use {SqliteResult, SqliteError, SqliteConnection, SqliteStatement, SqliteRows, SqliteRow};
use types::ToSql;

impl SqliteConnection {
    /// Convenience method to prepare and execute a single SQL statement with named parameter(s).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///   conn.execute_named("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)", &[(":name", &"one")])
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn execute_named(&self, sql: &str, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        self.prepare(sql).and_then(|mut stmt| stmt.execute_named(params))
    }

    /// Convenience method to execute a query with named parameter(s) that is expected to return a single row.
    ///
    /// If the query returns more than one row, all rows except the first are ignored.
    ///
    /// # Failure
    ///
    /// Will return `Err` if `sql` cannot be converted to a C-compatible string or if the
    /// underlying SQLite call fails.
    pub fn query_named_row<T, F>(&self,
                                 sql: &str,
                                 params: &[(&str, &ToSql)],
                                 f: F)
                                 -> SqliteResult<T>
        where F: FnOnce(SqliteRow) -> T
    {
        let mut stmt = try!(self.prepare(sql));
        let mut rows = try!(stmt.query_named(params));

        match rows.next() {
            Some(row) => row.map(f),
            None => {
                Err(SqliteError {
                    code: ffi::SQLITE_NOTICE,
                    message: "Query did not return a row".to_string(),
                })
            }
        }
    }
}

impl<'conn> SqliteStatement<'conn> {
    // pub fn parameter_names(&self) -> HashMap<String, i32> {
    // let n = unsafe { ffi::sqlite3_bind_parameter_count(self.stmt) };
    // let mut index_by_name = HashMap::with_capacity(n as usize);
    // for i in 1..n+1 {
    // let c_name = unsafe { ffi::sqlite3_bind_parameter_name(self.stmt, i) };
    // if !c_name.is_null() {
    // let c_slice = unsafe { CStr::from_ptr(c_name).to_bytes() };
    // index_by_name.insert(str::from_utf8(c_slice).unwrap().to_string(), n);
    // }
    // }
    // index_by_name
    // }

    /// Return the index of an SQL parameter given its name.
    ///
    /// # Failure
    ///
    /// Return None if `name` is invalid (NulError) or if no matching parameter is found.
    pub fn parameter_index(&self, name: &str) -> Option<i32> {
        unsafe {
            CString::new(name).ok().and_then(|c_name| {
                match ffi::sqlite3_bind_parameter_index(self.stmt, c_name.as_ptr()) {
                    0 => None, // A zero is returned if no matching parameter is found.
                    n => Some(n),
                }
            })

        }
    }

    /// Execute the prepared statement with named parameter(s).
    ///
    /// On success, returns the number of rows that were changed or inserted or deleted (via
    /// `sqlite3_changes`).
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult};
    /// fn insert(conn: &SqliteConnection) -> SqliteResult<i32> {
    ///   let mut stmt = try!(conn.prepare("INSERT INTO test (id, name, flag) VALUES (:id, :name, :flag)"));
    ///   return stmt.execute_named(&[(":name", &"one")]);
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails, the executed statement returns rows (in
    /// which case `query` should be used instead), or the underling SQLite call fails.
    pub fn execute_named(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<c_int> {
        unsafe {
            try!(self.bind_named_parameters(params));
            self.execute_()
        }
    }

    /// Execute the prepared statement with named parameter(s), returning an iterator over the resulting rows.
    ///
    /// ## Example
    ///
    /// ```rust,no_run
    /// # use rusqlite::{SqliteConnection, SqliteResult, SqliteRows};
    /// fn query(conn: &SqliteConnection) -> SqliteResult<()> {
    ///   let mut stmt = try!(conn.prepare("SELECT * FROM test where name = :name"));
    ///   let mut rows = try!(stmt.query_named(&[(":name", &"one")]));
    ///   for row in rows {
    ///   // ...
    ///   }
    ///   return Ok(())
    /// }
    /// ```
    ///
    /// # Failure
    ///
    /// Will return `Err` if binding parameters fails.
    pub fn query_named<'a>(&'a mut self,
                           params: &[(&str, &ToSql)])
                           -> SqliteResult<SqliteRows<'a>> {
        self.reset_if_needed();

        unsafe {
            try!(self.bind_named_parameters(params));
        }

        self.needs_reset = true;
        Ok(SqliteRows::new(self))
    }

    unsafe fn bind_named_parameters(&mut self, params: &[(&str, &ToSql)]) -> SqliteResult<()> {
        // Always check that the number of parameters is correct.
        assert!(params.len() as c_int == ffi::sqlite3_bind_parameter_count(self.stmt),
                "incorrect number of parameters to query(): expected {}, got {}",
                ffi::sqlite3_bind_parameter_count(self.stmt),
                params.len());

        // In debug, also sanity check that we got distinct parameter names.
        debug_assert!({
                          use std::collections::HashSet;

                          let mut s = HashSet::with_capacity(params.len());
                          for &(name, _) in params {
                              s.insert(name);
                          }

                          s.len() == params.len()
                      },
                      "named parameters must be unique");

        for &(name, value) in params {
            let i = try!(self.parameter_index(name).ok_or(SqliteError {
                code: ffi::SQLITE_MISUSE,
                message: format!("Invalid parameter name: {}", name),
            }));
            try!(self.conn.decode_result(value.bind_parameter(self.stmt, i)));
        }
        Ok(())
    }
}

#[cfg(test)]
mod test {
    use SqliteConnection;

    #[test]
    fn test_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        db.execute_batch("CREATE TABLE foo(x INTEGER)").unwrap();

        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &1i32)]).unwrap(),
                   1);
        assert_eq!(db.execute_named("INSERT INTO foo(x) VALUES (:x)", &[(":x", &2i32)]).unwrap(),
                   1);

        assert_eq!(3i32,
                   db.query_named_row("SELECT SUM(x) FROM foo WHERE x > :x",
                                      &[(":x", &0i32)],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_stmt_execute_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("INSERT INTO test (name) VALUES (:name)").unwrap();
        stmt.execute_named(&[(":name", &"one")]).unwrap();

        assert_eq!(1i32,
                   db.query_named_row("SELECT COUNT(*) FROM test WHERE name = :name",
                                      &[(":name", &"one")],
                                      |r| r.get(0))
                     .unwrap());
    }

    #[test]
    fn test_query_named() {
        let db = SqliteConnection::open_in_memory().unwrap();
        let sql = "CREATE TABLE test (id INTEGER PRIMARY KEY NOT NULL, name TEXT NOT NULL, flag \
                   INTEGER)";
        db.execute_batch(sql).unwrap();

        let mut stmt = db.prepare("SELECT * FROM test where name = :name").unwrap();
        stmt.query_named(&[(":name", &"one")]).unwrap();
    }

    #[test]
    #[should_panic]
    fn test_panic_on_incorrect_number_of_parameters() {
        let db = SqliteConnection::open_in_memory().unwrap();

        let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap();
        let _ = stmt.query_named(&[(":one", &1i32)]);
    }

    #[test]
    #[cfg(debug_assertions)]
    #[should_panic]
    fn test_debug_panic_on_incorrect_parameter_names() {
        let db = SqliteConnection::open_in_memory().unwrap();

        let mut stmt = db.prepare("SELECT 1 WHERE 1 = :one AND 2 = :two").unwrap();
        let _ = stmt.query_named(&[(":one", &1i32), (":one", &2i32)]);
    }
}
<|endoftext|>


<commit_before>use std::str::SendStr;
use http::status::Status;

pub use self::NickelErrorKind::{ErrorWithStatusCode, UserDefinedError, Other};

/// NickelError is the basic error type for HTTP errors as well as user defined errors.
/// One can pattern match against the `kind` property to handle the different cases.

#[deriving(Show)]
pub struct NickelError {
    pub kind: NickelErrorKind,
    pub message: SendStr
}

impl NickelError {
    /// Creates a new `NickelError` instance
    ///
    /// # Example
    /// ```{rust,ignore}
    /// NickelError::new("Error Parsing JSON", ErrorWithStatusCode(BadRequest));
    /// ```
    pub fn new<T: IntoMaybeOwned<'static>>(message: T, kind: NickelErrorKind) -> NickelError {
        NickelError {
            message: message.into_maybe_owned(),
            kind: kind
        }
    }
}

#[deriving(Show)]
pub enum NickelErrorKind {
    // FIXME: Should probably re-export http::status::Status
    ErrorWithStatusCode(Status),
    UserDefinedError(int, String),
    Other
}
<commit_msg>fix(SendStr): MaybeOwned is depreciated in favor of Cow pointers<commit_after>use std::str::CowString;
use http::status::Status;

pub use self::NickelErrorKind::{ErrorWithStatusCode, UserDefinedError, Other};

/// NickelError is the basic error type for HTTP errors as well as user defined errors.
/// One can pattern match against the `kind` property to handle the different cases.

#[deriving(Show)]
pub struct NickelError {
    pub kind: NickelErrorKind,
    pub message: CowString<'static>
}

impl NickelError {
    /// Creates a new `NickelError` instance
    ///
    /// # Example
    /// ```{rust,ignore}
    /// NickelError::new("Error Parsing JSON", ErrorWithStatusCode(BadRequest));
    /// ```
    pub fn new<T: IntoCow<'static, String, str>>(message: T, kind: NickelErrorKind) -> NickelError {
        NickelError {
            message: message.into_cow(),
            kind: kind
        }
    }
}

#[deriving(Show)]
pub enum NickelErrorKind {
    // FIXME: Should probably re-export http::status::Status
    ErrorWithStatusCode(Status),
    UserDefinedError(int, String),
    Other
}
<|endoftext|>


<commit_before>use std::mem;
use std::collections::HashMap;

use super::lexer::{Lexer, LexError};
use super::token::{TokenSpan, Token, Lit};
use super::exec::*; // TODO: remove * import

pub struct Parser<'a> {
    lexer: Lexer<'a>,

    curr: Option<TokenSpan>,
    last: Option<TokenSpan>,
    peek: Option<TokenSpan>,
}

impl<'a> Parser<'a> {
    pub fn from_query(q: &'a str) -> Parser<'a> {
        let lex = Lexer::from_query(q);
        Parser {
            lexer: lex,
            curr: None,
            last: None,
            peek: None,
        }
    }

    pub fn parse(&mut self) -> Result<Query, ParserError> {
        // TODO: handle this with a better error message
        try!(self.bump());
        try!(self.bump());

        self.parse_commands()
    }

    fn bump(&mut self) -> Result<(), ParserError> {
        // do stuff. Mainly swap last = curr, curr = peek, then peek = next_real
        mem::swap(&mut self.last, &mut self.curr);
        mem::swap(&mut self.curr, &mut self.peek);
        self.peek = try!(self.lexer.next_real());
        Ok(())
    }

    // SQL Commands

    fn parse_commands(&mut self) -> Result<Query, ParserError> {
        let curr = self.curr.clone();

        // Parse first word that
        match curr.unwrap().token {
            Token::Word(val) => self.run_major_command(val),
            _ => Err(ParserError::FirstCmdNotWord),
        }
    }

    fn run_major_command(&mut self, cmd: String) -> Result<Query, ParserError> {
        match Keyword::from_str(&*cmd).unwrap() { // TODO: clean up unwrap
            Keyword::Select => self.parse_select(),
            Keyword::Insert => self.parse_insert(),

            _ => Err(ParserError::FirstCmdNotMajor),
        }
    }

    fn parse_select(&mut self) -> Result<Query, ParserError> {
        // TODO: impl parse_select

        Ok(Query::Table(TableStmt::Select(SelectStmt {
            cols: vec![Col { name: "id".to_owned() }],
            table: Table {
                name: "user_v1".to_owned(),
                alias: None,
            },
        })))
    }

    fn parse_insert(&mut self) -> Result<Query, ParserError> {
        // TODO: impl parse_insert
        try!(self.expect_keyword(Keyword::Into));

        //try!();

        try!(self.expect_token(Token::ParentOP));

        let cols = try!(self.expect_comma_dil_word());

        Ok(Query::Table(TableStmt::Insert(InsertStmt {
            table: Table {
                name: "user_v1".to_owned(),
                alias: None,
            },
            cols: HashMap::new(),
        })))
    }
}

// Helper function
impl<'a> Parser<'a> {
    fn expect_keyword(&mut self, exp: Keyword) -> Result<Keyword, ParserError> {
        // TODO: clean up unwrap but they should be safe for the moment

        try!(self.bump());
        let curr = {
            let token = &self.curr.clone().unwrap();

            match &token.token {
                &Token::Word(ref word) => word.clone(),
                t => return Err(ParserError::ExpectedKeyword(exp, format!("{:?}", t))),
            }
        };

        let actual = try!(Keyword::from_str(&curr));

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedKeyword(exp, curr))
        }
    }

    fn expect_token(&mut self, exp: Token)  -> Result<Token, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedToken(exp, format!("{:?}", actual)))
        }
    }

    // expect word case insensitive.
    fn expect_word(&mut self) -> Result<String, ParserError> {
        try!(self.bump());

        let token = match self.curr.clone() {
            Some(t) => t,
            None => return Err(ParserError::ExpectedTokenButGotNone),
        };
        let actual = token.token.clone();

        let word = match actual {
            Token::Word(ref word) => word.to_lowercase(), // always lowercase
            t => return Err(ParserError::ExpectedToken(Token::Word(String::new()), format!("{:?}", t))),
        };

        Ok(word)
    }

    fn expect_lit(&mut self) -> Result<Lit, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        match actual {
            Token::Literal(lit) => Ok(lit),
            t => Err(ParserError::ExpectedToken(Token::Literal(Lit::String(String::new())), format!("{:?}", t))),
        }
    }

    fn expect_comma_dil_word(&mut self) -> Result<Vec<String>, ParserError> {
        let mut cols = Vec::new();

        loop {
            println!("{:?}", &self.curr);
            match self.expect_word() {
                Ok(ref word) => {
                    cols.push(word.to_owned());
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),
                        Token::ParentCL => {
                            try!(self.bump());
                            break;
                        },
                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(ParserError::ExpectedToken(_, _)) => {
                    let curr = self.curr.clone().unwrap().token;

                    match curr {
                        Token::Comma => try!(self.bump()),
                        Token::ParentCL => {
                            try!(self.bump());
                            break;
                        },
                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        Ok(cols)
    }

    fn peek_clone(&mut self) -> Result<Token, ParserError> {
        let peek = try!(Self::unwrap_tokenspan(self.peek.clone()));

        Ok(peek.token)
    }

    fn unwrap_tokenspan(t: Option<TokenSpan>) -> Result<TokenSpan, ParserError> {
        match t {
            Some(val) =>  Ok(val),
            None => Err(ParserError::ExpectedTokenButGotNone),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum Keyword {
    // Major
    Select,
    Insert,

    // Minor
    From,
    Into,
}

impl Keyword {
    pub fn from_str(k: &str) -> Result<Keyword, ParserError> {
        let keyword = match &*k.to_lowercase() {
            "select" => Keyword::Select,
            "insert" => Keyword::Insert,

            "from" => Keyword::From,
            "into" => Keyword::Into,

            // Keyword not found
            keyword => return Err(ParserError::UnexpectedKeyword(keyword.to_owned())), // TODO: clean up panic
        };

        Ok(keyword)
    }
}

#[derive(Debug)]
pub enum ParserError {
    InvalidCommand,
    LexerError(LexError),

    FirstCmdNotWord,
    FirstCmdNotMajor,

    ExpectedKeyword(Keyword, String), // exp, actual
    ExpectedToken(Token, String), // exp, actual
    ExpectedTokenButGotNone,

    UnexpectedKeyword(String),
}

impl From<LexError> for ParserError {
    fn from(e: LexError) -> ParserError {
        ParserError::LexerError(e)
    }
}

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

    #[test]
    fn select() {
        let mut p = Parser::from_query("select");
        p.parse().unwrap();
    }

    #[test]
    fn insert() {
        let mut p = Parser::from_query("insert into (name, email)");
        let q = p.parse().unwrap();

    }

    #[test]
    #[should_panic]
    fn insert_panic() {
        Parser::from_query("insert into (asdf aslkdfhjahh dsfkjals)").parse().unwrap();
    }

    #[test]
    #[should_panic]
    fn first_non_major() {
        let err = Parser::from_query("alskdfj").parse();
        assert!(err.is_err());
    }
}
<commit_msg>cleaned up comma_dil_word list<commit_after>use std::mem;
use std::collections::HashMap;

use super::lexer::{Lexer, LexError};
use super::token::{TokenSpan, Token, Lit};
use super::exec::*; // TODO: remove * import

pub struct Parser<'a> {
    lexer: Lexer<'a>,

    curr: Option<TokenSpan>,
    last: Option<TokenSpan>,
    peek: Option<TokenSpan>,
}

impl<'a> Parser<'a> {
    pub fn from_query(q: &'a str) -> Parser<'a> {
        let lex = Lexer::from_query(q);
        Parser {
            lexer: lex,
            curr: None,
            last: None,
            peek: None,
        }
    }

    pub fn parse(&mut self) -> Result<Query, ParserError> {
        // TODO: handle this with a better error message
        try!(self.bump());
        try!(self.bump());

        self.parse_commands()
    }

    fn bump(&mut self) -> Result<(), ParserError> {
        // do stuff. Mainly swap last = curr, curr = peek, then peek = next_real
        mem::swap(&mut self.last, &mut self.curr);
        mem::swap(&mut self.curr, &mut self.peek);
        self.peek = try!(self.lexer.next_real());
        Ok(())
    }

    // SQL Commands

    fn parse_commands(&mut self) -> Result<Query, ParserError> {
        let curr = self.curr.clone();

        // Parse first word that
        match curr.unwrap().token {
            Token::Word(val) => self.run_major_command(val),
            _ => Err(ParserError::FirstCmdNotWord),
        }
    }

    fn run_major_command(&mut self, cmd: String) -> Result<Query, ParserError> {
        match Keyword::from_str(&*cmd).unwrap() { // TODO: clean up unwrap
            Keyword::Select => self.parse_select(),
            Keyword::Insert => self.parse_insert(),

            _ => Err(ParserError::FirstCmdNotMajor),
        }
    }

    fn parse_select(&mut self) -> Result<Query, ParserError> {
        // TODO: impl parse_select

        Ok(Query::Table(TableStmt::Select(SelectStmt {
            cols: vec![Col { name: "id".to_owned() }],
            table: Table {
                name: "user_v1".to_owned(),
                alias: None,
            },
        })))
    }

    fn parse_insert(&mut self) -> Result<Query, ParserError> {
        // TODO: impl parse_insert
        try!(self.expect_keyword(Keyword::Into));

        //try!();

        try!(self.expect_token(Token::ParentOP));

        let cols = try!(self.expect_comma_dil_word());

        Ok(Query::Table(TableStmt::Insert(InsertStmt {
            table: Table {
                name: "user_v1".to_owned(),
                alias: None,
            },
            cols: HashMap::new(),
        })))
    }
}

// Helper function
impl<'a> Parser<'a> {
    fn expect_keyword(&mut self, exp: Keyword) -> Result<Keyword, ParserError> {
        // TODO: clean up unwrap but they should be safe for the moment

        try!(self.bump());
        let curr = {
            let token = &self.curr.clone().unwrap();

            match &token.token {
                &Token::Word(ref word) => word.clone(),
                t => return Err(ParserError::ExpectedKeyword(exp, format!("{:?}", t))),
            }
        };

        let actual = try!(Keyword::from_str(&curr));

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedKeyword(exp, curr))
        }
    }

    fn expect_token(&mut self, exp: Token)  -> Result<Token, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        if actual == exp {
            Ok(actual)
        } else {
            Err(ParserError::ExpectedToken(exp, format!("{:?}", actual)))
        }
    }

    // expect word case insensitive.
    fn expect_word(&mut self) -> Result<String, ParserError> {
        try!(self.bump());

        let token = match self.curr.clone() {
            Some(t) => t,
            None => return Err(ParserError::ExpectedTokenButGotNone),
        };
        let actual = token.token.clone();

        let word = match actual {
            Token::Word(ref word) => word.to_lowercase(), // always lowercase
            t => return Err(ParserError::ExpectedToken(Token::Word(String::new()), format!("{:?}", t))),
        };

        Ok(word)
    }

    fn expect_lit(&mut self) -> Result<Lit, ParserError> {
        try!(self.bump());

        let token = self.curr.clone().unwrap();
        let actual = token.token.clone();

        match actual {
            Token::Literal(lit) => Ok(lit),
            t => Err(ParserError::ExpectedToken(Token::Literal(Lit::String(String::new())), format!("{:?}", t))),
        }
    }

    fn expect_comma_dil_word(&mut self) -> Result<Vec<String>, ParserError> {
        let mut cols = Vec::new();

        loop {
            println!("{:?}", &self.curr);
            match self.expect_word() {
                Ok(ref word) => {
                    cols.push(word.to_owned());
                    match try!(self.peek_clone()) {
                        Token::Comma => try!(self.bump()),
                        Token::ParentCL => {
                            try!(self.bump());
                            break;
                        },
                        token => return Err(ParserError::ExpectedToken(Token::Comma, format!("{:?}", token))),
                    }
                },
                Err(err) => return Err(err),
            }
        }

        Ok(cols)
    }

    fn peek_clone(&mut self) -> Result<Token, ParserError> {
        let peek = try!(Self::unwrap_tokenspan(self.peek.clone()));

        Ok(peek.token)
    }

    fn unwrap_tokenspan(t: Option<TokenSpan>) -> Result<TokenSpan, ParserError> {
        match t {
            Some(val) =>  Ok(val),
            None => Err(ParserError::ExpectedTokenButGotNone),
        }
    }
}

#[derive(Debug, PartialEq)]
pub enum Keyword {
    // Major
    Select,
    Insert,

    // Minor
    From,
    Into,
}

impl Keyword {
    pub fn from_str(k: &str) -> Result<Keyword, ParserError> {
        let keyword = match &*k.to_lowercase() {
            "select" => Keyword::Select,
            "insert" => Keyword::Insert,

            "from" => Keyword::From,
            "into" => Keyword::Into,

            // Keyword not found
            keyword => return Err(ParserError::UnexpectedKeyword(keyword.to_owned())), // TODO: clean up panic
        };

        Ok(keyword)
    }
}

#[derive(Debug)]
pub enum ParserError {
    InvalidCommand,
    LexerError(LexError),

    FirstCmdNotWord,
    FirstCmdNotMajor,

    ExpectedKeyword(Keyword, String), // exp, actual
    ExpectedToken(Token, String), // exp, actual
    ExpectedTokenButGotNone,

    UnexpectedKeyword(String),
}

impl From<LexError> for ParserError {
    fn from(e: LexError) -> ParserError {
        ParserError::LexerError(e)
    }
}

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

    #[test]
    fn select() {
        let mut p = Parser::from_query("select");
        p.parse().unwrap();
    }

    #[test]
    fn insert() {
        let mut p = Parser::from_query("insert into (name, email)");
        let q = p.parse().unwrap();

    }

    #[test]
    #[should_panic]
    fn insert_panic() {
        Parser::from_query("insert into (asdf aslkdfhjahh dsfkjals)").parse().unwrap();
    }

    #[test]
    #[should_panic]
    fn first_non_major() {
        let err = Parser::from_query("alskdfj").parse();
        assert!(err.is_err());
    }
}
<|endoftext|>


<commit_before>use std::ops::Range;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenType {
    Identifier(String),

    StringLiteral(String),
    IntegerLiteral(String),
    FloatLiteral(String),

    Colon,
    Semicolon,
    Comma,
    Period,

    LParen,
    RParen,
    LBrace,
    RBrace,
    LBracket,
    RBracket,

    If,
    Else,
    For,
    This,
    Return,
    Bool(bool),

    BinaryOp,

    Invalid,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BinaryOp {
    Mul,
    Div,
    Plus,
    Minus,
    Equals,
    NotEquals,
    Lt,
    Gt,
    LtEqual,
    GtEqual,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token {
    pub token_type: TokenType,
    pub span:       Span,
}

impl Token {
    pub fn new(token_type: TokenType, start: usize, end: usize) -> Token {
        Token {
            token_type: token_type,
            span:       Span::new(start, end),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Span {
    pub start: usize,
    pub end:   usize,
}

impl Span {
    pub fn new(start: usize, end: usize) -> Span {
        Span {
            start: start,
            end:   end,
        }
    }
}

impl From<usize> for Span {
    fn from(position: usize) -> Span {
        Span::new(position, position)
    }
}

impl From<Range<usize>> for Span {
    fn from(range: Range<usize>) -> Span {
        Span::new(range.start, range.end)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Lexer<'a> {
    source:    &'a str,

    pos:       usize,
    token_pos: usize,

    current:   Option<char>,
}

impl<'a> Lexer<'a> {
    pub fn new(source: &'a str) -> Lexer {
        let mut lexer = Lexer {
            source:    source,

            pos:       0,
            token_pos: 0,

            current:   None,
        };

        lexer.current = lexer.char_at(0);
        lexer
    }

    fn lex_term(&mut self) -> &str {
        while let Some(c) = self.current {
            if !(identifier_worthy(c) || c.is_digit(10)) {
                break
            }

            self.move_forward();
        }

        &self.source[self.token_pos .. self.pos]
    }

    fn lex_string(&mut self) -> String {
        let mut buffer = String::new();

        loop {
            let c = match self.current {
                Some(c) => c,
                None    => panic!("opened string is unterminated"),
            };

            self.move_forward();

            match c {
                '\\' => {
                    let escaped = match self.current {
                        Some(e) => e,
                        None    => continue,
                    };

                    self.move_forward();

                    buffer.push(match escaped {
                        '"' | '\\' => escaped,
                        't'         => '\t',
                        'n'         => '\n',
                        'r'         => '\r',
                        c           => {
                            panic!("invalid escaped character: {:?}", c)
                        },
                    })
                },

                '"'  => break,
                c    => buffer.push(c),
            }
        }

        buffer
    }

    fn move_whitespace(&mut self) {
        while let Some(c) = self.current {
            if !c.is_whitespace() {
                break
            }

            self.move_forward()
        }
    }

    fn move_forward(&mut self) {
        if let Some(c) = self.current {
            self.pos += c.len_utf8();
            self.current    = self.char_at(self.pos);
        } else {
            panic!("lexer lexing too far")
        }
    }

    fn char_at(&self, pos: usize) -> Option<char> {
        self.source[pos ..].chars().next()
    }
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {

        self.move_whitespace();
        self.token_pos = self.pos;

        let c = match self.current {
            Some(c) => c,
            None    => return None,
        };

        let token_type = if c.is_alphabetic() {
            match self.lex_term() {
                "if"     => TokenType::If,
                "for"    => TokenType::For,
                "true"   => TokenType::Bool(true),
                "false"  => TokenType::Bool(false),
                "return" => TokenType::Return,
                _        => TokenType::Identifier(
                                String::from(self.lex_term())
                            ),
            }
        } else {
            self.move_forward();

            match c {
                '"' => TokenType::StringLiteral(
                           String::from(self.lex_string())
                       ),
                '['  => TokenType::LBracket,
                ']'  => TokenType::RBracket,
                '('  => TokenType::LParen,
                ')'  => TokenType::RParen,
                '{'  => TokenType::LBrace,
                '}'  => TokenType::RBrace,
                _    => TokenType::Invalid,
            }
        };

        Some(Token::new(token_type, self.token_pos, self.pos))
    }
}

pub fn tokenize(source: &str) -> Vec<Token> {
    Lexer::new(source).collect()
}

fn identifier_worthy(c: char) -> bool {
    c.is_alphabetic() || c == '_'
                      || c == '?'
}<commit_msg>Implement binary operand and number lexing<commit_after>use std::ops::Range;

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TokenType {
    Identifier(String),

    StringLiteral(String),
    NumberLiteral(String),

    Colon,
    Semicolon,
    Comma,
    Period,

    LParen,
    RParen,
    LBrace,
    RBrace,
    LBracket,
    RBracket,

    Assign,

    If,
    Else,
    For,
    This,
    Return,
    Bool(bool),

    BinaryOp((BinaryOp, u8)),

    Invalid,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum BinaryOp {
    Mul,
    Div,
    Plus,
    Minus,
    Equals,
    NotEquals,
    Lt,
    Gt,
    LtEquals,
    GtEquals,
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Token {
    pub token_type: TokenType,
    pub span:       Span,
}

impl Token {
    pub fn new(token_type: TokenType, start: usize, end: usize) -> Token {
        Token {
            token_type: token_type,
            span:       Span::new(start, end),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Span {
    pub start: usize,
    pub end:   usize,
}

impl Span {
    pub fn new(start: usize, end: usize) -> Span {
        Span {
            start: start,
            end:   end,
        }
    }
}

impl From<usize> for Span {
    fn from(position: usize) -> Span {
        Span::new(position, position)
    }
}

impl From<Range<usize>> for Span {
    fn from(range: Range<usize>) -> Span {
        Span::new(range.start, range.end)
    }
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Lexer<'a> {
    source:    &'a str,

    pos:       usize,
    token_pos: usize,

    current:   Option<char>,
}

impl<'a> Lexer<'a> {
    pub fn new(source: &'a str) -> Lexer {
        let mut lexer = Lexer {
            source:    source,

            pos:       0,
            token_pos: 0,

            current:   None,
        };

        lexer.current = lexer.char_at(0);
        lexer
    }

    fn lex_term(&mut self) -> &str {
        while let Some(c) = self.current {
            if !(identifier_worthy(c) || c.is_digit(10)) {
                break
            }

            self.move_forward(1);
        }

        &self.source[self.token_pos .. self.pos]
    }

    fn lex_number(&mut self) -> &str {
        while let Some(c) = self.current {
            if !c.is_digit(10) && c != '.' {
                break
            }

            self.move_forward(1);
        }

        &self.source[self.token_pos .. self.pos]
    }

    fn lex_bin_op(&mut self) -> String {
        let mut accum  = 2; // max operand length
        let mut buffer = String::new();

        while let Some(c) = self.current {
            if accum == 0 {
                break
            }

            buffer.push(c);
            accum -= 1;

            match bin_op(&buffer) {
                Some(_) => {
                    self.move_forward(2 - accum);
                    break
                },
                None    => (),
            }

            self.move_forward(1);
        }

        buffer
    }

    fn lex_string(&mut self) -> String {
        let mut buffer = String::new();

        loop {
            let c = match self.current {
                Some(c) => c,
                None    => panic!("opened string is unterminated"),
            };

            self.move_forward(1);

            match c {
                '\\' => {
                    let escaped = match self.current {
                        Some(e) => e,
                        None    => continue,
                    };

                    self.move_forward(1);

                    buffer.push(match escaped {
                        '"' | '\\' => escaped,
                        't'         => '\t',
                        'n'         => '\n',
                        'r'         => '\r',
                        c           => {
                            panic!("invalid escaped character: {:?}", c)
                        },
                    })
                },

                '"'  => break,
                c    => buffer.push(c),
            }
        }

        buffer
    }

    fn move_whitespace(&mut self) {
        while let Some(c) = self.current {
            if !c.is_whitespace() {
                break
            }

            self.move_forward(1)
        }
    }

    fn move_forward(&mut self, delta: usize) {
        for _ in 0 .. delta {
            if let Some(c) = self.current {
                self.pos += c.len_utf8();
                self.current = self.char_at(self.pos);
            } else {
                panic!("lexer lexing too far")
            }
        }
    }

    fn move_backward(&mut self, delta: usize) {
        for _ in 0 .. delta {
            if let Some(c) = self.current {
                self.pos -= c.len_utf8();
                self.current = self.char_at(self.pos);
            } else {
                panic!("lexer lexing too far")
            }
        }
    }

    fn char_at(&self, pos: usize) -> Option<char> {
        self.source[pos ..].chars().next()
    }
}

impl<'a> Iterator for Lexer<'a> {
    type Item = Token;

    fn next(&mut self) -> Option<Token> {

        self.move_whitespace();
        self.token_pos = self.pos;

        let c = match self.current {
            Some(c) => c,
            None    => return None,
        };

        let token_type = if c.is_alphabetic() {
            match self.lex_term() {
                "if"     => TokenType::If,
                "else"   => TokenType::Else,
                "for"    => TokenType::For,
                "true"   => TokenType::Bool(true),
                "false"  => TokenType::Bool(false),
                "return" => TokenType::Return,
                "this"   => TokenType::This,
                _        => TokenType::Identifier(
                                String::from(self.lex_term())
                            ),
            }
        } else if c.is_digit(10) {
            TokenType::NumberLiteral(
                String::from(self.lex_number())
            )
        } else {
            match bin_op(&self.lex_bin_op()) {
                Some(o) => TokenType::BinaryOp(o),
                None    => {
                    self.move_backward(1);

                    match c {
                        '"' => TokenType::StringLiteral(
                                    String::from(self.lex_string())
                                ),
                        '['  => TokenType::LBracket,
                        ']'  => TokenType::RBracket,
                        '('  => TokenType::LParen,
                        ')'  => TokenType::RParen,
                        '{'  => TokenType::LBrace,
                        '}'  => TokenType::RBrace,
                        ','  => TokenType::Comma,
                        ':'  => TokenType::Colon,
                        ';'  => TokenType::Semicolon,
                        '.'  => TokenType::Period,
                        '='  => TokenType::Assign,
                        _    => TokenType::Invalid
                    }
                },
            }
        };

        Some(Token::new(token_type, self.token_pos, self.pos))
    }
}

pub fn tokenize(source: &str) -> Vec<Token> {
    Lexer::new(source).collect()
}

fn identifier_worthy(c: char) -> bool {
    c.is_alphabetic() || c == '_'
                      || c == '?'
}

fn bin_op(v: &str) -> Option<(BinaryOp, u8)> {
    match v {
        "*"  => Some((BinaryOp::Mul, 1)),
        "/"  => Some((BinaryOp::Div, 1)),
        "+"  => Some((BinaryOp::Plus, 2)),
        "-"  => Some((BinaryOp::Minus, 2)),
        "==" => Some((BinaryOp::Equals, 4)),
        "~=" => Some((BinaryOp::NotEquals, 4)),
        "<"  => Some((BinaryOp::Lt, 4)),
        ">"  => Some((BinaryOp::Gt, 4)),
        "<=" => Some((BinaryOp::GtEquals, 4)),
        ">=" => Some((BinaryOp::LtEquals, 4)),
        _    => None,
    }
}<|endoftext|>


<commit_before>//! Test map generators by displaying generated text maps in console.

extern crate calx;
extern crate rand;
extern crate world;

use calx::Prefab;
use rand::Rng;
use std::collections::HashMap;
use std::iter::FromIterator;
use world::{Room, Sector};
use world::mapgen::{self, MapGen, Point2D, Vault, VaultCell};

struct TestMap {
    terrain: HashMap<Point2D, char>,
}

impl TestMap {
    pub fn new() -> TestMap {
        let mut terrain = HashMap::new();

        let sector = Sector::new(0, 0, 0);
        let sector_origin = sector.origin();
        let mapgen_origin = Point2D::zero();

        terrain.extend(
            sector
                .iter()
                .map(|loc| mapgen_origin + sector_origin.v2_at(loc).unwrap())
                .map(|p| (p, '*')),
        );

        TestMap { terrain }
    }
}

impl mapgen::Dungeon for TestMap {
    type Vault = Room;

    /// Return a random prefab room.
    fn sample_vault<R: Rng>(&mut self, rng: &mut R) -> Self::Vault { rng.gen() }

    /// Add a large open continuous region to dungeon.
    fn dig_chamber<I: IntoIterator<Item = Point2D>>(&mut self, area: I) {
        for pos in area {
            self.terrain.insert(pos, '.');
        }
    }

    /// Add a narrow corridor to dungeon.
    fn dig_corridor<I: IntoIterator<Item = Point2D>>(&mut self, path: I) {
        for pos in path {
            self.terrain.insert(pos, '▒');
        }
    }

    fn place_vault(&mut self, vault: &Self::Vault, pos: Point2D) {
        let points: Vec<(Point2D, VaultCell)> = vault.get_shape();
        for &(p, c) in &points {
            let p = p + pos.to_vector();
            if !self.terrain.contains_key(&p) {
                continue;
            }
            let c = match c {
                VaultCell::Interior => '.',
                _ => '#',
            };
            self.terrain.insert(p, c);
        }
    }

    fn add_door(&mut self, pos: Point2D) { self.terrain.insert(pos, '+'); }

    fn add_up_stairs(&mut self, pos: Point2D) { self.terrain.insert(pos, '<'); }

    fn add_down_stairs(&mut self, pos: Point2D) { self.terrain.insert(pos, '>'); }
}

fn main() {
    let mut map = TestMap::new();

    let domain: Vec<Point2D> = map.terrain.keys().cloned().collect();
    mapgen::RoomsAndCorridors.dig(&mut rand::thread_rng(), &mut map, domain);

    println!(
        "{}",
        String::from(Prefab::from_iter(
            map.terrain.iter().map(|(p, t)| (p.to_vector(), *t))
        ))
    );
}
<commit_msg>Mapgen test pretty-printing<commit_after>//! Test map generators by displaying generated text maps in console.

extern crate calx;
extern crate rand;
extern crate world;

use calx::Prefab;
use rand::Rng;
use std::collections::HashMap;
use std::fmt::Write;
use std::iter::FromIterator;
use std::str;
use world::{Room, Sector};
use world::mapgen::{self, MapGen, Point2D, Vault, VaultCell};

struct TestMap {
    terrain: HashMap<Point2D, char>,
}

impl TestMap {
    pub fn new() -> TestMap {
        let mut terrain = HashMap::new();

        let sector = Sector::new(0, 0, 0);
        let sector_origin = sector.origin();
        let mapgen_origin = Point2D::zero();

        terrain.extend(
            sector
                .iter()
                .map(|loc| mapgen_origin + sector_origin.v2_at(loc).unwrap())
                .map(|p| (p, '*')),
        );

        TestMap { terrain }
    }
}

impl mapgen::Dungeon for TestMap {
    type Vault = Room;

    /// Return a random prefab room.
    fn sample_vault<R: Rng>(&mut self, rng: &mut R) -> Self::Vault { rng.gen() }

    /// Add a large open continuous region to dungeon.
    fn dig_chamber<I: IntoIterator<Item = Point2D>>(&mut self, area: I) {
        for pos in area {
            self.terrain.insert(pos, '.');
        }
    }

    /// Add a narrow corridor to dungeon.
    fn dig_corridor<I: IntoIterator<Item = Point2D>>(&mut self, path: I) {
        for pos in path {
            self.terrain.insert(pos, '▒');
        }
    }

    fn place_vault(&mut self, vault: &Self::Vault, pos: Point2D) {
        let points: Vec<(Point2D, VaultCell)> = vault.get_shape();
        for &(p, c) in &points {
            let p = p + pos.to_vector();
            if !self.terrain.contains_key(&p) {
                continue;
            }
            let c = match c {
                VaultCell::Interior => '.',
                _ => '#',
            };
            self.terrain.insert(p, c);
        }
    }

    fn add_door(&mut self, pos: Point2D) { self.terrain.insert(pos, '|'); }

    fn add_up_stairs(&mut self, pos: Point2D) { self.terrain.insert(pos, '<'); }

    fn add_down_stairs(&mut self, pos: Point2D) { self.terrain.insert(pos, '>'); }
}

fn main() {
    let mut map = TestMap::new();

    let domain: Vec<Point2D> = map.terrain.keys().cloned().collect();
    mapgen::RoomsAndCorridors.dig(&mut rand::thread_rng(), &mut map, domain);

    let map_text = String::from(Prefab::from_iter(
        map.terrain.iter().map(|(p, t)| (p.to_vector(), *t)),
    ));

    let mut new_text = String::new();

    for line in map_text.lines() {
        let mut line = line.to_string();
        let mut bytes: Vec<u8> = line.bytes().collect();
        for i in 1..(bytes.len() - 1) {
            for &(a, b, c) in &[
                ('*', '*', '*'),
                ('#', '#', '#'),
                ('*', '#', '*'),
                ('#', '*', '*'),
                ('|', '#', '#'),
                ('#', '|', '#'),
            ]
            {
                if bytes[i] == ' ' as u8 && bytes[i - 1] == a as u8 && bytes[i + 1] == b as u8 {
                    bytes[i] = c as u8;
                }
            }
        }
        let _ = writeln!(&mut new_text, "{}", str::from_utf8(&bytes).unwrap());
    }

    println!("{}", new_text);
}
<|endoftext|>


<commit_before>//! TODO Fill in

use cairo::{Format, ImageSurface};
use glib::translate::ToGlibPtr;
use rustwlc::Geometry;
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::object::{Object, Objectable};
use super::class::{self, Class};
use super::property::Property;

#[derive(Clone, Debug)]
pub struct DrawableState {
    pub surface: Option<ImageSurface>,
    geo: Geometry,
    // TODO Use this to determine whether we draw this or not
    refreshed: bool,
}

pub struct Drawable<'lua>(Table<'lua>);

impl_objectable!(Drawable, DrawableState);

impl Default for DrawableState {
    fn default() -> Self {
        DrawableState {
            surface: None,
            geo: Geometry::zero(),
            refreshed: false
        }
    }
}

impl <'lua> Drawable<'lua> {
    pub fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "drawable")?;
        let builder = Drawable::allocate(lua, class)?;
        // TODO Do properly
        let table = lua.create_table();
        table.set("geometry", lua.create_function(geometry))?;
        Ok(builder.add_to_meta(table)?.build())
    }

    pub fn get_geometry(&self) -> rlua::Result<Geometry> {
        let drawable = self.state()?;
        Ok(drawable.geo)
    }

    pub fn get_surface(&self) -> rlua::Result<Value<'lua>> {
        let drawable = self.state()?;
        Ok(match drawable.surface {
            None => Value::Nil,
            Some(ref surface) => {
                let stash = surface.to_glib_none();
                let ptr = stash.0 as _;
                // So that it lives _forever_ heheheh.
                ::std::mem::forget(stash);
                Value::LightUserData(LightUserData(ptr))
            }
        })
    }

    /// Sets the geometry, and allocates a new surface.
    pub fn set_geometry(&mut self, geometry: Geometry) -> rlua::Result<()> {
        use rlua::Error::RuntimeError;
        let mut drawable = self.state()?;
        let size_changed = drawable.geo != geometry;
        drawable.geo = geometry;
        if size_changed {
            drawable.surface = None;
            drawable.refreshed = false;
            let size = geometry.size;
            if size.w > 0 && size.h > 0 {
                drawable.surface = Some(ImageSurface::create(Format::ARgb32,
                                                        size.w as i32,
                                                        size.h as i32)
                    .map_err(|err| RuntimeError(format!("Could not allocate {:?}", err)))?);
                // TODO emity property::surface
            }
        }
        self.set_state(drawable)
    }
}

impl Display for DrawableState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Drawable: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Drawable<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for DrawableState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::builder(lua, Some(Rc::new(Drawable::new)), None, None)?
        .method("geometry".into(), lua.create_function(geometry))?
        .property(Property::new("surface".into(),
                                None,
                                Some(lua.create_function(get_surface)),
                                None))?
        .save_class("drawable")?
        .build()
}

fn get_surface<'lua>(_: &'lua Lua, table: Table<'lua>) -> rlua::Result<Value<'lua>> {
    let drawable = Drawable::cast(table.clone().into())?;
    drawable.get_surface()
}

fn geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    use rustwlc::{Point, Size};
    let drawable = Drawable::cast(table.into())?;
    let geometry = drawable.get_geometry()?;
    let Point { x, y } = geometry.origin;
    let Size { w, h } = geometry.size;
    let table = lua.create_table();
    table.set("x", x)?;
    table.set("y", y)?;
    table.set("width", w)?;
    table.set("height", h)?;
    Ok(table)
}
<commit_msg>drawable: Implement refresh<commit_after>//! TODO Fill in

use cairo::{Format, ImageSurface};
use glib::translate::ToGlibPtr;
use rustwlc::Geometry;
use std::fmt::{self, Display, Formatter};
use std::default::Default;
use std::rc::Rc;
use rlua::{self, Table, Lua, UserData, ToLua, Value, LightUserData};
use super::object::{Object, Objectable};
use super::class::{self, Class};
use super::property::Property;

#[derive(Clone, Debug)]
pub struct DrawableState {
    pub surface: Option<ImageSurface>,
    geo: Geometry,
    // TODO Use this to determine whether we draw this or not
    refreshed: bool,
}

pub struct Drawable<'lua>(Table<'lua>);

impl_objectable!(Drawable, DrawableState);

impl Default for DrawableState {
    fn default() -> Self {
        DrawableState {
            surface: None,
            geo: Geometry::zero(),
            refreshed: false
        }
    }
}

impl <'lua> Drawable<'lua> {
    pub fn new(lua: &Lua) -> rlua::Result<Object> {
        let class = class::class_setup(lua, "drawable")?;
        let builder = Drawable::allocate(lua, class)?;
        // TODO Do properly
        let table = lua.create_table();
        table.set("geometry", lua.create_function(geometry))?;
        table.set("refresh", lua.create_function(refresh))?;
        Ok(builder.add_to_meta(table)?.build())
    }

    pub fn get_geometry(&self) -> rlua::Result<Geometry> {
        let drawable = self.state()?;
        Ok(drawable.geo)
    }

    pub fn get_surface(&self) -> rlua::Result<Value<'lua>> {
        let drawable = self.state()?;
        Ok(match drawable.surface {
            None => Value::Nil,
            Some(ref surface) => {
                let stash = surface.to_glib_none();
                let ptr = stash.0 as _;
                // So that it lives _forever_ heheheh.
                ::std::mem::forget(stash);
                Value::LightUserData(LightUserData(ptr))
            }
        })
    }

    /// Sets the geometry, and allocates a new surface.
    pub fn set_geometry(&mut self, geometry: Geometry) -> rlua::Result<()> {
        use rlua::Error::RuntimeError;
        let mut drawable = self.state()?;
        let size_changed = drawable.geo != geometry;
        drawable.geo = geometry;
        if size_changed {
            drawable.surface = None;
            drawable.refreshed = false;
            let size = geometry.size;
            if size.w > 0 && size.h > 0 {
                drawable.surface = Some(ImageSurface::create(Format::ARgb32,
                                                        size.w as i32,
                                                        size.h as i32)
                    .map_err(|err| RuntimeError(format!("Could not allocate {:?}", err)))?);
                // TODO emity property::surface
            }
        }
        self.set_state(drawable)
    }

    /// Signals that the drawable's surface was updated.
    pub fn refresh(&mut self) -> rlua::Result<()> {
        let mut drawable = self.state()?;
        drawable.refreshed = true;
        self.set_state(drawable)
    }
}

impl Display for DrawableState {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Drawable: {:p}", self)
    }
}

impl <'lua> ToLua<'lua> for Drawable<'lua> {
    fn to_lua(self, lua: &'lua Lua) -> rlua::Result<Value<'lua>> {
        self.0.to_lua(lua)
    }
}

impl UserData for DrawableState {}

pub fn init(lua: &Lua) -> rlua::Result<Class> {
    Class::builder(lua, Some(Rc::new(Drawable::new)), None, None)?
        .method("geometry".into(), lua.create_function(geometry))?
        .property(Property::new("surface".into(),
                                None,
                                Some(lua.create_function(get_surface)),
                                None))?
        .save_class("drawable")?
        .build()
}

fn get_surface<'lua>(_: &'lua Lua, table: Table<'lua>) -> rlua::Result<Value<'lua>> {
    let drawable = Drawable::cast(table.clone().into())?;
    drawable.get_surface()
}

fn geometry<'lua>(lua: &'lua Lua, table: Table<'lua>) -> rlua::Result<Table<'lua>> {
    use rustwlc::{Point, Size};
    let drawable = Drawable::cast(table.into())?;
    let geometry = drawable.get_geometry()?;
    let Point { x, y } = geometry.origin;
    let Size { w, h } = geometry.size;
    let table = lua.create_table();
    table.set("x", x)?;
    table.set("y", y)?;
    table.set("width", w)?;
    table.set("height", h)?;
    Ok(table)
}

fn refresh<'lua>(_: &'lua Lua, table: Table<'lua>) -> rlua::Result<()> {
    Drawable::cast(table.into())?.refresh()
}
<|endoftext|>


<commit_before>extern crate image;
extern crate texture_packer;

use std::{fs::File, path::Path};
use texture_packer::{
    exporter::ImageExporter, importer::ImageImporter, texture::Texture, MultiTexturePacker,
    TexturePacker, TexturePackerConfig,
};

fn main() {
    //
    // Perform texture packing
    //
    let config = TexturePackerConfig {
        max_width: 400,
        max_height: 400,
        allow_rotation: false,
        texture_outlines: true,
        border_padding: 2,
        ..Default::default()
    };

    // single atlas
    {
        let mut packer = TexturePacker::new_skyline(config);

        for i in 1..11 {
            let name = format!("{}.png", i);
            let path = format!("examples/assets/{}", name);
            let path = Path::new(&path);
            let texture = ImageImporter::import_from_file(&path)
                .expect("Unable to import file. Run this example with --features=\"png\"");

            packer.pack_own(name, texture).unwrap();
        }

        //
        // Print the information
        //
        println!("Dimensions : {}x{}", packer.width(), packer.height());
        for (name, frame) in packer.get_frames() {
            println!("  {:7} : {:?}", name, frame.frame);
        }

        //
        // Save the result
        //
        let exporter = ImageExporter::export(&packer).unwrap();
        let mut file = File::create("examples/output/skyline-packer-output.png").unwrap();
        exporter
            .write_to(&mut file, image::ImageFormat::Png)
            .unwrap();
    }

    // multiple atlases
    {
        let mut packer = MultiTexturePacker::new_skyline(config);

        for i in 1..11 {
            let name = format!("{}.png", i);
            let path = format!("examples/assets/{}", name);
            let path = Path::new(&path);
            let texture = ImageImporter::import_from_file(&path).unwrap();

            packer.pack_own(format!("A{}", i), texture.clone()).unwrap();
            packer.pack_own(format!("B{}", i), texture).unwrap();
        }

        for (i, page) in packer.get_pages().into_iter().enumerate() {
            //
            // Print the information
            //
            println!("#{} | Dimensions : {}x{}", i, page.width(), page.height());
            for (name, frame) in page.get_frames() {
                println!("#{} |   {:7} : {:?}", i, name, frame.frame);
            }

            //
            // Save the result
            //
            let exporter = ImageExporter::export(page).unwrap();
            let mut file = File::create(&format!(
                "examples/output/skyline-multi-packer-output-{}.png",
                i
            ))
            .unwrap();
            exporter
                .write_to(&mut file, image::ImageFormat::Png)
                .unwrap();
        }
    }
}
<commit_msg>example: Store output only in the target directory<commit_after>extern crate image;
extern crate texture_packer;

use std::{
    fs::{self, File},
    path::Path,
};
use texture_packer::{
    exporter::ImageExporter, importer::ImageImporter, texture::Texture, MultiTexturePacker,
    TexturePacker, TexturePackerConfig,
};

fn main() {
    fs::create_dir_all("target/output").unwrap();

    //
    // Perform texture packing
    //
    let config = TexturePackerConfig {
        max_width: 400,
        max_height: 400,
        allow_rotation: false,
        texture_outlines: true,
        border_padding: 2,
        ..Default::default()
    };

    // single atlas
    {
        let mut packer = TexturePacker::new_skyline(config);

        for i in 1..11 {
            let name = format!("{}.png", i);
            let path = format!("examples/assets/{}", name);
            let path = Path::new(&path);
            let texture = ImageImporter::import_from_file(&path)
                .expect("Unable to import file. Run this example with --features=\"png\"");

            packer.pack_own(name, texture).unwrap();
        }

        //
        // Print the information
        //
        println!("Dimensions : {}x{}", packer.width(), packer.height());
        for (name, frame) in packer.get_frames() {
            println!("  {:7} : {:?}", name, frame.frame);
        }

        //
        // Save the result
        //
        let exporter = ImageExporter::export(&packer).unwrap();
        let mut file = File::create("target/output/skyline-packer-output.png").unwrap();
        exporter
            .write_to(&mut file, image::ImageFormat::Png)
            .unwrap();

        println!("Output texture stored in {:?}", file);
    }

    // multiple atlases
    {
        let mut packer = MultiTexturePacker::new_skyline(config);

        for i in 1..11 {
            let name = format!("{}.png", i);
            let path = format!("examples/assets/{}", name);
            let path = Path::new(&path);
            let texture = ImageImporter::import_from_file(&path).unwrap();

            packer.pack_own(format!("A{}", i), texture.clone()).unwrap();
            packer.pack_own(format!("B{}", i), texture).unwrap();
        }

        for (i, page) in packer.get_pages().into_iter().enumerate() {
            //
            // Print the information
            //
            println!("#{} | Dimensions : {}x{}", i, page.width(), page.height());
            for (name, frame) in page.get_frames() {
                println!("#{} |   {:7} : {:?}", i, name, frame.frame);
            }

            //
            // Save the result
            //
            let exporter = ImageExporter::export(page).unwrap();
            let mut file = File::create(&format!(
                "target/output/skyline-multi-packer-output-{}.png",
                i
            ))
            .unwrap();
            exporter
                .write_to(&mut file, image::ImageFormat::Png)
                .unwrap();

            println!("Multi output texture stored in {:?}", file);
        }
    }
}
<|endoftext|>


<commit_before>extern crate rustc_serialize;
extern crate libc;
extern crate scan_dir;
extern crate argparse;
extern crate quire;
extern crate env_logger;
#[macro_use] extern crate log;
#[macro_use] extern crate lithos;

use std::env;
use std::fs::{metadata};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::default::Default;
use std::collections::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, Print};
use quire::parse_config;

use lithos::utils::{in_range, in_mapping, check_mapping, relative};
use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::container_config::ContainerConfig;
use lithos::child_config::ChildConfig;
use lithos::network::{get_host_name, get_host_ip};
use lithos::id_map::{IdMapExt};

static EXIT_STATUS: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! err {
    ( $( $x:expr ),* ) => {
        {
            error!($($x),*);
            EXIT_STATUS.store(1,  Ordering::SeqCst);
        }
    }
}

fn check_master_config(master: &MasterConfig, verbose: bool) {
    // TODO(tailhook) maybe check host only if we need it for hosts file
    match get_host_name() {
        Ok(hostname) => {
            if verbose {
                println!("Hostname is {}", hostname);
            }
        }
        Err(e) => {
            err!("Can't get hostname: {}", e);
        }
    }
    match get_host_ip() {
        Ok(ipaddr) => {
            if verbose {
                println!("IPAddr is {}", ipaddr);
            }
        }
        Err(e) => {
            err!("Can't get IPAddress: {}", e);
        }
    }

    if metadata(&master.devfs_dir).is_err() {
        err!("Devfs dir ({:?}) must exist and contain device nodes",
            master.devfs_dir);
    }
}

fn check_sandbox_config(sandbox: &SandboxConfig) {
    if sandbox.allow_users.len() == 0 {
        err!("No allowed users range. Please add `allow-users: [1-1000]`");
    }
    if sandbox.allow_groups.len() == 0 {
        err!("No allowed groups range. Please add `allow-groups: [1-1000]`");
    }
    // TODO(tailhook) check allow_users/allow_groups against uid_map/gid_map
}

fn check(config_file: &Path, verbose: bool,
    sandbox_name: Option<String>, alter_config: Option<PathBuf>)
{
    let mut alter_config = alter_config;
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            err!("Can't parse config: {}", e);
            return;
        }
    };

    check_master_config(&master, verbose);

    let config_dir = config_file.parent().unwrap().join(&master.sandboxes_dir);
    scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, current_fn) in yamls {
            // strip yaml suffix
            let current_name = &current_fn[..current_fn.len()-5];
            let sandbox: SandboxConfig = match parse_config(&entry.path(),
                &SandboxConfig::validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    err!("Can't parse config: {}", e);
                    continue;
                }
            };
            check_sandbox_config(&sandbox);

            let default_config = config_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&current_fn)));
            let config_file = match (current_name, &sandbox_name)
            {
                (name, &Some(ref t)) if name == t
                => alter_config.take().unwrap_or(default_config),
                _ => default_config,
            };

            debug!("Checking {:?}", config_file);
            let all_children: BTreeMap<String, ChildConfig>;
            all_children = match parse_config(&config_file,
                &ChildConfig::mapping_validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    err!("Can't read child config {:?}: {}", config_file, e);
                    continue;
                }
            };
            for (ref child_name, ref child_cfg) in all_children.iter() {
                let cfg_path = Path::new(&child_cfg.config);
                if !cfg_path.is_absolute() {
                    err!("Config path must be absolute");
                    continue;
                }
                debug!("Opening config for {:?}", child_name);
                let config: ContainerConfig = match parse_config(
                    &sandbox.image_dir
                        .join(&child_cfg.image)
                        .join(&relative(cfg_path, &Path::new("/"))),
                    &ContainerConfig::validator(), Default::default()) {
                    Ok(cfg) => cfg,
                    Err(e) => {
                        err!("Can't read child config {:?}: {}.\n\
                            Sometimes the reason of reading configuration is \
                            absolute symlinks for config, in that case it may \
                            work in real daemon, but better fix it.",
                            child_name, e);
                        continue;
                    }
                };
                if config.uid_map.len() > 0 {
                    if sandbox.uid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                    if !in_mapping(&config.uid_map, config.user_id) {
                        err!("User is not in mapped range (uid: {})",
                            config.user_id);
                    }
                } else {
                    if sandbox.uid_map.len() > 0 {
                        if sandbox.uid_map.map_id(config.user_id).is_none() {
                            err!("User is not in mapped range \
                                (uid: {})",
                                config.user_id);
                        }
                    }
                    if !in_range(&sandbox.allow_users, config.user_id) {
                        err!("User is not in allowed range (uid: {})",
                            config.user_id);
                    }
                }
                if config.gid_map.len() > 0 {
                    if sandbox.gid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                    if !in_mapping(&config.gid_map, config.group_id) {
                        err!("Group is not in mapped range (gid: {})",
                            config.user_id);
                    }
                } else {
                    if sandbox.gid_map.len() > 0 {
                        if sandbox.gid_map.map_id(config.group_id).is_none() {
                            err!("Group is not in mapped range \
                                (gid: {})",
                                config.group_id);
                        }
                    }
                    if !in_range(&sandbox.allow_groups, config.group_id) {
                        err!("Group is not in allowed range (gid: {})",
                            config.group_id);
                    }
                }
                if !check_mapping(&sandbox.allow_users, &config.uid_map) {
                    err!("Bad uid mapping (probably doesn't match allow_users)");
                }
                if !check_mapping(&sandbox.allow_groups, &config.gid_map) {
                    err!("Bad gid mapping (probably doesn't match allow_groups)");
                }
            }
        }
    }).map_err(|e| {
        err!("Can't read config directory {:?}: {}", config_dir, e);
    }).ok();
    if alter_config.is_some() {
        err!("Tree {:?} is not used", sandbox_name);
    }
}

fn check_binaries() {
    let dir = match env::current_exe().ok()
        .and_then(|x| x.parent().map(|x| x.to_path_buf()))
    {
        Some(dir) => dir,
        None => {
            err!("Can't find out exe path");
            return;
        }
    };
    if metadata(&dir.join("lithos_tree")).is_err() {
        err!("Can't find lithos_tree binary");
    }
    if metadata(&dir.join("lithos_knot")).is_err() {
        err!("Can't find lithos_knot binary");
    }
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();

    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut alter_config = None;
    let mut sandbox_name = None;
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Checks if lithos configuration is ok");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut alter_config)
          .add_option(&["--alternate-config"], ParseOption,
            "Name of the alterate file name with configs.
             Useful to test configuration file before
             switching it to be primary one.
             You must also specify --sandbox.")
          .metavar("DIR");
        ap.refer(&mut sandbox_name)
          .add_option(&["--sandbox", "--sandbox-name",
            // Compatibility names
            "-T", "--tree", "--subtree-name",
            ], ParseOption,
            "Name of the sandbox for which --config-dir takes effect")
          .metavar("NAME");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }
    if alter_config.is_some() && sandbox_name.is_none() {
        err!("Please specify --sandbox if you use --dir");
    }
    check_binaries();
    check(&config_file, verbose, sandbox_name, alter_config);
    exit(EXIT_STATUS.load(Ordering::SeqCst) as i32);
}
<commit_msg>Implement `--check-container` option for `lithos_check`<commit_after>extern crate rustc_serialize;
extern crate libc;
extern crate scan_dir;
extern crate argparse;
extern crate quire;
extern crate env_logger;
#[macro_use] extern crate log;
#[macro_use] extern crate lithos;

use std::env;
use std::fs::{metadata};
use std::path::{Path, PathBuf};
use std::process::exit;
use std::default::Default;
use std::collections::BTreeMap;
use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT};

use argparse::{ArgumentParser, Parse, ParseOption, StoreTrue, Print, Collect};
use quire::parse_config;

use lithos::utils::{in_range, in_mapping, check_mapping, relative};
use lithos::master_config::MasterConfig;
use lithos::sandbox_config::SandboxConfig;
use lithos::container_config::ContainerConfig;
use lithos::child_config::ChildConfig;
use lithos::network::{get_host_name, get_host_ip};
use lithos::id_map::{IdMapExt};

static EXIT_STATUS: AtomicUsize = ATOMIC_USIZE_INIT;

macro_rules! err {
    ( $( $x:expr ),* ) => {
        {
            error!($($x),*);
            EXIT_STATUS.store(1,  Ordering::SeqCst);
        }
    }
}

fn check_master_config(master: &MasterConfig, verbose: bool) {
    // TODO(tailhook) maybe check host only if we need it for hosts file
    match get_host_name() {
        Ok(hostname) => {
            if verbose {
                println!("Hostname is {}", hostname);
            }
        }
        Err(e) => {
            err!("Can't get hostname: {}", e);
        }
    }
    match get_host_ip() {
        Ok(ipaddr) => {
            if verbose {
                println!("IPAddr is {}", ipaddr);
            }
        }
        Err(e) => {
            err!("Can't get IPAddress: {}", e);
        }
    }

    if metadata(&master.devfs_dir).is_err() {
        err!("Devfs dir ({:?}) must exist and contain device nodes",
            master.devfs_dir);
    }
}

fn check_sandbox_config(sandbox: &SandboxConfig) {
    if sandbox.allow_users.len() == 0 {
        err!("No allowed users range. Please add `allow-users: [1-1000]`");
    }
    if sandbox.allow_groups.len() == 0 {
        err!("No allowed groups range. Please add `allow-groups: [1-1000]`");
    }
    // TODO(tailhook) check allow_users/allow_groups against uid_map/gid_map
}

fn check_container(config_file: &Path) -> Result<ContainerConfig, ()>
{
    // Only checks things that can be checked without other configs
    let config: ContainerConfig = match parse_config(config_file,
        &ContainerConfig::validator(), Default::default())
    {
        Ok(cfg) => cfg,
        Err(e) => {
            err!("Can't read child config {:?}: {}", config_file, e);
            return Err(());
        }
    };
    if config.uid_map.len() > 0 {
        if !in_mapping(&config.uid_map, config.user_id) {
            err!("User is not in mapped range (uid: {})",
                config.user_id);
        }
    }
    if config.gid_map.len() > 0 {
        if !in_mapping(&config.gid_map, config.group_id) {
            err!("Group is not in mapped range (gid: {})",
                config.user_id);
        }
    }
    Ok(config)
}

fn check(config_file: &Path, verbose: bool,
    sandbox_name: Option<String>, alter_config: Option<PathBuf>)
{
    let mut alter_config = alter_config;
    let master: MasterConfig = match parse_config(&config_file,
        &MasterConfig::validator(), Default::default()) {
        Ok(cfg) => cfg,
        Err(e) => {
            err!("Can't parse config: {}", e);
            return;
        }
    };

    check_master_config(&master, verbose);

    let config_dir = config_file.parent().unwrap().join(&master.sandboxes_dir);
    scan_dir::ScanDir::files().read(&config_dir, |iter| {
        let yamls = iter.filter(|&(_, ref name)| name.ends_with(".yaml"));
        for (entry, current_fn) in yamls {
            // strip yaml suffix
            let current_name = &current_fn[..current_fn.len()-5];
            let sandbox: SandboxConfig = match parse_config(&entry.path(),
                &SandboxConfig::validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    err!("Can't parse config: {}", e);
                    continue;
                }
            };
            check_sandbox_config(&sandbox);

            let default_config = config_file.parent().unwrap()
                .join(&master.processes_dir)
                .join(sandbox.config_file.as_ref().unwrap_or(
                    &PathBuf::from(&current_fn)));
            let config_file = match (current_name, &sandbox_name)
            {
                (name, &Some(ref t)) if name == t
                => alter_config.take().unwrap_or(default_config),
                _ => default_config,
            };

            debug!("Checking {:?}", config_file);
            let all_children: BTreeMap<String, ChildConfig>;
            all_children = match parse_config(&config_file,
                &ChildConfig::mapping_validator(), Default::default()) {
                Ok(cfg) => cfg,
                Err(e) => {
                    err!("Can't read child config {:?}: {}", config_file, e);
                    continue;
                }
            };
            for (ref child_name, ref child_cfg) in all_children.iter() {
                let cfg_path = Path::new(&child_cfg.config);
                if !cfg_path.is_absolute() {
                    err!("Config path must be absolute");
                    continue;
                }
                debug!("Opening config for {:?}", child_name);
                let config = match check_container(&sandbox.image_dir
                    .join(&child_cfg.image)
                    .join(&relative(cfg_path, &Path::new("/"))))
                {
                    Ok(config) => config,
                    Err(()) => continue,
                };
                if config.uid_map.len() > 0 {
                    if sandbox.uid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                } else {
                    if sandbox.uid_map.len() > 0 {
                        if sandbox.uid_map.map_id(config.user_id).is_none() {
                            err!("User is not in mapped range \
                                (uid: {})",
                                config.user_id);
                        }
                    }
                    if !in_range(&sandbox.allow_users, config.user_id) {
                        err!("User is not in allowed range (uid: {})",
                            config.user_id);
                    }
                }
                if config.gid_map.len() > 0 {
                    if sandbox.gid_map.len() > 0 {
                        err!("Can't have uid_maps in both the sandbox and a \
                              container itself");
                    }
                } else {
                    if sandbox.gid_map.len() > 0 {
                        if sandbox.gid_map.map_id(config.group_id).is_none() {
                            err!("Group is not in mapped range \
                                (gid: {})",
                                config.group_id);
                        }
                    }
                    if !in_range(&sandbox.allow_groups, config.group_id) {
                        err!("Group is not in allowed range (gid: {})",
                            config.group_id);
                    }
                }
                if !check_mapping(&sandbox.allow_users, &config.uid_map) {
                    err!("Bad uid mapping (probably doesn't match allow_users)");
                }
                if !check_mapping(&sandbox.allow_groups, &config.gid_map) {
                    err!("Bad gid mapping (probably doesn't match allow_groups)");
                }
            }
        }
    }).map_err(|e| {
        err!("Can't read config directory {:?}: {}", config_dir, e);
    }).ok();
    if alter_config.is_some() {
        err!("Tree {:?} is not used", sandbox_name);
    }
}

fn check_binaries() {
    let dir = match env::current_exe().ok()
        .and_then(|x| x.parent().map(|x| x.to_path_buf()))
    {
        Some(dir) => dir,
        None => {
            err!("Can't find out exe path");
            return;
        }
    };
    if metadata(&dir.join("lithos_tree")).is_err() {
        err!("Can't find lithos_tree binary");
    }
    if metadata(&dir.join("lithos_knot")).is_err() {
        err!("Can't find lithos_knot binary");
    }
}

fn main() {

    if env::var("RUST_LOG").is_err() {
        env::set_var("RUST_LOG", "warn");
    }
    env_logger::init().unwrap();

    let mut config_file = PathBuf::from("/etc/lithos/master.yaml");
    let mut verbose = false;
    let mut alter_config = None;
    let mut sandbox_name = None;
    let mut check_containers = Vec::<String>::new();
    {
        let mut ap = ArgumentParser::new();
        ap.set_description("Checks if lithos configuration is ok");
        ap.refer(&mut config_file)
          .add_option(&["-C", "--config"], Parse,
            "Name of the global configuration file \
             (default /etc/lithos/master.yaml)")
          .metavar("FILE");
        ap.refer(&mut verbose)
          .add_option(&["-v", "--verbose"], StoreTrue,
            "Verbose output");
        ap.refer(&mut alter_config)
          .add_option(&["--alternate-config"], ParseOption,
            "Name of the alterate file name with configs.
             Useful to test configuration file before
             switching it to be primary one.
             You must also specify --sandbox.")
          .metavar("FILE");
        ap.refer(&mut sandbox_name)
          .add_option(&["--sandbox", "--sandbox-name",
            // Compatibility names
            "-T", "--tree", "--subtree-name",
            ], ParseOption,
            "Name of the sandbox for which --config-dir takes effect")
          .metavar("NAME");
        ap.refer(&mut check_containers)
          .add_option(&["--check-container"], Collect, "
            Instead of checking full lithos configuration check the
            container's configuration in the FILE. This is useful to check
            container locally, where you don't have lithos configured,
            before actually uploading the container. Multiple files may be
            specified in multiple arguments.
            ")
          .metavar("FILE");
        ap.add_option(&["--version"],
            Print(env!("CARGO_PKG_VERSION").to_string()),
            "Show version");
        match ap.parse_args() {
            Ok(()) => {}
            Err(x) => {
                exit(x);
            }
        }
    }
    if alter_config.is_some() && sandbox_name.is_none() {
        err!("Please specify --sandbox if you use --dir");
    }
    if check_containers.len() > 0 {
        for file in &check_containers {
            check_container(Path::new(file)).ok();
        }
    } else {
        check_binaries();
        check(&config_file, verbose, sandbox_name, alter_config);
    }
    exit(EXIT_STATUS.load(Ordering::SeqCst) as i32);
}
<|endoftext|>