<commit_before>//! Pragma helpers

use std::ops::Deref;

use crate::error::Error;
use crate::ffi;
use crate::types::{ToSql, ToSqlOutput, ValueRef};
use crate::{Connection, DatabaseName, Result, Row, NO_PARAMS};

pub struct Sql {
    buf: String,
}

impl Sql {
    pub fn new() -> Sql {
        Sql { buf: String::new() }
    }

    pub fn push_pragma(
        &mut self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
    ) -> Result<()> {
        self.push_keyword("PRAGMA")?;
        self.push_space();
        if let Some(schema_name) = schema_name {
            self.push_schema_name(schema_name);
            self.push_dot();
        }
        self.push_keyword(pragma_name)
    }

    pub fn push_keyword(&mut self, keyword: &str) -> Result<()> {
        if !keyword.is_empty() && is_identifier(keyword) {
            self.buf.push_str(keyword);
            Ok(())
        } else {
            Err(Error::SqliteFailure(
                ffi::Error::new(ffi::SQLITE_MISUSE),
                Some(format!("Invalid keyword \"{}\"", keyword)),
            ))
        }
    }

    pub fn push_schema_name(&mut self, schema_name: DatabaseName<'_>) {
        match schema_name {
            DatabaseName::Main => self.buf.push_str("main"),
            DatabaseName::Temp => self.buf.push_str("temp"),
            DatabaseName::Attached(s) => self.push_identifier(s),
        };
    }

    pub fn push_identifier(&mut self, s: &str) {
        if is_identifier(s) {
            self.buf.push_str(s);
        } else {
            self.wrap_and_escape(s, '"');
        }
    }

    pub fn push_value(&mut self, value: &dyn ToSql) -> Result<()> {
        let value = value.to_sql()?;
        let value = match value {
            ToSqlOutput::Borrowed(v) => v,
            ToSqlOutput::Owned(ref v) => ValueRef::from(v),
            #[cfg(feature = "blob")]
            ToSqlOutput::ZeroBlob(_) => {
                return Err(Error::SqliteFailure(
                    ffi::Error::new(ffi::SQLITE_MISUSE),
                    Some(format!("Unsupported value \"{:?}\"", value)),
                ));
            }
            #[cfg(feature = "array")]
            ToSqlOutput::Array(_) => {
                return Err(Error::SqliteFailure(
                    ffi::Error::new(ffi::SQLITE_MISUSE),
                    Some(format!("Unsupported value \"{:?}\"", value)),
                ));
            }
        };
        match value {
            ValueRef::Integer(i) => {
                self.push_int(i);
            }
            ValueRef::Real(r) => {
                self.push_real(r);
            }
            ValueRef::Text(s) => {
                self.push_string_literal(s);
            }
            _ => {
                return Err(Error::SqliteFailure(
                    ffi::Error::new(ffi::SQLITE_MISUSE),
                    Some(format!("Unsupported value \"{:?}\"", value)),
                ));
            }
        };
        Ok(())
    }

    pub fn push_string_literal(&mut self, s: &str) {
        self.wrap_and_escape(s, '\'');
    }

    pub fn push_int(&mut self, i: i64) {
        self.buf.push_str(&i.to_string());
    }

    pub fn push_real(&mut self, f: f64) {
        self.buf.push_str(&f.to_string());
    }

    pub fn push_space(&mut self) {
        self.buf.push(' ');
    }

    pub fn push_dot(&mut self) {
        self.buf.push('.');
    }

    pub fn push_equal_sign(&mut self) {
        self.buf.push('=');
    }

    pub fn open_brace(&mut self) {
        self.buf.push('(');
    }

    pub fn close_brace(&mut self) {
        self.buf.push(')');
    }

    pub fn as_str(&self) -> &str {
        &self.buf
    }

    fn wrap_and_escape(&mut self, s: &str, quote: char) {
        self.buf.push(quote);
        let chars = s.chars();
        for ch in chars {
            // escape `quote` by doubling it
            if ch == quote {
                self.buf.push(ch);
            }
            self.buf.push(ch)
        }
        self.buf.push(quote);
    }
}

impl Deref for Sql {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl Connection {
    /// Query the current value of `pragma_name`.
    ///
    /// Some pragmas will return multiple rows/values which cannot be retrieved
    /// with this method.
    ///
    /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
    /// `SELECT user_version FROM pragma_user_version;`
    pub fn pragma_query_value<T, F>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        f: F,
    ) -> Result<T>
    where
        F: FnOnce(Row<'_, '_>) -> Result<T>,
    {
        let mut query = Sql::new();
        query.push_pragma(schema_name, pragma_name)?;
        let mut stmt = self.prepare(&query)?;
        let mut rows = stmt.query(NO_PARAMS)?;
        if let Some(result_row) = rows.next() {
            let row = result_row?;
            f(row)
        } else {
            Err(Error::QueryReturnedNoRows)
        }
    }

    /// Query the current rows/values of `pragma_name`.
    ///
    /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
    /// `SELECT * FROM pragma_collation_list;`
    pub fn pragma_query<F>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        mut f: F,
    ) -> Result<()>
    where
        F: FnMut(&Row<'_, '_>) -> Result<()>,
    {
        let mut query = Sql::new();
        query.push_pragma(schema_name, pragma_name)?;
        let mut stmt = self.prepare(&query)?;
        let mut rows = stmt.query(NO_PARAMS)?;
        while let Some(result_row) = rows.next() {
            let row = result_row?;
            f(&row)?;
        }
        Ok(())
    }

    /// Query the current value(s) of `pragma_name` associated to
    /// `pragma_value`.
    ///
    /// This method can be used with query-only pragmas which need an argument
    /// (e.g. `table_info('one_tbl')`) or pragmas which returns value(s)
    /// (e.g. `integrity_check`).
    ///
    /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
    /// `SELECT * FROM pragma_table_info(?);`
    pub fn pragma<F>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        pragma_value: &dyn ToSql,
        mut f: F,
    ) -> Result<()>
    where
        F: FnMut(&Row<'_, '_>) -> Result<()>,
    {
        let mut sql = Sql::new();
        sql.push_pragma(schema_name, pragma_name)?;
        // The argument may be either in parentheses
        // or it may be separated from the pragma name by an equal sign.
        // The two syntaxes yield identical results.
        sql.open_brace();
        sql.push_value(pragma_value)?;
        sql.close_brace();
        let mut stmt = self.prepare(&sql)?;
        let mut rows = stmt.query(NO_PARAMS)?;
        while let Some(result_row) = rows.next() {
            let row = result_row?;
            f(&row)?;
        }
        Ok(())
    }

    /// Set a new value to `pragma_name`.
    ///
    /// Some pragmas will return the updated value which cannot be retrieved
    /// with this method.
    pub fn pragma_update(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        pragma_value: &dyn ToSql,
    ) -> Result<()> {
        let mut sql = Sql::new();
        sql.push_pragma(schema_name, pragma_name)?;
        // The argument may be either in parentheses
        // or it may be separated from the pragma name by an equal sign.
        // The two syntaxes yield identical results.
        sql.push_equal_sign();
        sql.push_value(pragma_value)?;
        self.execute_batch(&sql)
    }

    /// Set a new value to `pragma_name` and return the updated value.
    ///
    /// Only few pragmas automatically return the updated value.
    pub fn pragma_update_and_check<F, T>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        pragma_value: &dyn ToSql,
        f: F,
    ) -> Result<T>
    where
        F: FnOnce(Row<'_, '_>) -> Result<T>,
    {
        let mut sql = Sql::new();
        sql.push_pragma(schema_name, pragma_name)?;
        // The argument may be either in parentheses
        // or it may be separated from the pragma name by an equal sign.
        // The two syntaxes yield identical results.
        sql.push_equal_sign();
        sql.push_value(pragma_value)?;
        let mut stmt = self.prepare(&sql)?;
        let mut rows = stmt.query(NO_PARAMS)?;
        if let Some(result_row) = rows.next() {
            let row = result_row?;
            f(row)
        } else {
            Err(Error::QueryReturnedNoRows)
        }
    }
}

fn is_identifier(s: &str) -> bool {
    let chars = s.char_indices();
    for (i, ch) in chars {
        if i == 0 {
            if !is_identifier_start(ch) {
                return false;
            }
        } else if !is_identifier_continue(ch) {
            return false;
        }
    }
    true
}

fn is_identifier_start(c: char) -> bool {
    (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z') || c > '\x7F'
}

fn is_identifier_continue(c: char) -> bool {
    c == '$'
        || (c >= '0' && c <= '9')
        || (c >= 'A' && c <= 'Z')
        || c == '_'
        || (c >= 'a' && c <= 'z')
        || c > '\x7F'
}

#[cfg(test)]
mod test {
    use super::Sql;
    use crate::pragma;
    use crate::{Connection, DatabaseName};

    #[test]
    fn pragma_query_value() {
        let db = Connection::open_in_memory().unwrap();
        let user_version: i32 = db
            .pragma_query_value(None, "user_version", |row| row.get_checked(0))
            .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    #[cfg(feature = "bundled")]
    fn pragma_func_query_value() {
        use crate::NO_PARAMS;

        let db = Connection::open_in_memory().unwrap();
        let user_version: i32 = db
            .query_row(
                "SELECT user_version FROM pragma_user_version",
                NO_PARAMS,
                |row| row.get(0),
            )
            .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    fn pragma_query_no_schema() {
        let db = Connection::open_in_memory().unwrap();
        let mut user_version = -1;
        db.pragma_query(None, "user_version", |row| {
            user_version = row.get_checked(0)?;
            Ok(())
        })
        .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    fn pragma_query_with_schema() {
        let db = Connection::open_in_memory().unwrap();
        let mut user_version = -1;
        db.pragma_query(Some(DatabaseName::Main), "user_version", |row| {
            user_version = row.get_checked(0)?;
            Ok(())
        })
        .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    fn pragma() {
        let db = Connection::open_in_memory().unwrap();
        let mut columns = Vec::new();
        db.pragma(None, "table_info", &"sqlite_master", |row| {
            let column: String = row.get_checked(1)?;
            columns.push(column);
            Ok(())
        })
        .unwrap();
        assert_eq!(5, columns.len());
    }

    #[test]
    #[cfg(feature = "bundled")]
    fn pragma_func() {
        let db = Connection::open_in_memory().unwrap();
        let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?)").unwrap();
        let mut columns = Vec::new();
        let mut rows = table_info.query(&["sqlite_master"]).unwrap();

        while let Some(row) = rows.next() {
            let row = row.unwrap();
            let column: String = row.get(1);
            columns.push(column);
        }
        assert_eq!(5, columns.len());
    }

    #[test]
    fn pragma_update() {
        let db = Connection::open_in_memory().unwrap();
        db.pragma_update(None, "user_version", &1).unwrap();
    }

    #[test]
    fn pragma_update_and_check() {
        let db = Connection::open_in_memory().unwrap();
        let journal_mode: String = db
            .pragma_update_and_check(None, "journal_mode", &"OFF", |row| row.get_checked(0))
            .unwrap();
        assert_eq!("off", &journal_mode);
    }

    #[test]
    fn is_identifier() {
        assert!(pragma::is_identifier("full"));
        assert!(pragma::is_identifier("r2d2"));
        assert!(!pragma::is_identifier("sp ce"));
        assert!(!pragma::is_identifier("semi;colon"));
    }

    #[test]
    fn double_quote() {
        let mut sql = Sql::new();
        sql.push_schema_name(DatabaseName::Attached(r#"schema";--"#));
        assert_eq!(r#""schema"";--""#, sql.as_str());
    }

    #[test]
    fn wrap_and_escape() {
        let mut sql = Sql::new();
        sql.push_string_literal("value'; --");
        assert_eq!("'value''; --'", sql.as_str());
    }
}
<commit_msg>Unify callback parameter signature<commit_after>//! Pragma helpers

use std::ops::Deref;

use crate::error::Error;
use crate::ffi;
use crate::types::{ToSql, ToSqlOutput, ValueRef};
use crate::{Connection, DatabaseName, Result, Row, NO_PARAMS};

pub struct Sql {
    buf: String,
}

impl Sql {
    pub fn new() -> Sql {
        Sql { buf: String::new() }
    }

    pub fn push_pragma(
        &mut self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
    ) -> Result<()> {
        self.push_keyword("PRAGMA")?;
        self.push_space();
        if let Some(schema_name) = schema_name {
            self.push_schema_name(schema_name);
            self.push_dot();
        }
        self.push_keyword(pragma_name)
    }

    pub fn push_keyword(&mut self, keyword: &str) -> Result<()> {
        if !keyword.is_empty() && is_identifier(keyword) {
            self.buf.push_str(keyword);
            Ok(())
        } else {
            Err(Error::SqliteFailure(
                ffi::Error::new(ffi::SQLITE_MISUSE),
                Some(format!("Invalid keyword \"{}\"", keyword)),
            ))
        }
    }

    pub fn push_schema_name(&mut self, schema_name: DatabaseName<'_>) {
        match schema_name {
            DatabaseName::Main => self.buf.push_str("main"),
            DatabaseName::Temp => self.buf.push_str("temp"),
            DatabaseName::Attached(s) => self.push_identifier(s),
        };
    }

    pub fn push_identifier(&mut self, s: &str) {
        if is_identifier(s) {
            self.buf.push_str(s);
        } else {
            self.wrap_and_escape(s, '"');
        }
    }

    pub fn push_value(&mut self, value: &dyn ToSql) -> Result<()> {
        let value = value.to_sql()?;
        let value = match value {
            ToSqlOutput::Borrowed(v) => v,
            ToSqlOutput::Owned(ref v) => ValueRef::from(v),
            #[cfg(feature = "blob")]
            ToSqlOutput::ZeroBlob(_) => {
                return Err(Error::SqliteFailure(
                    ffi::Error::new(ffi::SQLITE_MISUSE),
                    Some(format!("Unsupported value \"{:?}\"", value)),
                ));
            }
            #[cfg(feature = "array")]
            ToSqlOutput::Array(_) => {
                return Err(Error::SqliteFailure(
                    ffi::Error::new(ffi::SQLITE_MISUSE),
                    Some(format!("Unsupported value \"{:?}\"", value)),
                ));
            }
        };
        match value {
            ValueRef::Integer(i) => {
                self.push_int(i);
            }
            ValueRef::Real(r) => {
                self.push_real(r);
            }
            ValueRef::Text(s) => {
                self.push_string_literal(s);
            }
            _ => {
                return Err(Error::SqliteFailure(
                    ffi::Error::new(ffi::SQLITE_MISUSE),
                    Some(format!("Unsupported value \"{:?}\"", value)),
                ));
            }
        };
        Ok(())
    }

    pub fn push_string_literal(&mut self, s: &str) {
        self.wrap_and_escape(s, '\'');
    }

    pub fn push_int(&mut self, i: i64) {
        self.buf.push_str(&i.to_string());
    }

    pub fn push_real(&mut self, f: f64) {
        self.buf.push_str(&f.to_string());
    }

    pub fn push_space(&mut self) {
        self.buf.push(' ');
    }

    pub fn push_dot(&mut self) {
        self.buf.push('.');
    }

    pub fn push_equal_sign(&mut self) {
        self.buf.push('=');
    }

    pub fn open_brace(&mut self) {
        self.buf.push('(');
    }

    pub fn close_brace(&mut self) {
        self.buf.push(')');
    }

    pub fn as_str(&self) -> &str {
        &self.buf
    }

    fn wrap_and_escape(&mut self, s: &str, quote: char) {
        self.buf.push(quote);
        let chars = s.chars();
        for ch in chars {
            // escape `quote` by doubling it
            if ch == quote {
                self.buf.push(ch);
            }
            self.buf.push(ch)
        }
        self.buf.push(quote);
    }
}

impl Deref for Sql {
    type Target = str;

    fn deref(&self) -> &str {
        self.as_str()
    }
}

impl Connection {
    /// Query the current value of `pragma_name`.
    ///
    /// Some pragmas will return multiple rows/values which cannot be retrieved
    /// with this method.
    ///
    /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
    /// `SELECT user_version FROM pragma_user_version;`
    pub fn pragma_query_value<T, F>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        f: F,
    ) -> Result<T>
    where
        F: FnOnce(&Row<'_, '_>) -> Result<T>,
    {
        let mut query = Sql::new();
        query.push_pragma(schema_name, pragma_name)?;
        self.query_row(&query, NO_PARAMS, f)
    }

    /// Query the current rows/values of `pragma_name`.
    ///
    /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
    /// `SELECT * FROM pragma_collation_list;`
    pub fn pragma_query<F>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        mut f: F,
    ) -> Result<()>
    where
        F: FnMut(&Row<'_, '_>) -> Result<()>,
    {
        let mut query = Sql::new();
        query.push_pragma(schema_name, pragma_name)?;
        let mut stmt = self.prepare(&query)?;
        let mut rows = stmt.query(NO_PARAMS)?;
        while let Some(result_row) = rows.next() {
            let row = result_row?;
            f(&row)?;
        }
        Ok(())
    }

    /// Query the current value(s) of `pragma_name` associated to
    /// `pragma_value`.
    ///
    /// This method can be used with query-only pragmas which need an argument
    /// (e.g. `table_info('one_tbl')`) or pragmas which returns value(s)
    /// (e.g. `integrity_check`).
    ///
    /// Prefer [PRAGMA function](https://sqlite.org/pragma.html#pragfunc) introduced in SQLite 3.20:
    /// `SELECT * FROM pragma_table_info(?);`
    pub fn pragma<F>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        pragma_value: &dyn ToSql,
        mut f: F,
    ) -> Result<()>
    where
        F: FnMut(&Row<'_, '_>) -> Result<()>,
    {
        let mut sql = Sql::new();
        sql.push_pragma(schema_name, pragma_name)?;
        // The argument may be either in parentheses
        // or it may be separated from the pragma name by an equal sign.
        // The two syntaxes yield identical results.
        sql.open_brace();
        sql.push_value(pragma_value)?;
        sql.close_brace();
        let mut stmt = self.prepare(&sql)?;
        let mut rows = stmt.query(NO_PARAMS)?;
        while let Some(result_row) = rows.next() {
            let row = result_row?;
            f(&row)?;
        }
        Ok(())
    }

    /// Set a new value to `pragma_name`.
    ///
    /// Some pragmas will return the updated value which cannot be retrieved
    /// with this method.
    pub fn pragma_update(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        pragma_value: &dyn ToSql,
    ) -> Result<()> {
        let mut sql = Sql::new();
        sql.push_pragma(schema_name, pragma_name)?;
        // The argument may be either in parentheses
        // or it may be separated from the pragma name by an equal sign.
        // The two syntaxes yield identical results.
        sql.push_equal_sign();
        sql.push_value(pragma_value)?;
        self.execute_batch(&sql)
    }

    /// Set a new value to `pragma_name` and return the updated value.
    ///
    /// Only few pragmas automatically return the updated value.
    pub fn pragma_update_and_check<F, T>(
        &self,
        schema_name: Option<DatabaseName<'_>>,
        pragma_name: &str,
        pragma_value: &dyn ToSql,
        f: F,
    ) -> Result<T>
    where
        F: FnOnce(&Row<'_, '_>) -> Result<T>,
    {
        let mut sql = Sql::new();
        sql.push_pragma(schema_name, pragma_name)?;
        // The argument may be either in parentheses
        // or it may be separated from the pragma name by an equal sign.
        // The two syntaxes yield identical results.
        sql.push_equal_sign();
        sql.push_value(pragma_value)?;
        self.query_row(&sql, NO_PARAMS, f)
    }
}

fn is_identifier(s: &str) -> bool {
    let chars = s.char_indices();
    for (i, ch) in chars {
        if i == 0 {
            if !is_identifier_start(ch) {
                return false;
            }
        } else if !is_identifier_continue(ch) {
            return false;
        }
    }
    true
}

fn is_identifier_start(c: char) -> bool {
    (c >= 'A' && c <= 'Z') || c == '_' || (c >= 'a' && c <= 'z') || c > '\x7F'
}

fn is_identifier_continue(c: char) -> bool {
    c == '$'
        || (c >= '0' && c <= '9')
        || (c >= 'A' && c <= 'Z')
        || c == '_'
        || (c >= 'a' && c <= 'z')
        || c > '\x7F'
}

#[cfg(test)]
mod test {
    use super::Sql;
    use crate::pragma;
    use crate::{Connection, DatabaseName};

    #[test]
    fn pragma_query_value() {
        let db = Connection::open_in_memory().unwrap();
        let user_version: i32 = db
            .pragma_query_value(None, "user_version", |row| row.get(0))
            .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    #[cfg(feature = "bundled")]
    fn pragma_func_query_value() {
        use crate::NO_PARAMS;

        let db = Connection::open_in_memory().unwrap();
        let user_version: i32 = db
            .query_row(
                "SELECT user_version FROM pragma_user_version",
                NO_PARAMS,
                |row| row.get(0),
            )
            .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    fn pragma_query_no_schema() {
        let db = Connection::open_in_memory().unwrap();
        let mut user_version = -1;
        db.pragma_query(None, "user_version", |row| {
            user_version = row.get(0)?;
            Ok(())
        })
        .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    fn pragma_query_with_schema() {
        let db = Connection::open_in_memory().unwrap();
        let mut user_version = -1;
        db.pragma_query(Some(DatabaseName::Main), "user_version", |row| {
            user_version = row.get(0)?;
            Ok(())
        })
        .unwrap();
        assert_eq!(0, user_version);
    }

    #[test]
    fn pragma() {
        let db = Connection::open_in_memory().unwrap();
        let mut columns = Vec::new();
        db.pragma(None, "table_info", &"sqlite_master", |row| {
            let column: String = row.get(1)?;
            columns.push(column);
            Ok(())
        })
        .unwrap();
        assert_eq!(5, columns.len());
    }

    #[test]
    #[cfg(feature = "bundled")]
    fn pragma_func() {
        let db = Connection::open_in_memory().unwrap();
        let mut table_info = db.prepare("SELECT * FROM pragma_table_info(?)").unwrap();
        let mut columns = Vec::new();
        let mut rows = table_info.query(&["sqlite_master"]).unwrap();

        while let Some(row) = rows.next() {
            let row = row.unwrap();
            let column: String = row.get(1).unwrap();
            columns.push(column);
        }
        assert_eq!(5, columns.len());
    }

    #[test]
    fn pragma_update() {
        let db = Connection::open_in_memory().unwrap();
        db.pragma_update(None, "user_version", &1).unwrap();
    }

    #[test]
    fn pragma_update_and_check() {
        let db = Connection::open_in_memory().unwrap();
        let journal_mode: String = db
            .pragma_update_and_check(None, "journal_mode", &"OFF", |row| row.get(0))
            .unwrap();
        assert_eq!("off", &journal_mode);
    }

    #[test]
    fn is_identifier() {
        assert!(pragma::is_identifier("full"));
        assert!(pragma::is_identifier("r2d2"));
        assert!(!pragma::is_identifier("sp ce"));
        assert!(!pragma::is_identifier("semi;colon"));
    }

    #[test]
    fn double_quote() {
        let mut sql = Sql::new();
        sql.push_schema_name(DatabaseName::Attached(r#"schema";--"#));
        assert_eq!(r#""schema"";--""#, sql.as_str());
    }

    #[test]
    fn wrap_and_escape() {
        let mut sql = Sql::new();
        sql.push_string_literal("value'; --");
        assert_eq!("'value''; --'", sql.as_str());
    }
}
<|endoftext|>


<commit_before>use euclid::{self, vec2};
use space::{CellVector, Transformation, Space};
use std::collections::{hash_map, HashMap};
use std::error::Error;
use std::fmt;
use std::i32;

/// The text map character coordinate space.
struct TextSpace;

pub type TextVector = euclid::TypedVector2D<i32, TextSpace>;

// |  2 0 |
// | -1 1 |
//
// | 1/2 0 |
// | 1/2 1 |

impl Transformation for TextSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [(v[0] + v[1]) / 2, v[1]]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - v[1], v[1]]
    }
}

impl TextSpace {
    /// Which of the two possible map lattices is this vector in?
    pub fn in_even_lattice(v: TextVector) -> bool { (v.x + v.y) % 2 == 0 }
}

/// The on-screen minimap pixel coordinate space.
struct MinimapSpace;

// |  2  1 |
// | -2  1 |
//
// | 1/4  -1/4 |
// | 1/2   1/2 |

impl Transformation for MinimapSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [v[0] / 4 + v[1] / 2, v[1] / 2 - v[0] / 4]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - 2 * v[1], v[0] + v[1]]
    }
}

// Idea was there'd be both 'Floating' and 'Anchored' prefabs, but I don't think I actually have an
// use case for the 'Floating' one. So just using the 'Anchored' one and calling it a generic
// Prefab.

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum PrefabError {
    InvalidInput,
    MissingAnchor,
}

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

impl Error for PrefabError {
    fn description(&self) -> &str {
        match self {
            &PrefabError::InvalidInput => "Invalid input",
            &PrefabError::MissingAnchor => "Anchor not found in input",
        }
    }
}

// TODO: Once `std::convert::TryFrom` becomes stable, use that instead of this custom trait.

/// A trait for types that can be parsed into a map `Prefab`.
pub trait IntoPrefab<T> {
    fn try_into(self) -> Result<Prefab<T>, PrefabError>;
}

/// A piece of 2D map data with a fixed origin position.
///
/// # Examples
///
/// ```
/// # extern crate euclid;
/// # extern crate calx;
/// # fn main() {
///
/// use euclid::vec2;
/// use calx::{Prefab, IntoPrefab};
///
/// let string_map = Prefab::parse(r#"
///   1 2
///  3[4]5
///   6 7"#).expect("Failed to parse string map");
///
/// for &(c, p) in &[
///   ('1', (-1, -1)),
///   ('2', ( 0, -1)),
///   ('3', (-1,  0)),
///   ('4', ( 0,  0)),
///   ('5', ( 1,  0)),
///   ('6', ( 0,  1)),
///   ('7', ( 1,  1))] {
///     assert_eq!(Some(&c), string_map.get(vec2(p.0, p.1)));
/// }
///
/// # }
/// ```
#[derive(Clone)]
pub struct Prefab<T> {
    points: HashMap<CellVector, T>,
}

impl<T> Prefab<T> {
    pub fn parse<U: IntoPrefab<T>>(value: U) -> Result<Prefab<T>, PrefabError> { value.try_into() }

    pub fn get(&self, pos: CellVector) -> Option<&T> { self.points.get(&pos) }

    pub fn iter(&self) -> hash_map::Iter<CellVector, T> { self.points.iter() }
}

// Not using FromStr even when the input type is String, since there's a conversion family here
// where the sources can be strings or images and I want the interface to be uniform for both.

impl<S: Into<String>> IntoPrefab<char> for S {
    fn try_into(self) -> Result<Prefab<char>, PrefabError> {
        /// Recognize the point set that only contains the origin markup and return the
        /// corresponding origin value.
        fn is_origin(points: &HashMap<TextVector, char>) -> Option<TextVector> {
            if points.len() != 2 {
                return None;
            }
            let mut it = points.iter();
            let (mut p1, mut p2) = (it.next().unwrap(), it.next().unwrap());

            if p1.0.x > p2.0.x {
                ::std::mem::swap(&mut p1, &mut p2);
            }

            if *p2.0 - *p1.0 == vec2(2, 0) && *p1.1 == '[' && *p2.1 == ']' {
                Some(*p1.0 + vec2(1, 0))
            } else {
                None
            }
        }

        let value: String = self.into();

        let mut odds = HashMap::new();
        let mut evens = HashMap::new();
        for (y, line) in value.lines().enumerate() {
            for (x, c) in line.chars().enumerate() {
                if !c.is_whitespace() {
                    let vec = vec2(x as i32, y as i32);
                    if TextSpace::in_even_lattice(vec) {
                        evens.insert(vec, c);
                    } else {
                        odds.insert(vec, c);
                    }
                }
            }
        }

        let (offset, data) = if let Some(offset) = is_origin(&odds) {
            (offset, &evens)
        } else if let Some(offset) = is_origin(&evens) {
            (offset, &odds)
        } else {
            return Err(PrefabError::InvalidInput);
        };

        let mut points = HashMap::new();
        for text_data in data.iter() {
            // Set origin
            let text_pos = *text_data.0 - offset;
            // Store into cell space.
            points.insert(text_pos.to_cell_space(), *text_data.1);
        }

        Ok(Prefab { points })
    }
}

impl fmt::Display for Prefab<char> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let dummy_origin: (&CellVector, &char) = (&vec2(0, 0), &' ');
        // Ensure that the origin cell shows up in the printout even if it's blank in the prefab.
        let append = if self.iter().find(|&(&pos, _)| pos == vec2(0, 0)).is_none() {
            Some(dummy_origin)
        } else {
            None
        };

        // How far left does the textmap go?
        //
        // Subtract 1 from the result so that we can always fit the origin brackets on the leftmost
        // char if need be.
        let min_x = self.iter()
            .chain(append)
            .map(|(&pos, _)| TextVector::from_cell_space(pos).x)
            .min()
            .unwrap_or(0) - 1;

        // Arrange cells in print order.
        let mut sorted: Vec<(TextVector, char)> = self.iter()
            .chain(append)
            .map(|(&pos, &c)| (TextVector::from_cell_space(pos), c))
            .collect();

        sorted.sort_by(|a, b| (a.0.y, a.0.x).cmp(&(b.0.y, b.0.x)));

        if sorted.len() == 0 {
            return Ok(());
        }

        // Printing position.
        let mut print_y = sorted[0].0.y;
        let mut print_x = min_x;

        for &(pos, c) in &sorted {
            while print_y < pos.y {
                writeln!(f, "")?;
                print_x = min_x;
                print_y += 1;
            }

            while print_x < pos.x - 1 {
                write!(f, " ")?;
                print_x += 1;
            }

            debug_assert_eq!(print_y, pos.y);
            if pos == vec2(0, 0) {
                // Write origin markers around origin cell.
                debug_assert_eq!(print_x, pos.x - 1);
                write!(f, "[{}]", c)?;
                print_x += 3;
            } else {
                // Print x should be in pos.x - 1, except in the case right after the origin marker
                // when it's in pos.x.
                debug_assert!(print_x == pos.x - 1 || print_x == pos.x);
                if print_x < pos.x {
                    write!(f, " ")?;
                    print_x += 1;
                }
                write!(f, "{}", c)?;
                print_x += 1;
            }
        }

        Ok(())
    }
}

impl From<Prefab<char>> for String {
    fn from(prefab: Prefab<char>) -> String { format!("{}", prefab) }
}
<commit_msg>Prefab iterator conversions<commit_after>use euclid::{self, vec2};
use space::{CellVector, Transformation, Space};
use std::collections::{hash_map, HashMap};
use std::error::Error;
use std::fmt;
use std::i32;
use std::iter::{FromIterator, IntoIterator};

/// The text map character coordinate space.
struct TextSpace;

pub type TextVector = euclid::TypedVector2D<i32, TextSpace>;

// |  2 0 |
// | -1 1 |
//
// | 1/2 0 |
// | 1/2 1 |

impl Transformation for TextSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [(v[0] + v[1]) / 2, v[1]]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - v[1], v[1]]
    }
}

impl TextSpace {
    /// Which of the two possible map lattices is this vector in?
    pub fn in_even_lattice(v: TextVector) -> bool { (v.x + v.y) % 2 == 0 }
}

/// The on-screen minimap pixel coordinate space.
struct MinimapSpace;

// |  2  1 |
// | -2  1 |
//
// | 1/4  -1/4 |
// | 1/2   1/2 |

impl Transformation for MinimapSpace {
    type Element = i32;

    fn project<V: Into<[Self::Element; 2]>>(v: V) -> [i32; 2] {
        let v = v.into();
        [v[0] / 4 + v[1] / 2, v[1] / 2 - v[0] / 4]
    }

    fn unproject<V: Into<[i32; 2]>>(v: V) -> [Self::Element; 2] {
        let v = v.into();
        [2 * v[0] - 2 * v[1], v[0] + v[1]]
    }
}

// Idea was there'd be both 'Floating' and 'Anchored' prefabs, but I don't think I actually have an
// use case for the 'Floating' one. So just using the 'Anchored' one and calling it a generic
// Prefab.

#[derive(Copy, Clone, Eq, PartialEq, Debug)]
pub enum PrefabError {
    InvalidInput,
    MissingAnchor,
}

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

impl Error for PrefabError {
    fn description(&self) -> &str {
        match self {
            &PrefabError::InvalidInput => "Invalid input",
            &PrefabError::MissingAnchor => "Anchor not found in input",
        }
    }
}

// XXX: This is basically just a TryFrom, which isn't stable yet. Though I can derive it for things
// I can't derive TryFrom for, like the templatized GenericImage, because I'm controlling the trait
// here.

/// A trait for types that can be parsed into a map `Prefab`.
pub trait IntoPrefab<T> {
    fn try_into(self) -> Result<Prefab<T>, PrefabError>;
}

/// A piece of 2D map data with a fixed origin position.
///
/// # Examples
///
/// ```
/// # extern crate euclid;
/// # extern crate calx;
/// # fn main() {
///
/// use euclid::vec2;
/// use calx::{Prefab, IntoPrefab};
///
/// let string_map = Prefab::parse(r#"
///   1 2
///  3[4]5
///   6 7"#).expect("Failed to parse string map");
///
/// for &(c, p) in &[
///   ('1', (-1, -1)),
///   ('2', ( 0, -1)),
///   ('3', (-1,  0)),
///   ('4', ( 0,  0)),
///   ('5', ( 1,  0)),
///   ('6', ( 0,  1)),
///   ('7', ( 1,  1))] {
///     assert_eq!(Some(&c), string_map.get(vec2(p.0, p.1)));
/// }
///
/// # }
/// ```
#[derive(Clone)]
pub struct Prefab<T> {
    points: HashMap<CellVector, T>,
}

impl<T> Prefab<T> {
    pub fn parse<U: IntoPrefab<T>>(value: U) -> Result<Prefab<T>, PrefabError> { value.try_into() }

    pub fn get(&self, pos: CellVector) -> Option<&T> { self.points.get(&pos) }

    pub fn iter(&self) -> hash_map::Iter<CellVector, T> { self.points.iter() }
}

impl<T> FromIterator<(CellVector, T)> for Prefab<T> {
    fn from_iter<I: IntoIterator<Item = (CellVector, T)>>(iter: I) -> Self {
        Prefab { points: FromIterator::from_iter(iter) }
    }
}

impl<T> IntoIterator for Prefab<T> {
    type Item = (CellVector, T);
    type IntoIter = hash_map::IntoIter<CellVector, T>;

    fn into_iter(self) -> Self::IntoIter { self.points.into_iter() }
}

// Text prefabs

// Not using FromStr even when the input type is String, since there's a conversion family here
// where the sources can be strings or images and I want the interface to be uniform for both.

impl<S: Into<String>> IntoPrefab<char> for S {
    fn try_into(self) -> Result<Prefab<char>, PrefabError> {
        /// Recognize the point set that only contains the origin markup and return the
        /// corresponding origin value.
        fn is_origin(points: &HashMap<TextVector, char>) -> Option<TextVector> {
            if points.len() != 2 {
                return None;
            }
            let mut it = points.iter();
            let (mut p1, mut p2) = (it.next().unwrap(), it.next().unwrap());

            if p1.0.x > p2.0.x {
                ::std::mem::swap(&mut p1, &mut p2);
            }

            if *p2.0 - *p1.0 == vec2(2, 0) && *p1.1 == '[' && *p2.1 == ']' {
                Some(*p1.0 + vec2(1, 0))
            } else {
                None
            }
        }

        let value: String = self.into();

        let mut odds = HashMap::new();
        let mut evens = HashMap::new();
        for (y, line) in value.lines().enumerate() {
            for (x, c) in line.chars().enumerate() {
                if !c.is_whitespace() {
                    let vec = vec2(x as i32, y as i32);
                    if TextSpace::in_even_lattice(vec) {
                        evens.insert(vec, c);
                    } else {
                        odds.insert(vec, c);
                    }
                }
            }
        }

        let (offset, data) = if let Some(offset) = is_origin(&odds) {
            (offset, &evens)
        } else if let Some(offset) = is_origin(&evens) {
            (offset, &odds)
        } else {
            return Err(PrefabError::InvalidInput);
        };

        let mut points = HashMap::new();
        for text_data in data.iter() {
            // Set origin
            let text_pos = *text_data.0 - offset;
            // Store into cell space.
            points.insert(text_pos.to_cell_space(), *text_data.1);
        }

        Ok(Prefab { points })
    }
}

impl fmt::Display for Prefab<char> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let dummy_origin: (&CellVector, &char) = (&vec2(0, 0), &' ');
        // Ensure that the origin cell shows up in the printout even if it's blank in the prefab.
        let append = if self.iter().find(|&(&pos, _)| pos == vec2(0, 0)).is_none() {
            Some(dummy_origin)
        } else {
            None
        };

        // How far left does the textmap go?
        //
        // Subtract 1 from the result so that we can always fit the origin brackets on the leftmost
        // char if need be.
        let min_x = self.iter()
            .chain(append)
            .map(|(&pos, _)| TextVector::from_cell_space(pos).x)
            .min()
            .unwrap_or(0) - 1;

        // Arrange cells in print order.
        let mut sorted: Vec<(TextVector, char)> = self.iter()
            .chain(append)
            .map(|(&pos, &c)| (TextVector::from_cell_space(pos), c))
            .collect();

        sorted.sort_by(|a, b| (a.0.y, a.0.x).cmp(&(b.0.y, b.0.x)));

        if sorted.len() == 0 {
            return Ok(());
        }

        // Printing position.
        let mut print_y = sorted[0].0.y;
        let mut print_x = min_x;

        for &(pos, c) in &sorted {
            while print_y < pos.y {
                writeln!(f, "")?;
                print_x = min_x;
                print_y += 1;
            }

            while print_x < pos.x - 1 {
                write!(f, " ")?;
                print_x += 1;
            }

            debug_assert_eq!(print_y, pos.y);
            if pos == vec2(0, 0) {
                // Write origin markers around origin cell.
                debug_assert_eq!(print_x, pos.x - 1);
                write!(f, "[{}]", c)?;
                print_x += 3;
            } else {
                // Print x should be in pos.x - 1, except in the case right after the origin marker
                // when it's in pos.x.
                debug_assert!(print_x == pos.x - 1 || print_x == pos.x);
                if print_x < pos.x {
                    write!(f, " ")?;
                    print_x += 1;
                }
                write!(f, "{}", c)?;
                print_x += 1;
            }
        }

        Ok(())
    }
}

impl From<Prefab<char>> for String {
    fn from(prefab: Prefab<char>) -> String { format!("{}", prefab) }
}
<|endoftext|>


<commit_before>//! Runs rustfmt on the repository.

use crate::Build;
use build_helper::{output, t};
use ignore::WalkBuilder;
use std::path::Path;
use std::process::{Command, Stdio};

fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
    let mut cmd = Command::new(&rustfmt);
    // avoid the submodule config paths from coming into play,
    // we only allow a single global config for the workspace for now
    cmd.arg("--config-path").arg(&src.canonicalize().unwrap());
    cmd.arg("--edition").arg("2018");
    cmd.arg("--unstable-features");
    cmd.arg("--skip-children");
    if check {
        cmd.arg("--check");
    }
    cmd.arg(&path);
    let cmd_debug = format!("{:?}", cmd);
    let status = cmd.status().expect("executing rustfmt");
    if !status.success() {
        eprintln!(
            "Running `{}` failed.\nIf you're running `tidy`, \
            try again with `--bless` flag. Or, you just want to format \
            code, run `./x.py fmt` instead.",
            cmd_debug,
        );
        std::process::exit(1);
    }
}

#[derive(serde::Deserialize)]
struct RustfmtConfig {
    ignore: Vec<String>,
}

pub fn format(build: &Build, check: bool) {
    if build.config.dry_run {
        return;
    }
    let mut builder = ignore::types::TypesBuilder::new();
    builder.add_defaults();
    builder.select("rust");
    let matcher = builder.build().unwrap();
    let rustfmt_config = build.src.join("rustfmt.toml");
    if !rustfmt_config.exists() {
        eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
        eprintln!("This may happen in distributed tarballs.");
        return;
    }
    let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
    let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
    let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
    for ignore in rustfmt_config.ignore {
        ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
    }
    let git_available = match Command::new("git")
        .arg("--version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
    {
        Ok(status) => status.success(),
        Err(_) => false,
    };
    if git_available {
        let in_working_tree = match Command::new("git")
            .arg("rev-parse")
            .arg("--is-inside-work-tree")
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .status()
        {
            Ok(status) => status.success(),
            Err(_) => false,
        };
        if in_working_tree {
            let untracked_paths_output = output(
                Command::new("git")
                    .arg("status")
                    .arg("--porcelain")
                    .arg("--untracked-files=normal"),
            );
            let untracked_paths = untracked_paths_output
                .lines()
                .filter(|entry| entry.starts_with("??"))
                .map(|entry| {
                    entry.split(" ").nth(1).expect("every git status entry should list a path")
                });
            for untracked_path in untracked_paths {
                eprintln!("skip untracked path {} during rustfmt invocations", untracked_path);
                ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path);
            }
        } else {
            eprintln!("Not in git tree. Skipping git-aware format checks");
        }
    } else {
        eprintln!("Could not find usable git. Skipping git-aware format checks");
    }
    let ignore_fmt = ignore_fmt.build().unwrap();

    let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
        eprintln!("./x.py fmt is not supported on this channel");
        std::process::exit(1);
    });
    let src = build.src.clone();
    let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel();
    walker.run(|| {
        let src = src.clone();
        let rustfmt_path = rustfmt_path.clone();
        Box::new(move |entry| {
            let entry = t!(entry);
            if entry.file_type().map_or(false, |t| t.is_file()) {
                rustfmt(&src, &rustfmt_path, &entry.path(), check);
            }
            ignore::WalkState::Continue
        })
    });
}
<commit_msg>Rollup merge of #72291 - RalfJung:format-typo, r=jonas-schievink<commit_after>//! Runs rustfmt on the repository.

use crate::Build;
use build_helper::{output, t};
use ignore::WalkBuilder;
use std::path::Path;
use std::process::{Command, Stdio};

fn rustfmt(src: &Path, rustfmt: &Path, path: &Path, check: bool) {
    let mut cmd = Command::new(&rustfmt);
    // avoid the submodule config paths from coming into play,
    // we only allow a single global config for the workspace for now
    cmd.arg("--config-path").arg(&src.canonicalize().unwrap());
    cmd.arg("--edition").arg("2018");
    cmd.arg("--unstable-features");
    cmd.arg("--skip-children");
    if check {
        cmd.arg("--check");
    }
    cmd.arg(&path);
    let cmd_debug = format!("{:?}", cmd);
    let status = cmd.status().expect("executing rustfmt");
    if !status.success() {
        eprintln!(
            "Running `{}` failed.\nIf you're running `tidy`, \
            try again with `--bless`. Or, if you just want to format \
            code, run `./x.py fmt` instead.",
            cmd_debug,
        );
        std::process::exit(1);
    }
}

#[derive(serde::Deserialize)]
struct RustfmtConfig {
    ignore: Vec<String>,
}

pub fn format(build: &Build, check: bool) {
    if build.config.dry_run {
        return;
    }
    let mut builder = ignore::types::TypesBuilder::new();
    builder.add_defaults();
    builder.select("rust");
    let matcher = builder.build().unwrap();
    let rustfmt_config = build.src.join("rustfmt.toml");
    if !rustfmt_config.exists() {
        eprintln!("Not running formatting checks; rustfmt.toml does not exist.");
        eprintln!("This may happen in distributed tarballs.");
        return;
    }
    let rustfmt_config = t!(std::fs::read_to_string(&rustfmt_config));
    let rustfmt_config: RustfmtConfig = t!(toml::from_str(&rustfmt_config));
    let mut ignore_fmt = ignore::overrides::OverrideBuilder::new(&build.src);
    for ignore in rustfmt_config.ignore {
        ignore_fmt.add(&format!("!{}", ignore)).expect(&ignore);
    }
    let git_available = match Command::new("git")
        .arg("--version")
        .stdout(Stdio::null())
        .stderr(Stdio::null())
        .status()
    {
        Ok(status) => status.success(),
        Err(_) => false,
    };
    if git_available {
        let in_working_tree = match Command::new("git")
            .arg("rev-parse")
            .arg("--is-inside-work-tree")
            .stdout(Stdio::null())
            .stderr(Stdio::null())
            .status()
        {
            Ok(status) => status.success(),
            Err(_) => false,
        };
        if in_working_tree {
            let untracked_paths_output = output(
                Command::new("git")
                    .arg("status")
                    .arg("--porcelain")
                    .arg("--untracked-files=normal"),
            );
            let untracked_paths = untracked_paths_output
                .lines()
                .filter(|entry| entry.starts_with("??"))
                .map(|entry| {
                    entry.split(" ").nth(1).expect("every git status entry should list a path")
                });
            for untracked_path in untracked_paths {
                eprintln!("skip untracked path {} during rustfmt invocations", untracked_path);
                ignore_fmt.add(&format!("!{}", untracked_path)).expect(&untracked_path);
            }
        } else {
            eprintln!("Not in git tree. Skipping git-aware format checks");
        }
    } else {
        eprintln!("Could not find usable git. Skipping git-aware format checks");
    }
    let ignore_fmt = ignore_fmt.build().unwrap();

    let rustfmt_path = build.config.initial_rustfmt.as_ref().unwrap_or_else(|| {
        eprintln!("./x.py fmt is not supported on this channel");
        std::process::exit(1);
    });
    let src = build.src.clone();
    let walker = WalkBuilder::new(&build.src).types(matcher).overrides(ignore_fmt).build_parallel();
    walker.run(|| {
        let src = src.clone();
        let rustfmt_path = rustfmt_path.clone();
        Box::new(move |entry| {
            let entry = t!(entry);
            if entry.file_type().map_or(false, |t| t.is_file()) {
                rustfmt(&src, &rustfmt_path, &entry.path(), check);
            }
            ignore::WalkState::Continue
        })
    });
}
<|endoftext|>


<commit_before>extern crate pkg_config;

use std::io::ErrorKind;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let target = env::var("TARGET").unwrap();
    let mingw = target.contains("windows-gnu");
    cflags.push_str(" -ffunction-sections -fdata-sections");

    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let src = PathBuf::new(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::new(&env::var("OUT_DIR").unwrap());
    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("cmake");
    cmd.arg(&src.join("libgit2"))
       .current_dir(&dst.join("build"));
    if mingw {
        cmd.arg("-G").arg("Unix Makefiles");
    }
    let profile = match &env::var("PROFILE").unwrap()[..] {
        "bench" | "release" => "Release",
        _ => "Debug",
    };
    run(cmd.arg("-DTHREADSAFE=ON")
           .arg("-DBUILD_SHARED_LIBS=OFF")
           .arg("-DBUILD_CLAR=OFF")
           .arg(&format!("-DCMAKE_BUILD_TYPE={}", profile))
           .arg(&format!("-DCMAKE_INSTALL_PREFIX={}", dst.display()))
           .arg("-DBUILD_EXAMPLES=OFF")
           .arg(&format!("-DCMAKE_C_FLAGS={}", cflags)), "cmake");
    run(Command::new("cmake")
                .arg("--build").arg(".")
                .arg("--target").arg("install")
                .current_dir(&dst.join("build")), "cmake");

    println!("cargo:root={}", dst.display());
    if mingw || target.contains("windows") {
        println!("cargo:rustc-flags=-l winhttp -l rpcrt4 -l ole32 \
                                    -l ws2_32 -l bcrypt -l crypt32 \
                                    -l git2:static -L {}",
                 dst.join("lib").display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        append("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-flags=-l git2:static");
    println!("cargo:rustc-flags=-L {}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-flags=-l iconv");
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            append("CMAKE_PREFIX_PATH", PathBuf::new(&s));
            append("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn append(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let val = env::join_paths(env::split_paths(&prefix)
                                  .chain(Some(val).into_iter())).unwrap();
    env::set_var(var, &val);
}

fn fail(s: &str) -> ! {
    panic!("\n{}\n\nbuild script failed, must exit now", s)
}
<commit_msg>libgit2-sys: Updated to changed PathBuf API<commit_after>#![feature(convert)]

extern crate pkg_config;

use std::io::ErrorKind;
use std::env;
use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;

fn main() {
    register_dep("SSH2");
    register_dep("OPENSSL");

    let mut cflags = env::var("CFLAGS").unwrap_or(String::new());
    let target = env::var("TARGET").unwrap();
    let mingw = target.contains("windows-gnu");
    cflags.push_str(" -ffunction-sections -fdata-sections");

    if target.contains("i686") {
        cflags.push_str(" -m32");
    } else if target.contains("x86_64") {
        cflags.push_str(" -m64");
    }
    if !target.contains("i686") {
        cflags.push_str(" -fPIC");
    }

    let src = PathBuf::from(&env::var("CARGO_MANIFEST_DIR").unwrap());
    let dst = PathBuf::from(&env::var("OUT_DIR").unwrap());
    let _ = fs::create_dir(&dst.join("build"));

    let mut cmd = Command::new("cmake");
    cmd.arg(&src.join("libgit2"))
       .current_dir(&dst.join("build"));
    if mingw {
        cmd.arg("-G").arg("Unix Makefiles");
    }
    let profile = match &env::var("PROFILE").unwrap()[..] {
        "bench" | "release" => "Release",
        _ => "Debug",
    };
    run(cmd.arg("-DTHREADSAFE=ON")
           .arg("-DBUILD_SHARED_LIBS=OFF")
           .arg("-DBUILD_CLAR=OFF")
           .arg(&format!("-DCMAKE_BUILD_TYPE={}", profile))
           .arg(&format!("-DCMAKE_INSTALL_PREFIX={}", dst.display()))
           .arg("-DBUILD_EXAMPLES=OFF")
           .arg(&format!("-DCMAKE_C_FLAGS={}", cflags)), "cmake");
    run(Command::new("cmake")
                .arg("--build").arg(".")
                .arg("--target").arg("install")
                .current_dir(&dst.join("build")), "cmake");

    println!("cargo:root={}", dst.display());
    if mingw || target.contains("windows") {
        println!("cargo:rustc-flags=-l winhttp -l rpcrt4 -l ole32 \
                                    -l ws2_32 -l bcrypt -l crypt32 \
                                    -l git2:static -L {}",
                 dst.join("lib").display());
        return
    }

    if env::var("HOST") == env::var("TARGET") {
        append("PKG_CONFIG_PATH", dst.join("lib/pkgconfig"));
        if pkg_config::Config::new().statik(true).find("libgit2").is_ok() {
            return
        }
    }

    println!("cargo:rustc-flags=-l git2:static");
    println!("cargo:rustc-flags=-L {}", dst.join("lib").display());
    if target.contains("apple") {
        println!("cargo:rustc-flags=-l iconv");
    }
}

fn run(cmd: &mut Command, program: &str) {
    println!("running: {:?}", cmd);
    let status = match cmd.status() {
        Ok(status) => status,
        Err(ref e) if e.kind() == ErrorKind::NotFound => {
            fail(&format!("failed to execute command: {}\nis `{}` not installed?",
                          e, program));
        }
        Err(e) => fail(&format!("failed to execute command: {}", e)),
    };
    if !status.success() {
        fail(&format!("command did not execute successfully, got: {}", status));
    }
}

fn register_dep(dep: &str) {
    match env::var(&format!("DEP_{}_ROOT", dep)) {
        Ok(s) => {
            append("CMAKE_PREFIX_PATH", PathBuf::from(&s));
            append("PKG_CONFIG_PATH", Path::new(&s).join("lib/pkgconfig"));
        }
        Err(..) => {}
    }
}

fn append(var: &str, val: PathBuf) {
    let prefix = env::var(var).unwrap_or(String::new());
    let val = env::join_paths(env::split_paths(&prefix)
                                  .chain(Some(val).into_iter())).unwrap();
    env::set_var(var, &val);
}

fn fail(s: &str) -> ! {
    panic!("\n{}\n\nbuild script failed, must exit now", s)
}
<|endoftext|>


<commit_before>//! byt - os_unix
//!
//! Implement functions by targeting the terminal functionality
//! commonly offered by unix terminals.

// EXTERNS

// LIBRARY INCLUDES
use std::io::{self, Write};
use byt::libc::{
    self,
    ioctl,
    c_void,
    TIOCGWINSZ,
    winsize
};
use std::mem;

// SUBMODULES

// LOCAL INCLUDES
use byt::envs::*;

// Constants for manipulating the terminal.
const CMD_LEAD           : &str = "\x1B[";  // Leader for all commands.
const CMD_CLEAR          : &str = "2J";     // Clears the display.
const CMD_NORM           : &str = "?47l";   // Change to normal buffer
const CMD_ALT            : &str = "?47h";   // Change to alternate buffer
const CMD_SAVE_CURSOR    : &str = "6";      // Save the cursor's position.
const CMD_RESTORE_CURSOR : &str = "7";      // Restore the cursor's position.

pub struct Term {
    saved_config : libc::termios,
}

impl Term {
    // #################################
    // P R I V A T E  F U N C T I O N S
    // #################################
    /// Print a command to the terminal.
    fn cmd(&self, command : &str) {
        let mut handle = io::stdout();
        handle.write(CMD_LEAD.as_bytes())
              .expect("Writing command leader to stdout failed");
        handle.write(command.as_bytes())
              .expect("Writing command to stdout failed");
        handle.flush()
              .expect("Flushing stdout failed");
    }

    /// Set the terminal to the alternate full screen buffer.
    fn set_alternate(&self) {
        self.cmd(CMD_ALT);
    }

    /// Set the terminal back to the normal buffer.
    fn set_normal(&self) {
        self.cmd(CMD_NORM);
    }

    /// Save the cursor position.
    fn save_cursor(&self) {
        self.cmd(CMD_SAVE_CURSOR);
    }

    /// Restore the cursor position.
    fn restore_cursor(&self) {
        self.cmd(CMD_RESTORE_CURSOR);
    }

    /// Get the current termios config.
    fn get_mode() -> libc::termios {
        unsafe {
            let mut temp : libc::termios = mem::zeroed();
            libc::tcgetattr(0, &mut temp);
            temp
        }
    }

    // ###############################
    // P U B L I C  F U N C T I O N S
    // ###############################
    pub fn new() -> Term {
        Term {
            saved_config : Term::get_mode(),
        }
    }

    /// Open the application.
    pub fn start(&self) {
        self.save_cursor();
        self.set_alternate();
    }

    /// Close the application.
    pub fn stop(&self) {
        self.set_normal();
        self.restore_cursor();
    }

    /// Get the size of the terminal in rows and columns.
    pub fn get_size(&self) -> (u16, u16) {
        let (rows, cols);

        // Get the size of the terminal by calling ioctl.
        unsafe {
            let mut ws = winsize {
                ws_row: 0,
                ws_col: 0,
                ws_xpixel: 0,
                ws_ypixel: 0,
            };

            // Grab a pointer to the struct.
            let ptr : *mut c_void = &mut ws as *mut _ as *mut c_void;

            // Run ioctl to check the window size.
            // TODO: If this fails, try TIOCGSIZE.
            let result = ioctl(1, TIOCGWINSZ, ptr);

            match result {
                0 => {rows = ws.ws_row; cols = ws.ws_col},
                _ => panic!("Grabbing terminal size from ioctl failed")
            }
        }

        (rows, cols)
    }

    /// Move the terminal's cursor to a particular location.
    ///
    /// Takes input in (row, col).
    pub fn move_cursor(&self, row : u16, col : u16) {
        self.cmd(format!("{};{}H", row, col).as_str());
    }

    /// Clear the terminal's screen.
    pub fn clear_screen(&self) {
        self.cmd(CMD_CLEAR);
    }

    /// Write a string to the terminal at the cursor.
    pub fn write(&self, out : &str) {
        let mut handle = io::stdout();
        handle.write(out.as_bytes())
              .expect("Failed to write to stdout");
        handle.flush()
              .expect("Flushing to stdout failed");
    }
    /// Set the terminal's mode between `Raw` and `Cooked`
    /// modes.
    pub fn set_mode(&self, mode : TermMode) {
        let mut copy = self.saved_config;
        match mode {
            TermMode::Cooked => {
                // Don't do anything, just set it back to normal
            },
            TermMode::Raw => {
                // Most of this is borrowed from:
                // http://www.cs.uleth.ca/~holzmann/C/system/ttyraw.c
                copy.c_iflag &=
                    !(libc::BRKINT | // Turn off line break
                      libc::ICRNL  | // Don't check for parity
                      libc::INPCK  | // Don't strip characters
                      libc::ISTRIP |
                      libc::IXON);

                // give full 8 bits
                copy.c_cflag |= libc::CS8;

                // Also fix local modes
                copy.c_lflag &= !(libc::ECHO |
                                  libc::ICANON |
                                  libc::IEXTEN |
                                  libc::ISIG);

                // We only want to interpret one byte at a time
                copy.c_cc[libc::VMIN] = 1;

                // Don't wait for anything
                copy.c_cc[libc::VTIME] = 0;
            }
        }

        unsafe {
            libc::tcsetattr(0, libc::TCSAFLUSH, &copy);
        }
    }
}
<commit_msg>fix(rendering): solution inspired by vim's<commit_after>//! byt - os_unix
//!
//! Implement functions by targeting the terminal functionality
//! commonly offered by unix terminals.

// EXTERNS

// LIBRARY INCLUDES
use std::io::{self, Write};
use byt::libc::{
    self,
    ioctl,
    c_void,
    TIOCGWINSZ,
    winsize
};
use std::mem;

// SUBMODULES

// LOCAL INCLUDES
use byt::envs::*;

// Constants for manipulating the terminal.
const CMD_LEAD           : &str = "\x1B";  // Leader for all commands.
const CMD_CLEAR          : &str = "[2J";     // Clears the display.
const CMD_TI             : &str = "7\x1B[?47h";   // Change to termcap mode.
const CMD_TE             : &str = "[2J\x1B[?47l\x1B8";   // Change to normal mode.

pub struct Term {
    saved_config : libc::termios,
}

impl Term {
    // #################################
    // P R I V A T E  F U N C T I O N S
    // #################################
    /// Print a command to the terminal.
    fn cmd(&self, command : &str) {
        let mut handle = io::stdout();
        handle.write(CMD_LEAD.as_bytes())
              .expect("Writing command leader to stdout failed");
        handle.write(command.as_bytes())
              .expect("Writing command to stdout failed");
        handle.flush()
              .expect("Flushing stdout failed");
    }

    /// Set the terminal to the alternate full screen buffer.
    fn set_alternate(&self) {
        self.cmd(CMD_TI);
    }

    /// Set the terminal back to the normal buffer.
    fn set_normal(&self) {
        self.cmd(CMD_TE);
    }

    /// Get the current termios config.
    fn get_mode() -> libc::termios {
        unsafe {
            let mut temp : libc::termios = mem::zeroed();
            libc::tcgetattr(0, &mut temp);
            temp
        }
    }

    // ###############################
    // P U B L I C  F U N C T I O N S
    // ###############################
    pub fn new() -> Term {
        Term {
            saved_config : Term::get_mode(),
        }
    }

    /// Open the application.
    pub fn start(&self) {
        self.set_alternate();
    }

    /// Close the application.
    pub fn stop(&self) {
        self.set_normal();
    }

    /// Get the size of the terminal in rows and columns.
    pub fn get_size(&self) -> (u16, u16) {
        let (rows, cols);

        // Get the size of the terminal by calling ioctl.
        unsafe {
            let mut ws = winsize {
                ws_row: 0,
                ws_col: 0,
                ws_xpixel: 0,
                ws_ypixel: 0,
            };

            // Grab a pointer to the struct.
            let ptr : *mut c_void = &mut ws as *mut _ as *mut c_void;

            // Run ioctl to check the window size.
            // TODO: If this fails, try TIOCGSIZE.
            let result = ioctl(1, TIOCGWINSZ, ptr);

            match result {
                0 => {rows = ws.ws_row; cols = ws.ws_col},
                _ => panic!("Grabbing terminal size from ioctl failed")
            }
        }

        (rows, cols)
    }

    /// Move the terminal's cursor to a particular location.
    ///
    /// Takes input in (row, col).
    pub fn move_cursor(&self, row : u16, col : u16) {
        self.cmd(format!("{};{}H", row, col).as_str());
    }

    /// Clear the terminal's screen.
    pub fn clear_screen(&self) {
        self.cmd(CMD_CLEAR);
    }

    /// Write a string to the terminal at the cursor.
    pub fn write(&self, out : &str) {
        let mut handle = io::stdout();
        handle.write(out.as_bytes())
              .expect("Failed to write to stdout");
        handle.flush()
              .expect("Flushing to stdout failed");
    }
    /// Set the terminal's mode between `Raw` and `Cooked`
    /// modes.
    pub fn set_mode(&self, mode : TermMode) {
        let mut copy = self.saved_config;
        match mode {
            TermMode::Cooked => {
                // Don't do anything, just set it back to normal
            },
            TermMode::Raw => {
                // Most of this is borrowed from:
                // http://www.cs.uleth.ca/~holzmann/C/system/ttyraw.c
                copy.c_iflag &=
                    !(libc::BRKINT | // Turn off line break
                      libc::ICRNL  | // Don't check for parity
                      libc::INPCK  | // Don't strip characters
                      libc::ISTRIP |
                      libc::IXON);

                // give full 8 bits
                copy.c_cflag |= libc::CS8;

                // Also fix local modes
                copy.c_lflag &= !(libc::ECHO |
                                  libc::ICANON |
                                  libc::IEXTEN |
                                  libc::ISIG);

                // We only want to interpret one byte at a time
                copy.c_cc[libc::VMIN] = 1;

                // Don't wait for anything
                copy.c_cc[libc::VTIME] = 0;
            }
        }

        unsafe {
            libc::tcsetattr(0, libc::TCSAFLUSH, &copy);
        }
    }
}
<|endoftext|>


<commit_before>//! Read las points.
//!
//! If you're reading any significant number of points, you'll want to make sure you're using a
//! `BufRead` instead of just a `Read`:
//!
//! ```
//! use std::fs::File;
//! use std::io::BufReader;
//! use las::Reader;
//!
//! let read = BufReader::new(File::open("tests/data/autzen.las").unwrap());
//! let reader = Reader::new(read).unwrap();
//! ```
//!
//! `Reader::from_path` does this for you:
//!
//! ```
//! use las::Reader;
//! let reader = Reader::from_path("tests/data/autzen.las").unwrap();
//! ```
//!
//! For now, compressed files are not supported:
//!
//! ```
//! use las::Reader;
//! assert!(Reader::from_path("tests/data/autzen.laz").is_err());
//! ```
//!
//! Use `Reader::read` to read one point, and `Reader::points` to get an iterator over
//! `Result<Point>`:
//!
//! ```
//! use las::Reader;
//! let mut reader = Reader::from_path("tests/data/autzen.las").unwrap();
//! let first_point = reader.read().unwrap().unwrap();
//! let the_rest = reader.points().map(|r| r.unwrap()).collect::<Vec<_>>();
//! ```

use {Builder, Header, Point, Result, Vlr, raw};
use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::path::Path;

quick_error! {
    /// Error while reading.
    #[derive(Clone, Copy, Debug)]
    pub enum Error {
        /// The offset to the point data was too small.
        OffsetToPointDataTooSmall(offset: u32) {
            description("offset to point data too small")
            display("offset to point data too small: {}", offset)
        }
        /// The offset to the start of the evlrs is too small.
        OffsetToEvlrsTooSmall(offset: u64) {
            description("offset the evlrs is too small")
            display("offset to the evlrs is too small: {}", offset)
        }
    }
}

/// Reads LAS data.
#[derive(Debug)]
pub struct Reader<R: Read + Seek> {
    header: Header,
    read: R,
    number_of_points: u64,
    number_read: u64,
}

impl<R: Read + Seek> Reader<R> {
    /// Creates a new reader.
    ///
    /// This does *not* wrap the `Read` in a `BufRead`, so if you're concered about performance you
    /// should do that wrapping yourself (or use `from_path`).
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::BufReader;
    /// use std::fs::File;
    /// # use las::Reader;
    /// let file = File::open("tests/data/autzen.las").unwrap();
    /// let reader = Reader::new(BufReader::new(file)).unwrap();
    /// ```
    pub fn new(mut read: R) -> Result<Reader<R>> {
        let raw_header = raw::Header::read_from(&mut read)?;
        if raw_header.point_format.is_compressed {
            return Err(::Error::Laszip);
        }
        let mut position = u64::from(raw_header.header_size);
        let number_of_variable_length_records = raw_header.number_of_variable_length_records;
        let offset_to_point_data = u64::from(raw_header.offset_to_point_data);
        let offset_to_end_of_points = raw_header.offset_to_end_of_points();
        let evlr = raw_header.evlr;

        let mut builder = Builder::new(raw_header)?;
        for _ in 0..number_of_variable_length_records {
            let vlr = raw::Vlr::read_from(&mut read, false).and_then(Vlr::new)?;
            position += vlr.len(false) as u64;
            builder.vlrs.push(vlr);
        }
        if position > offset_to_point_data {
            return Err(
                Error::OffsetToPointDataTooSmall(offset_to_point_data as u32).into(),
            );
        } else if position < offset_to_point_data {
            read.by_ref()
                .take(offset_to_point_data - position)
                .read_to_end(&mut builder.vlr_padding)?;
        }

        read.seek(SeekFrom::Start(offset_to_end_of_points))?;
        if let Some(evlr) = evlr {
            if evlr.start_of_first_evlr < offset_to_end_of_points {
                return Err(
                    Error::OffsetToEvlrsTooSmall(evlr.start_of_first_evlr).into(),
                );
            } else if evlr.start_of_first_evlr > offset_to_end_of_points {
                let n = evlr.start_of_first_evlr - offset_to_end_of_points;
                read.by_ref().take(n).read_to_end(
                    &mut builder.point_padding,
                )?;
            }
            builder.evlrs.push(
                raw::Vlr::read_from(&mut read, true).and_then(
                    Vlr::new,
                )?,
            );
        }

        read.seek(SeekFrom::Start(offset_to_point_data))?;
        let header = builder.into_header()?;

        Ok(Reader {
            number_of_points: header.number_of_points(),
            header: header,
            read: read,
            number_read: 0,
        })
    }

    /// Returns a reference to this reader's header.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Reader;
    /// let reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// let header = reader.header();
    /// ```
    pub fn header(&self) -> &Header {
        &self.header
    }

    /// Reads a point.
    ///
    /// Returns `Ok(None)` if we have already read the last point.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Reader;
    /// let mut reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// let point = reader.read().unwrap().unwrap();
    /// ```
    pub fn read(&mut self) -> Result<Option<Point>> {
        if self.number_read >= self.number_of_points {
            Ok(None)
        } else {
            let point = raw::Point::read_from(&mut self.read, self.header.point_format())
                .map(|raw_point| {
                    Some(Point::new(raw_point, self.header.transforms()))
                });
            self.number_read += 1;
            point
        }
    }

    /// Returns an iterator over this reader's points.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Reader;
    /// let mut reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// let points = reader.points().collect::<Result<Vec<_>, _>>().unwrap();
    /// ```
    pub fn points(&mut self) -> Points<R> {
        Points { reader: self }
    }
}

impl Reader<BufReader<File>> {
    /// Creates a new reader from a path.
    ///
    /// The underlying `File` is wrapped in a `BufReader` for performance reasons.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Reader;
    /// let reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Reader<BufReader<File>>> {
        File::open(path).map_err(::Error::from).and_then(|file| {
            Reader::new(BufReader::new(file))
        })
    }
}

/// An iterator over of the points in a `Reader`.
///
/// This struct is generally created by calling `points()` on `Reader`.
#[derive(Debug)]
pub struct Points<'a, R: 'a + Read + Seek> {
    reader: &'a mut Reader<R>,
}

impl<'a, R: Read + Seek> Iterator for Points<'a, R> {
    type Item = Result<Point>;
    fn next(&mut self) -> Option<Result<Point>> {
        match self.reader.read() {
            Ok(None) => None,
            Ok(Some(point)) => Some(Ok(point)),
            Err(err) => Some(Err(err)),
        }
    }
}
<commit_msg>Add seek method to reader<commit_after>//! Read las points.
//!
//! If you're reading any significant number of points, you'll want to make sure you're using a
//! `BufRead` instead of just a `Read`:
//!
//! ```
//! use std::fs::File;
//! use std::io::BufReader;
//! use las::Reader;
//!
//! let read = BufReader::new(File::open("tests/data/autzen.las").unwrap());
//! let reader = Reader::new(read).unwrap();
//! ```
//!
//! `Reader::from_path` does this for you:
//!
//! ```
//! use las::Reader;
//! let reader = Reader::from_path("tests/data/autzen.las").unwrap();
//! ```
//!
//! For now, compressed files are not supported:
//!
//! ```
//! use las::Reader;
//! assert!(Reader::from_path("tests/data/autzen.laz").is_err());
//! ```
//!
//! Use `Reader::read` to read one point, and `Reader::points` to get an iterator over
//! `Result<Point>`:
//!
//! ```
//! use las::Reader;
//! let mut reader = Reader::from_path("tests/data/autzen.las").unwrap();
//! let first_point = reader.read().unwrap().unwrap();
//! let the_rest = reader.points().map(|r| r.unwrap()).collect::<Vec<_>>();
//! ```

use {Builder, Header, Point, Result, Vlr, raw};
use std::fs::File;
use std::io::{BufReader, Read, Seek, SeekFrom};
use std::path::Path;

quick_error! {
    /// Error while reading.
    #[derive(Clone, Copy, Debug)]
    pub enum Error {
        /// The offset to the point data was too small.
        OffsetToPointDataTooSmall(offset: u32) {
            description("offset to point data too small")
            display("offset to point data too small: {}", offset)
        }
        /// The offset to the start of the evlrs is too small.
        OffsetToEvlrsTooSmall(offset: u64) {
            description("offset the evlrs is too small")
            display("offset to the evlrs is too small: {}", offset)
        }
    }
}

/// Reads LAS data.
#[derive(Debug)]
pub struct Reader<R: Read + Seek> {
    header: Header,
    number_of_points: u64,
    number_read: u64,
    offset_to_point_data: u64,
    read: R,
}

impl<R: Read + Seek> Reader<R> {
    /// Creates a new reader.
    ///
    /// This does *not* wrap the `Read` in a `BufRead`, so if you're concered about performance you
    /// should do that wrapping yourself (or use `from_path`).
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io::BufReader;
    /// use std::fs::File;
    /// # use las::Reader;
    /// let file = File::open("tests/data/autzen.las").unwrap();
    /// let reader = Reader::new(BufReader::new(file)).unwrap();
    /// ```
    pub fn new(mut read: R) -> Result<Reader<R>> {
        let raw_header = raw::Header::read_from(&mut read)?;
        if raw_header.point_format.is_compressed {
            return Err(::Error::Laszip);
        }
        let mut position = u64::from(raw_header.header_size);
        let number_of_variable_length_records = raw_header.number_of_variable_length_records;
        let offset_to_point_data = u64::from(raw_header.offset_to_point_data);
        let offset_to_end_of_points = raw_header.offset_to_end_of_points();
        let evlr = raw_header.evlr;

        let mut builder = Builder::new(raw_header)?;
        for _ in 0..number_of_variable_length_records {
            let vlr = raw::Vlr::read_from(&mut read, false).and_then(Vlr::new)?;
            position += vlr.len(false) as u64;
            builder.vlrs.push(vlr);
        }
        if position > offset_to_point_data {
            return Err(
                Error::OffsetToPointDataTooSmall(offset_to_point_data as u32).into(),
            );
        } else if position < offset_to_point_data {
            read.by_ref()
                .take(offset_to_point_data - position)
                .read_to_end(&mut builder.vlr_padding)?;
        }

        read.seek(SeekFrom::Start(offset_to_end_of_points))?;
        if let Some(evlr) = evlr {
            if evlr.start_of_first_evlr < offset_to_end_of_points {
                return Err(
                    Error::OffsetToEvlrsTooSmall(evlr.start_of_first_evlr).into(),
                );
            } else if evlr.start_of_first_evlr > offset_to_end_of_points {
                let n = evlr.start_of_first_evlr - offset_to_end_of_points;
                read.by_ref().take(n).read_to_end(
                    &mut builder.point_padding,
                )?;
            }
            builder.evlrs.push(
                raw::Vlr::read_from(&mut read, true).and_then(
                    Vlr::new,
                )?,
            );
        }

        read.seek(SeekFrom::Start(offset_to_point_data))?;
        let header = builder.into_header()?;

        Ok(Reader {
            number_of_points: header.number_of_points(),
            header: header,
            offset_to_point_data: offset_to_point_data,
            read: read,
            number_read: 0,
        })
    }

    /// Returns a reference to this reader's header.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Reader;
    /// let reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// let header = reader.header();
    /// ```
    pub fn header(&self) -> &Header {
        &self.header
    }

    /// Reads a point.
    ///
    /// Returns `Ok(None)` if we have already read the last point.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Reader;
    /// let mut reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// let point = reader.read().unwrap().unwrap();
    /// ```
    pub fn read(&mut self) -> Result<Option<Point>> {
        if self.number_read >= self.number_of_points {
            Ok(None)
        } else {
            let point = raw::Point::read_from(&mut self.read, self.header.point_format())
                .map(|raw_point| {
                    Some(Point::new(raw_point, self.header.transforms()))
                });
            self.number_read += 1;
            point
        }
    }

    /// Seeks to the given point number, zero-indexed.
    ///
    /// # Examples
    ///
    /// ```
    /// use las::Reader;
    /// let mut reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// reader.seek(1).unwrap(); // <- seeks to the second point
    /// let the_second_point = reader.read().unwrap().unwrap();
    /// ```
    pub fn seek(&mut self, position: u64) -> Result<()> {
        self.read.seek(SeekFrom::Start(
            self.offset_to_point_data +
                position *
                    u64::from(self.header.point_format().len()),
        ))?;
        Ok(())
    }

    /// Returns an iterator over this reader's points.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Reader;
    /// let mut reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// let points = reader.points().collect::<Result<Vec<_>, _>>().unwrap();
    /// ```
    pub fn points(&mut self) -> Points<R> {
        Points { reader: self }
    }
}

impl Reader<BufReader<File>> {
    /// Creates a new reader from a path.
    ///
    /// The underlying `File` is wrapped in a `BufReader` for performance reasons.
    ///
    /// # Examples
    ///
    /// ```
    /// # use las::Reader;
    /// let reader = Reader::from_path("tests/data/autzen.las").unwrap();
    /// ```
    pub fn from_path<P: AsRef<Path>>(path: P) -> Result<Reader<BufReader<File>>> {
        File::open(path).map_err(::Error::from).and_then(|file| {
            Reader::new(BufReader::new(file))
        })
    }
}

/// An iterator over of the points in a `Reader`.
///
/// This struct is generally created by calling `points()` on `Reader`.
#[derive(Debug)]
pub struct Points<'a, R: 'a + Read + Seek> {
    reader: &'a mut Reader<R>,
}

impl<'a, R: Read + Seek> Iterator for Points<'a, R> {
    type Item = Result<Point>;
    fn next(&mut self) -> Option<Result<Point>> {
        match self.reader.read() {
            Ok(None) => None,
            Ok(Some(point)) => Some(Ok(point)),
            Err(err) => Some(Err(err)),
        }
    }
}

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

    #[test]
    fn seek() {
        let mut writer = Writer::default();
        writer.write(Default::default()).unwrap();
        let point = Point {
            x: 1.,
            y: 2.,
            z: 3.,
            ..Default::default()
        };
        writer.write(point.clone()).unwrap();
        let mut reader = Reader::new(writer.into_inner().unwrap()).unwrap();
        reader.seek(1).unwrap();
        assert_eq!(point, reader.read().unwrap().unwrap());
    }
}
<|endoftext|>