<commit_before>use std::{
    collections::HashMap,
    env,
    path::{Path, PathBuf},
    str::FromStr,
};

use sqlx::{
    any::{AnyConnectOptions, AnyPool, AnyPoolOptions, AnyRow},
    Column, ConnectOptions, Row, TypeInfo,
};

use kernel::{
    common::{
        async_trait::async_trait,
        eyre::{bail, Result},
        serde::Serialize,
        tracing::{self, log::LevelFilter},
    },
    stencila_schema::{
        ArrayValidator, BooleanValidator, CodeError, Datatable, DatatableColumn, IntegerValidator,
        Node, Null, Number, NumberValidator, StringValidator, ValidatorTypes,
    },
    Kernel, KernelSelector, KernelStatus, KernelTrait, KernelType, Task, TaskResult,
};

/// A kernel that executes SQL
#[derive(Debug, Default, Serialize)]
#[serde(crate = "kernel::common::serde")]
pub struct SqlKernel {
    /// The kernel configuration containing the database URL
    config: Option<String>,

    /// The directory that the kernel is started in
    ///
    /// Used to be able to resolve the path to SQLite files with relative paths
    directory: Option<PathBuf>,

    /// The kernel's database connection pool
    #[serde(skip)]
    connection_pool: Option<AnyPool>,

    /// Any select queries that have been assigned to variables
    #[serde(skip)]
    assigned_selects: HashMap<String, Datatable>,
}

impl SqlKernel {
    /// Create a new `SqlKernel`
    pub fn new(selector: &KernelSelector) -> Self {
        Self {
            config: selector.config.clone(),
            ..Default::default()
        }
    }

    /// Connect to the database
    ///
    /// This is called just-in-time (called by `exec`, `get` etc) as well as in `start`
    /// so that any errors during connection can be surfaced to the user as code chunk errors.
    async fn connect(&mut self) -> Result<()> {
        if let Some(_pool) = &self.connection_pool {
            return Ok(());
        }

        // Resolve the database URL, falling back to env var and then to in-memory SQLite
        let mut url = self
            .config
            .clone()
            .or_else(|| env::var("DATABASE_URL").ok())
            .unwrap_or_else(|| "sqlite://:memory:".to_string());

        // If the URL is for a SQLite and the file path is relative then make it
        // absolute using the directory that the kernel was started in.
        if let (Some(spec), Some(directory)) = (url.strip_prefix("sqlite://"), &self.directory) {
            let path = PathBuf::from(spec);
            if spec != ":memory:" && path.is_relative() {
                url = ["sqlite://", &directory.join(path).to_string_lossy()].concat();
            }
        }

        tracing::trace!("Connecting to database: {}", url);

        let mut options = AnyConnectOptions::from_str(&url)?;
        if let Some(options) = options.as_mssql_mut() {
            options.log_statements(LevelFilter::Trace);
        }
        if let Some(options) = options.as_postgres_mut() {
            options.log_statements(LevelFilter::Trace);
        }
        if let Some(options) = options.as_sqlite_mut() {
            options.log_statements(LevelFilter::Trace);
        }

        let pool = AnyPoolOptions::new()
            .max_connections(10)
            .connect_with(options)
            .await?;
        self.connection_pool = Some(pool);

        Ok(())
    }
}

#[async_trait]
impl KernelTrait for SqlKernel {
    async fn spec(&self) -> Kernel {
        Kernel::new("sql", KernelType::Builtin, &["sql"], true, false, false)
    }

    async fn status(&self) -> Result<KernelStatus> {
        Ok(KernelStatus::Idle)
    }

    async fn start(&mut self, directory: &Path) -> Result<()> {
        self.directory = Some(directory.to_owned());

        // Log error but do not return error so that another attempt is made to
        // re-connect is made in `exec` etc
        if let Err(error) = self.connect().await {
            tracing::error!("While attempting to connect to database: {}", error)
        }

        Ok(())
    }

    async fn get(&mut self, name: &str) -> Result<Node> {
        let _pool = self.connect().await?;
        let pool = self.connection_pool.as_ref().unwrap();
        let sql = format!("SELECT * FROM \"{}\"", name.replace('"', "-"));
        match sqlx::query(&sql).fetch_all(pool).await {
            Ok(rows) => Ok(any_rows_to_datatable(rows)),
            Err(error) => {
                bail!("No such symbol: {}", error)
            }
        }
    }

    async fn set(&mut self, _name: &str, _value: Node) -> Result<()> {
        todo!("Create a new table with name")
    }

    async fn exec_sync(&mut self, code: &str) -> Result<Task> {
        let mut task = Task::begin_sync();
        let mut outputs = Vec::new();
        let mut messages = Vec::new();

        match self.connect().await {
            Ok(..) => {
                let pool = self.connection_pool.as_ref().unwrap();
                match sqlx::query(code).fetch_all(pool).await {
                    Ok(rows) => {
                        let datatable = any_rows_to_datatable(rows);
                        outputs.push(datatable);
                    }
                    Err(error) => {
                        let message = error.to_string();
                        let message = message
                            .strip_prefix("error returned from database:")
                            .unwrap_or(&message);
                        let message = message.trim().to_string();

                        messages.push(CodeError {
                            error_message: message,
                            ..Default::default()
                        });
                    }
                }
            }
            Err(error) => {
                messages.push(CodeError {
                    error_message: error.to_string(),
                    ..Default::default()
                });
            }
        }

        task.end(TaskResult::new(outputs, messages));

        Ok(task)
    }
}

/// Transform a vector of `sqlx` [`AnyRow`]s to a Stencila [`Datatable`]
fn any_rows_to_datatable(rows: Vec<AnyRow>) -> Node {
    let rows_len = rows.len();
    let columns = if let Some(row) = rows.first() {
        row.columns()
            .iter()
            .map(|column| {
                let name = column.name().to_string();
                let type_name = column.type_info().name();
                let validator = match type_name {
                    "BOOLEAN" => {
                        Some(ValidatorTypes::BooleanValidator(BooleanValidator::default()))
                    }
                    "INTEGER" => {
                        Some(ValidatorTypes::IntegerValidator(IntegerValidator::default()))
                    }
                    "REAL" => Some(ValidatorTypes::NumberValidator(NumberValidator::default())),
                    "TEXT" => Some(ValidatorTypes::StringValidator(StringValidator::default())),
                    "BLOB" | "NULL" => None,
                    _ => {
                        tracing::debug!("Unhandled column type: {}", type_name);
                        None
                    }
                };
                (name, validator)
            })
            .collect()
    } else {
        Vec::new()
    };

    // Pre-allocate an vector of the size needed to hold all values and insert them in
    // column-first order
    let mut values: Vec<Node> = vec![Node::Null(Null {}); columns.len() * rows.len()];
    for (row_index, row) in rows.into_iter().enumerate() {
        for (col_index, (_name, validator)) in columns.iter().enumerate() {
            let position = col_index * rows_len + row_index;
            let value = match validator {
                Some(ValidatorTypes::BooleanValidator(..)) => {
                    row.try_get::<bool, usize>(col_index).map(Node::Boolean)
                }
                Some(ValidatorTypes::IntegerValidator(..)) => {
                    row.try_get::<i64, usize>(col_index).map(Node::Integer)
                }
                Some(ValidatorTypes::NumberValidator(..)) => row
                    .try_get::<f64, usize>(col_index)
                    .map(|num| Node::Number(Number(num))),
                Some(ValidatorTypes::StringValidator(..)) => {
                    row.try_get::<String, usize>(col_index).map(Node::String)
                }
                _ => row.try_get::<String, usize>(col_index).map(Node::String),
            };
            if let Ok(value) = value {
                values[position] = value;
            }
        }
    }

    let columns = columns
        .into_iter()
        .map(|(name, validator)| DatatableColumn {
            name,
            validator: validator.map(|validator| {
                Box::new(ArrayValidator {
                    items_validator: Some(Box::new(validator)),
                    ..Default::default()
                })
            }),
            values: values.drain(..rows_len).collect(),
            ..Default::default()
        })
        .collect();

    Node::Datatable(Datatable {
        columns,
        ..Default::default()
    })
}

#[cfg(test)]
mod tests {
    use super::*;
    use kernel::{common::tokio, stencila_schema::Number, KernelTrait};
    use test_utils::{assert_json_eq, common::serde_json::json};

    #[tokio::test]
    async fn get_set_exec() -> Result<()> {
        let mut kernel = SqlKernel::new(&KernelSelector::default());

        match kernel.get("a").await {
            Ok(..) => bail!("Expected an error"),
            Err(error) => assert!(error.to_string().contains("does not exist")),
        };

        match kernel.set("a", Node::String("A".to_string())).await {
            Ok(..) => bail!("Expected an error"),
            Err(error) => assert!(error
                .to_string()
                .contains("Unable to convert string `A` to a number")),
        };

        kernel.set("a", Node::Number(Number(1.23))).await?;

        let a = kernel.get("a").await?;
        assert!(matches!(a, Node::Number(..)));
        assert_json_eq!(a, json!(1.23));

        let (outputs, errors) = kernel.exec("a * 2").await?;
        assert_json_eq!(outputs, json!([2.46]));
        assert_eq!(errors.len(), 0);

        let (outputs, errors) = kernel.exec("x * 2").await?;
        assert_eq!(outputs.len(), 0);
        assert_json_eq!(
            errors,
            json!([{"type": "CodeError", "errorMessage": "Undefined variable or function: x"}])
        );

        Ok(())
    }
}
<commit_msg>feat(SQL kernel): Implement `set` for `Datatable`s<commit_after>use std::{
    collections::HashMap,
    env,
    path::{Path, PathBuf},
    str::FromStr,
};

use sqlx::{
    any::{AnyConnectOptions, AnyKind, AnyPool, AnyPoolOptions, AnyRow},
    Column, ConnectOptions, Row, TypeInfo,
};

use kernel::{
    common::{
        async_trait::async_trait,
        eyre::{bail, Result},
        itertools::Itertools,
        serde::Serialize,
        serde_json,
        tracing::{self, log::LevelFilter},
    },
    stencila_schema::{
        ArrayValidator, BooleanValidator, CodeError, Datatable, DatatableColumn, IntegerValidator,
        Node, Null, Number, NumberValidator, StringValidator, ValidatorTypes,
    },
    Kernel, KernelSelector, KernelStatus, KernelTrait, KernelType, Task, TaskResult,
};

/// A kernel that executes SQL
#[derive(Debug, Default, Serialize)]
#[serde(crate = "kernel::common::serde")]
pub struct SqlKernel {
    /// The kernel configuration containing the database URL
    config: Option<String>,

    /// The directory that the kernel is started in
    ///
    /// Used to be able to resolve the path to SQLite files with relative paths
    directory: Option<PathBuf>,

    /// The kernel's database connection pool
    #[serde(skip)]
    pool: Option<AnyPool>,

    /// Any select queries that have been assigned to variables
    #[serde(skip)]
    assigned_selects: HashMap<String, Datatable>,
}

impl SqlKernel {
    /// Create a new `SqlKernel`
    pub fn new(selector: &KernelSelector) -> Self {
        Self {
            config: selector.config.clone(),
            ..Default::default()
        }
    }

    /// Connect to the database (if not already connected)
    ///
    /// This is called just-in-time (called by `exec`, `get` etc) as well as in `start`
    /// so that any errors during connection can be surfaced to the user as code chunk errors.
    async fn connect(&mut self) -> Result<()> {
        if self.pool.is_some() {
            return Ok(());
        }

        // Resolve the database URL, falling back to env var and then to in-memory SQLite
        let mut url = self
            .config
            .clone()
            .or_else(|| env::var("DATABASE_URL").ok())
            .unwrap_or_else(|| "sqlite://:memory:".to_string());

        // If the URL is for a SQLite and the file path is relative then make it
        // absolute using the directory that the kernel was started in.
        if let (Some(spec), Some(directory)) = (url.strip_prefix("sqlite://"), &self.directory) {
            let path = PathBuf::from(spec);
            if spec != ":memory:" && path.is_relative() {
                url = ["sqlite://", &directory.join(path).to_string_lossy()].concat();
            }
        }

        tracing::trace!("Connecting to database: {}", url);

        let mut options = AnyConnectOptions::from_str(&url)?;
        if let Some(options) = options.as_mssql_mut() {
            options.log_statements(LevelFilter::Trace);
        }
        if let Some(options) = options.as_postgres_mut() {
            options.log_statements(LevelFilter::Trace);
        }
        if let Some(options) = options.as_sqlite_mut() {
            options.log_statements(LevelFilter::Trace);
        }

        let pool = AnyPoolOptions::new()
            .max_connections(10)
            .connect_with(options)
            .await?;
        self.pool = Some(pool);

        Ok(())
    }
}

#[async_trait]
impl KernelTrait for SqlKernel {
    async fn spec(&self) -> Kernel {
        Kernel::new("sql", KernelType::Builtin, &["sql"], true, false, false)
    }

    async fn status(&self) -> Result<KernelStatus> {
        Ok(KernelStatus::Idle)
    }

    async fn start(&mut self, directory: &Path) -> Result<()> {
        self.directory = Some(directory.to_owned());

        // Log error but do not return error so that another attempt is made to
        // re-connect is made in `exec` etc
        if let Err(error) = self.connect().await {
            tracing::error!("While attempting to connect to database: {}", error)
        }

        Ok(())
    }

    async fn get(&mut self, name: &str) -> Result<Node> {
        self.connect().await?;
        let pool = self
            .pool
            .as_ref()
            .expect("connect() should ensure connection");

        let sql = format!("SELECT * FROM \"{}\"", name.replace('"', "-"));
        match sqlx::query(&sql).fetch_all(pool).await {
            Ok(rows) => Ok(any_rows_to_datatable(rows)),
            Err(error) => {
                bail!("While getting symbol `{}` from SQL kernel: {}", name, error)
            }
        }
    }

    async fn set(&mut self, name: &str, value: Node) -> Result<()> {
        self.connect().await?;
        let pool = self
            .pool
            .as_ref()
            .expect("connect() should ensure connection");

        let datatable = match value {
            Node::Datatable(node) => node,
            _ => {
                bail!(
                    "Only Datatables can be set as symbols in a SQL database; got a `{}`",
                    value.as_ref()
                )
            }
        };

        let result = match pool.any_kind() {
            AnyKind::Postgres => datatable_to_postgres(name, datatable, pool).await,
            _ => datatable_to_other(name, datatable, pool).await,
        };

        match result {
            Ok(..) => Ok(()),
            Err(error) => {
                bail!("While setting symbol `{}` in SQL kernel: {}", name, error)
            }
        }
    }

    async fn exec_sync(&mut self, code: &str) -> Result<Task> {
        let mut task = Task::begin_sync();
        let mut outputs = Vec::new();
        let mut messages = Vec::new();

        match self.connect().await {
            Ok(..) => {
                let pool = self
                    .pool
                    .as_ref()
                    .expect("connect() should ensure connection");
                match sqlx::query(code).fetch_all(pool).await {
                    Ok(rows) => {
                        let datatable = any_rows_to_datatable(rows);
                        outputs.push(datatable);
                    }
                    Err(error) => {
                        let message = error.to_string();
                        let message = message
                            .strip_prefix("error returned from database:")
                            .unwrap_or(&message);
                        let message = message.trim().to_string();

                        messages.push(CodeError {
                            error_message: message,
                            ..Default::default()
                        });
                    }
                }
            }
            Err(error) => {
                messages.push(CodeError {
                    error_message: error.to_string(),
                    ..Default::default()
                });
            }
        }

        task.end(TaskResult::new(outputs, messages));

        Ok(task)
    }
}

/// Transform a vector of `sqlx` [`AnyRow`]s to a Stencila [`Datatable`]
fn any_rows_to_datatable(rows: Vec<AnyRow>) -> Node {
    let rows_len = rows.len();
    let columns = if let Some(row) = rows.first() {
        row.columns()
            .iter()
            .map(|column| {
                let name = column.name().to_string();
                let type_name = column.type_info().name();
                let validator = match type_name {
                    "BOOLEAN" => {
                        Some(ValidatorTypes::BooleanValidator(BooleanValidator::default()))
                    }
                    "INTEGER" => {
                        Some(ValidatorTypes::IntegerValidator(IntegerValidator::default()))
                    }
                    "REAL" => Some(ValidatorTypes::NumberValidator(NumberValidator::default())),
                    "TEXT" => Some(ValidatorTypes::StringValidator(StringValidator::default())),
                    "BLOB" | "NULL" => None,
                    _ => {
                        tracing::debug!("Unhandled column type: {}", type_name);
                        None
                    }
                };
                (name, validator)
            })
            .collect()
    } else {
        Vec::new()
    };

    // Pre-allocate an vector of the size needed to hold all values and insert them in
    // column-first order
    let mut values: Vec<Node> = vec![Node::Null(Null {}); columns.len() * rows.len()];
    for (row_index, row) in rows.into_iter().enumerate() {
        for (col_index, (_name, validator)) in columns.iter().enumerate() {
            let position = col_index * rows_len + row_index;
            let value = match validator {
                Some(ValidatorTypes::BooleanValidator(..)) => row
                    .try_get::<bool, usize>(col_index)
                    .map(Node::Boolean)
                    .ok(),
                Some(ValidatorTypes::IntegerValidator(..)) => {
                    row.try_get::<i64, usize>(col_index).map(Node::Integer).ok()
                }
                Some(ValidatorTypes::NumberValidator(..)) => row
                    .try_get::<f64, usize>(col_index)
                    .map(|num| Node::Number(Number(num)))
                    .ok(),
                Some(ValidatorTypes::StringValidator(..)) => row
                    .try_get::<String, usize>(col_index)
                    .map(Node::String)
                    .ok(),
                _ => row
                    .try_get::<String, usize>(col_index)
                    .ok()
                    .and_then(|json| serde_json::from_str(&json).ok()),
            };
            if let Some(value) = value {
                values[position] = value;
            }
        }
    }

    let columns = columns
        .into_iter()
        .map(|(name, validator)| DatatableColumn {
            name,
            validator: validator.map(|validator| {
                Box::new(ArrayValidator {
                    items_validator: Some(Box::new(validator)),
                    ..Default::default()
                })
            }),
            values: values.drain(..rows_len).collect(),
            ..Default::default()
        })
        .collect();

    Node::Datatable(Datatable {
        columns,
        ..Default::default()
    })
}

/// Transform a Stencila [`Datatable`] to a Postgres table
///
/// This function follows the recommendation [here](https://github.com/launchbadge/sqlx/blob/main/FAQ.md#how-can-i-bind-an-array-to-a-values-clause-how-can-i-do-bulk-inserts)
/// for doing bulk inserts to Postgres
async fn datatable_to_postgres(name: &str, datatable: Datatable, pool: &AnyPool) -> Result<()> {
    todo!();

    let mut sql = datatable_to_create_table(name, &datatable);
    Ok(())
}

/// Transform a Stencila [`Datatable`] to SQL table
async fn datatable_to_other(name: &str, datatable: Datatable, pool: &AnyPool) -> Result<()> {
    let cols = datatable.columns.len();
    let rows = datatable
        .columns
        .first()
        .map(|column| column.values.len())
        .unwrap_or(0);

    let mut sql = datatable_to_create_table(name, &datatable);

    if rows > 0 {
        sql += &format!("INSERT INTO \"{}\" VALUES\n", name);
        sql += &vec![format!(" ({})", vec!["?"; cols].join(", ")); rows].join(",\n");
        sql += ";";
    }

    let mut query = sqlx::query(&sql);
    for row in 0..rows {
        for col in 0..cols {
            let column = &datatable.columns[col];
            let node = &column.values[row];
            match node {
                Node::Null(..) => query = query.bind("null"),
                Node::Boolean(value) => query = query.bind(value),
                Node::Integer(value) => query = query.bind(value),
                Node::Number(value) => query = query.bind(value.0),
                Node::String(value) => query = query.bind(value),
                _ => query = query.bind(serde_json::to_string(node).unwrap_or_default()),
            }
        }
    }
    query.execute(pool).await?;

    Ok(())
}

/// Generate a `CREATE TABLE` query
fn datatable_to_create_table(name: &str, datatable: &Datatable) -> String {
    let columns = datatable
        .columns
        .iter()
        .map(|column| {
            let validator = column
                .validator
                .as_deref()
                .and_then(|array_validator| array_validator.items_validator.clone());
            let datatype = match validator.as_deref() {
                Some(ValidatorTypes::BooleanValidator(..)) => "BOOLEAN",
                Some(ValidatorTypes::IntegerValidator(..)) => "INTEGER",
                Some(ValidatorTypes::NumberValidator(..)) => "REAL",
                Some(ValidatorTypes::StringValidator(..)) => "TEXT",
                _ => "JSON",
            };
            format!("{} {}", column.name, datatype)
        })
        .collect_vec()
        .join(", ");
    format!(
        "DROP TABLE IF EXISTS \"{}\";\nCREATE TABLE \"{}\"({});\n",
        name, name, columns
    )
}

#[cfg(test)]
mod tests {
    use super::*;
    use kernel::{common::tokio, KernelTrait, stencila_schema::Primitive};
    use test_utils::assert_json_eq;

    #[tokio::test]
    async fn get_set_exec() -> Result<()> {
        let mut kernel = SqlKernel::new(&KernelSelector::default());

        if let Ok(..) = kernel.get("table_a").await {
            bail!("Expected an error because table not yet created")
        };

        match kernel.set("table_a", Node::String("A".to_string())).await {
            Ok(..) => bail!("Expected an error"),
            Err(error) => assert!(error
                .to_string()
                .contains("Only Datatables can be set as symbols")),
        };

        let rows = 5;
        let col_1 = DatatableColumn {
            name: "col_1".to_string(),
            validator: Some(Box::new(ArrayValidator {
                items_validator: Some(Box::new(ValidatorTypes::BooleanValidator(
                    BooleanValidator::default(),
                ))),
                ..Default::default()
            })),
            values: vec![Node::Boolean(true); rows],
            ..Default::default()
        };
        let col_2 = DatatableColumn {
            name: "col_2".to_string(),
            validator: Some(Box::new(ArrayValidator {
                items_validator: Some(Box::new(ValidatorTypes::IntegerValidator(
                    IntegerValidator::default(),
                ))),
                ..Default::default()
            })),
            values: (0..rows)
                .map(|index| Node::Integer(index as i64))
                .collect_vec(),
            ..Default::default()
        };
        let col_3 = DatatableColumn {
            name: "col_3".to_string(),
            validator: Some(Box::new(ArrayValidator {
                items_validator: Some(Box::new(ValidatorTypes::NumberValidator(
                    NumberValidator::default(),
                ))),
                ..Default::default()
            })),
            values: (0..rows)
                .map(|index| Node::Number(Number(index as f64)))
                .collect_vec(),
            ..Default::default()
        };
        let col_4 = DatatableColumn {
            name: "col_4".to_string(),
            validator: Some(Box::new(ArrayValidator {
                items_validator: Some(Box::new(ValidatorTypes::StringValidator(
                    StringValidator::default(),
                ))),
                ..Default::default()
            })),
            values: (0..rows)
                .map(|index| Node::String(format!("string-{}", index)))
                .collect_vec(),
            ..Default::default()
        };
        let col_5 = DatatableColumn {
            name: "col_5".to_string(),
            validator: None,
            values: (0..rows)
                .map(|index| Node::Array(vec![Primitive::Integer(index as i64)]))
                .collect_vec(),
            ..Default::default()
        };
        let datatable_a = Datatable {
            columns: vec![col_1, col_2, col_3, col_4, col_5],
            ..Default::default()
        };
        kernel
            .set("table_a", Node::Datatable(datatable_a.clone()))
            .await?;

        let table_a = kernel.get("table_a").await?;
        assert!(matches!(table_a, Node::Datatable(..)));
        assert_json_eq!(table_a, datatable_a);

        Ok(())
    }
}
<|endoftext|>


<commit_before>//! List owned projects
//!
//! https://docs.gitlab.com/ce/api/projects.html#list-owned-projects
//!
//! # List owned projects
//!
//! Get a list of projects which are owned by the authenticated user.
//!
//! ```text
//! GET /projects/owned
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `archived` | boolean | no | Limit by archived status |
//! | `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
//! | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
//! | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
//! | `search` | string | no | Return list of authorized projects matching the search criteria |

use BuildQuery;

use serde_json;

use gitlab::GitLab;
use Projects;
use projects::OwnedProjectListerInternal;

impl GitLab {
    pub fn projects_owned(&self, listing: Listing) -> Result<Projects, serde_json::Error> {
        let query = listing.build_query();
        self.get(&query)
    }
}

#[derive(Debug, Copy, Clone)]
pub enum ListingOrderBy {
    Id,
    Name,
    Path,
    CreatedAt,
    UpdatedAt,
    LastActivityAt,
}

#[derive(Debug, Clone)]
pub struct ProjectsLister<'a> {
    gl: &'a ::GitLab,
    internal: OwnedProjectListerInternal,
}

#[derive(Default, Debug, Clone)]
pub struct Listing {
    /// Limit by archived status
    archived: Option<bool>,
    /// Limit by visibility.
    visibility: Option<::ListingVisibility>,
    /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`.
    order_by: Option<ListingOrderBy>,
    /// Return requests sorted. Default is `::ListingSort::Desc`.
    sort: Option<::ListingSort>,
    /// Return list of authorized projects matching the search criteria.
    search: String,
}

#[allow(dead_code)]
impl Listing {
    pub fn new() -> Listing {
        Default::default()
    }
    pub fn archived(&mut self, archived: bool) -> &mut Listing {
        self.archived = Some(archived);
        self
    }
    pub fn visibility(&mut self, visibility: ::ListingVisibility) -> &mut Listing {
        self.visibility = Some(visibility);
        self
    }
    pub fn order_by(&mut self, order_by: ListingOrderBy) -> &mut Listing {
        self.order_by = Some(order_by);
        self
    }
    fn sort(&mut self, sort: ::ListingSort) -> &mut Listing {
        self.sort = Some(sort);
        self
    }
    pub fn search(&mut self, search: String) -> &mut Listing {
        self.search = search;
        self
    }
}

impl BuildQuery for Listing {
    fn build_query(&self) -> String {

        let mut query = String::from("projects/owned");

        let amp_char = "&";
        let none_char = "";
        let mut split_char = &none_char;

        // Append a "?" only if at least one of the `Option` is `Some(_)` or if
        // strings contain something.
        query.push_str(match (&self.archived,
                              &self.visibility,
                              &self.order_by,
                              &self.sort,
                              self.search.is_empty()) {
            (&None, &None, &None, &None, true) => "",
            _ => "?",
        });

        self.archived.map(|archived| {
            query.push_str(split_char);
            split_char = &amp_char;

            if archived {
                query.push_str("archived=true")
            } else {
                query.push_str("archived=false")
            }
        });

        self.visibility.map(|visibility| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("visibility=");
            query.push_str(match visibility {
                ::ListingVisibility::Public => "public",
                ::ListingVisibility::Internal => "internal",
                ::ListingVisibility::Private => "private",
            });
        });

        self.order_by.map(|order_by| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("order_by=");
            query.push_str(match order_by {
                ListingOrderBy::Id => "id",
                ListingOrderBy::Name => "name",
                ListingOrderBy::Path => "path",
                ListingOrderBy::CreatedAt => "created_at",
                ListingOrderBy::UpdatedAt => "updated_at",
                ListingOrderBy::LastActivityAt => "last_activity_at",
            });
        });

        self.sort.map(|sort| {
            query.push_str(split_char);
            split_char = &amp_char;

            query.push_str("sort=");
            query.push_str(match sort {
                ::ListingSort::Asc => "asc",
                ::ListingSort::Desc => "desc",
            });
        });

        if !self.search.is_empty() {
            query.push_str(split_char);
            // split_char = &amp_char;

            query.push_str("search=");
            query.push_str(&self.search);
        }

        query
    }
}

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

    #[test]
    fn build_query_default() {
        let expected_string = "projects/owned";
        let listing: Listing = Default::default();
        let query = listing.build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned";
        let listing = Listing::new();
        let query = listing.build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_archived() {
        let expected_string = "projects/owned?archived=true";
        let query = Listing::new().archived(true).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?archived=false";
        let query = Listing::new().archived(false).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_visibility() {
        let expected_string = "projects/owned?visibility=public";
        let query = Listing::new().visibility(::ListingVisibility::Public).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?visibility=internal";
        let query = Listing::new().visibility(::ListingVisibility::Internal).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?visibility=private";
        let query = Listing::new().visibility(::ListingVisibility::Private).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_order_by() {
        let expected_string = "projects/owned?order_by=id";
        let query = Listing::new().order_by(ListingOrderBy::Id).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?order_by=name";
        let query = Listing::new().order_by(ListingOrderBy::Name).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?order_by=path";
        let query = Listing::new().order_by(ListingOrderBy::Path).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?order_by=created_at";
        let query = Listing::new().order_by(ListingOrderBy::CreatedAt).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?order_by=updated_at";
        let query = Listing::new().order_by(ListingOrderBy::UpdatedAt).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?order_by=last_activity_at";
        let query = Listing::new().order_by(ListingOrderBy::LastActivityAt).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_sort() {
        let expected_string = "projects/owned?sort=asc";
        let query = Listing::new().sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);

        let expected_string = "projects/owned?sort=desc";
        let query = Listing::new().sort(::ListingSort::Desc).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn build_query_search() {
        let expected_string = "projects/owned?search=SearchPattern";
        let query = Listing::new().search(String::from("SearchPattern")).build_query();
        assert_eq!(query, expected_string);
    }

    #[test]
    fn groups_build_query_multiple() {
        let expected_string = "projects/owned?archived=false&sort=asc";
        let query = Listing::new().archived(false).sort(::ListingSort::Asc).build_query();
        assert_eq!(query, expected_string);
    }
}
<commit_msg>Change API for projects/owned<commit_after>//! List owned projects
//!
//! https://docs.gitlab.com/ce/api/projects.html#list-owned-projects
//!
//! # List owned projects
//!
//! Get a list of projects which are owned by the authenticated user.
//!
//! ```text
//! GET /projects/owned
//! ```
//!
//! Parameters:
//!
//! | Attribute | Type | Required | Description |
//! | --------- | ---- | -------- | ----------- |
//! | `archived` | boolean | no | Limit by archived status |
//! | `visibility` | string | no | Limit by visibility `public`, `internal`, or `private` |
//! | `order_by` | string | no | Return projects ordered by `id`, `name`, `path`, `created_at`, `updated_at`, or `last_activity_at` fields. Default is `created_at` |
//! | `sort` | string | no | Return projects sorted in `asc` or `desc` order. Default is `desc` |
//! | `search` | string | no | Return list of authorized projects matching the search criteria |

use serde_json;
use serde_urlencoded;

use BuildQuery;
use gitlab::GitLab;
use Projects;
use projects::{OwnedProjectListerInternal, ListingOrderBy};

// impl GitLab {
//     pub fn projects_owned(&self, listing: Listing) -> Result<Projects, serde_json::Error> {
//         let query = listing.build_query();
//         self.get(&query)
//     }
// }

// #[derive(Debug, Copy, Clone)]
// pub enum ListingOrderBy {
//     Id,
//     Name,
//     Path,
//     CreatedAt,
//     UpdatedAt,
//     LastActivityAt,
// }

#[derive(Debug, Clone)]
pub struct ProjectsLister<'a> {
    gl: &'a ::GitLab,
    internal: OwnedProjectListerInternal,
}

impl<'a> ProjectsLister<'a> {
    pub fn new(gl: &'a ::GitLab) -> ProjectsLister {
        ProjectsLister {
            gl: gl,
            internal: OwnedProjectListerInternal {
                archived: None,
                visibility: None,
                order_by: None,
                sort: None,
                search: None,
            },
        }
    }

    pub fn archived(&'a mut self, archived: bool) -> &'a mut ProjectsLister {
        self.internal.archived = Some(archived);
        self
    }

    pub fn visibility(&'a mut self, visibility: ::ListingVisibility) -> &'a mut ProjectsLister {
        self.internal.visibility = Some(visibility);
        self
    }

    pub fn order_by(&'a mut self, order_by: ListingOrderBy) -> &'a mut ProjectsLister {
        self.internal.order_by = Some(order_by);
        self
    }

    pub fn sort(&'a mut self, sort: ::ListingSort) -> &'a mut ProjectsLister {
        self.internal.sort = Some(sort);
        self
    }

    pub fn search(&'a mut self, search: String) -> &'a mut ProjectsLister {
        self.internal.search = Some(search);
        self
    }

    /// Commit the lister: Query GitLab and return a list of projects.
    pub fn list(&self) -> Projects {
        // let query = serde_urlencoded::to_string(&self);
        let query = self.build_query();
        debug!("query: {:?}", query);

        let projects: Result<Projects, serde_json::Error> = self.gl.get(&query);

        projects.unwrap()
    }
}

impl<'a> BuildQuery for ProjectsLister<'a> {
    fn build_query(&self) -> String {

        let encoded = serde_urlencoded::to_string(&self.internal).unwrap();
        let mut query = String::from("projects/owned");
        if !encoded.is_empty() {
            query.push_str("?");
            query.push_str(&encoded);
        }

        query
    }
}

// #[derive(Default, Debug, Clone)]
// pub struct Listing {
//     /// Limit by archived status
//     archived: Option<bool>,
//     /// Limit by visibility.
//     visibility: Option<::ListingVisibility>,
//     /// Return requests ordered by. Default is `ListingOrderBy::CreatedAt`.
//     order_by: Option<ListingOrderBy>,
//     /// Return requests sorted. Default is `::ListingSort::Desc`.
//     sort: Option<::ListingSort>,
//     /// Return list of authorized projects matching the search criteria.
//     search: String,
// }

// #[allow(dead_code)]
// impl Listing {
//     pub fn new() -> Listing {
//         Default::default()
//     }
//     pub fn archived(&mut self, archived: bool) -> &mut Listing {
//         self.archived = Some(archived);
//         self
//     }
//     pub fn visibility(&mut self, visibility: ::ListingVisibility) -> &mut Listing {
//         self.visibility = Some(visibility);
//         self
//     }
//     pub fn order_by(&mut self, order_by: ListingOrderBy) -> &mut Listing {
//         self.order_by = Some(order_by);
//         self
//     }
//     fn sort(&mut self, sort: ::ListingSort) -> &mut Listing {
//         self.sort = Some(sort);
//         self
//     }
//     pub fn search(&mut self, search: String) -> &mut Listing {
//         self.search = search;
//         self
//     }
// }
//
//
// impl BuildQuery for Listing {
//     fn build_query(&self) -> String {
//
//         let mut query = String::from("projects/owned");
//
//         let amp_char = "&";
//         let none_char = "";
//         let mut split_char = &none_char;
//
//         // Append a "?" only if at least one of the `Option` is `Some(_)` or if
//         // strings contain something.
//         query.push_str(match (&self.archived,
//                               &self.visibility,
//                               &self.order_by,
//                               &self.sort,
//                               self.search.is_empty()) {
//             (&None, &None, &None, &None, true) => "",
//             _ => "?",
//         });
//
//         self.archived.map(|archived| {
//             query.push_str(split_char);
//             split_char = &amp_char;
//
//             if archived {
//                 query.push_str("archived=true")
//             } else {
//                 query.push_str("archived=false")
//             }
//         });
//
//         self.visibility.map(|visibility| {
//             query.push_str(split_char);
//             split_char = &amp_char;
//
//             query.push_str("visibility=");
//             query.push_str(match visibility {
//                 ::ListingVisibility::Public => "public",
//                 ::ListingVisibility::Internal => "internal",
//                 ::ListingVisibility::Private => "private",
//             });
//         });
//
//         self.order_by.map(|order_by| {
//             query.push_str(split_char);
//             split_char = &amp_char;
//
//             query.push_str("order_by=");
//             query.push_str(match order_by {
//                 ListingOrderBy::Id => "id",
//                 ListingOrderBy::Name => "name",
//                 ListingOrderBy::Path => "path",
//                 ListingOrderBy::CreatedAt => "created_at",
//                 ListingOrderBy::UpdatedAt => "updated_at",
//                 ListingOrderBy::LastActivityAt => "last_activity_at",
//             });
//         });
//
//         self.sort.map(|sort| {
//             query.push_str(split_char);
//             split_char = &amp_char;
//
//             query.push_str("sort=");
//             query.push_str(match sort {
//                 ::ListingSort::Asc => "asc",
//                 ::ListingSort::Desc => "desc",
//             });
//         });
//
//         if !self.search.is_empty() {
//             query.push_str(split_char);
//             // split_char = &amp_char;
//
//             query.push_str("search=");
//             query.push_str(&self.search);
//         }
//
//         query
//     }
// }

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

    // #[test]
    // fn build_query_default() {
    //     let expected_string = "projects/owned";
    //     let listing: Listing = Default::default();
    //     let query = listing.build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned";
    //     let listing = Listing::new();
    //     let query = listing.build_query();
    //     assert_eq!(query, expected_string);
    // }
    //
    //
    // #[test]
    // fn build_query_archived() {
    //     let expected_string = "projects/owned?archived=true";
    //     let query = Listing::new().archived(true).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?archived=false";
    //     let query = Listing::new().archived(false).build_query();
    //     assert_eq!(query, expected_string);
    // }
    //
    //
    // #[test]
    // fn build_query_visibility() {
    //     let expected_string = "projects/owned?visibility=public";
    //     let query = Listing::new().visibility(::ListingVisibility::Public).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?visibility=internal";
    //     let query = Listing::new().visibility(::ListingVisibility::Internal).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?visibility=private";
    //     let query = Listing::new().visibility(::ListingVisibility::Private).build_query();
    //     assert_eq!(query, expected_string);
    // }
    //
    //
    // #[test]
    // fn build_query_order_by() {
    //     let expected_string = "projects/owned?order_by=id";
    //     let query = Listing::new().order_by(ListingOrderBy::Id).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?order_by=name";
    //     let query = Listing::new().order_by(ListingOrderBy::Name).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?order_by=path";
    //     let query = Listing::new().order_by(ListingOrderBy::Path).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?order_by=created_at";
    //     let query = Listing::new().order_by(ListingOrderBy::CreatedAt).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?order_by=updated_at";
    //     let query = Listing::new().order_by(ListingOrderBy::UpdatedAt).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?order_by=last_activity_at";
    //     let query = Listing::new().order_by(ListingOrderBy::LastActivityAt).build_query();
    //     assert_eq!(query, expected_string);
    // }
    //
    //
    // #[test]
    // fn build_query_sort() {
    //     let expected_string = "projects/owned?sort=asc";
    //     let query = Listing::new().sort(::ListingSort::Asc).build_query();
    //     assert_eq!(query, expected_string);
    //
    //     let expected_string = "projects/owned?sort=desc";
    //     let query = Listing::new().sort(::ListingSort::Desc).build_query();
    //     assert_eq!(query, expected_string);
    // }
    //
    //
    // #[test]
    // fn build_query_search() {
    //     let expected_string = "projects/owned?search=SearchPattern";
    //     let query = Listing::new().search(String::from("SearchPattern")).build_query();
    //     assert_eq!(query, expected_string);
    // }
    //
    //
    // #[test]
    // fn groups_build_query_multiple() {
    //     let expected_string = "projects/owned?archived=false&sort=asc";
    //     let query = Listing::new().archived(false).sort(::ListingSort::Asc).build_query();
    //     assert_eq!(query, expected_string);
    // }
}
<|endoftext|>


<commit_before>use util::bits::Bits as _;

use crate::memory::{
    io::{Effect, IoRegisters},
    palette::Palette,
};

pub const OBJ: usize = 4;
pub const BACKDROP: usize = 5;

pub struct LineBuffer {
    // 240 pixels for each background (BG0-3 + OBJ)
    pixels: [[u16; 240]; 5],
    layer_attrs: [LayerAttrs; 5],
}

impl Default for LineBuffer {
    fn default() -> Self {
        LineBuffer {
            pixels: [[0; 240]; 5],
            layer_attrs: [LayerAttrs::default(); 5],
        }
    }
}

impl LineBuffer {
    pub(crate) fn put(&mut self, layer: usize, x: usize, pixel: u16) {
        self.pixels[layer][x] = pixel | 0x8000;
    }

    pub(crate) fn put_obj_4bpp(&mut self, attrs: PixelAttrs, x: usize, palette: u8, entry: u8) {
        self.pixels[OBJ][x] =
            (attrs.value as u16) | ((palette as u16) << 12) | ((entry as u16) << 8);
    }

    pub(crate) fn put_obj_8bpp(&mut self, attrs: PixelAttrs, x: usize, entry: u8) {
        self.pixels[OBJ][x] = attrs.value as u16 | ((entry as u16) << 8);
    }

    pub(crate) fn put_4bpp(&mut self, layer: usize, x: usize, palette: u8, entry: u8) {
        self.pixels[layer][x] = ((palette as u16) << 4) | (entry as u16);
    }

    pub(crate) fn put_8bpp(&mut self, layer: usize, x: usize, entry: u8) {
        self.pixels[layer][x] = entry as u16;
    }

    pub(crate) fn layer_attrs_mut(&mut self, layer: usize) -> &mut LayerAttrs {
        &mut self.layer_attrs[layer]
    }

    fn color_obj(&self, x: usize, priority: u16, palette: &Palette) -> Option<(u16, PixelAttrs)> {
        let entry = self.pixels[OBJ][x];
        let attrs = PixelAttrs { value: entry as u8 };

        if attrs.priority() != priority {
            return None;
        }

        let entry = (entry >> 8) as u8;

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = entry >> 4;
            Some((
                palette.get_obj16(palette_index as _, color_entry as _),
                attrs,
            ))
        } else if entry as u8 == 0 {
            None
        } else {
            Some((palette.get_obj256(entry), attrs))
        }
    }

    fn color_bg(&self, layer: usize, x: usize, palette: &Palette) -> Option<u16> {
        let attrs = self.layer_attrs[layer];
        let entry = self.pixels[layer][x];

        if attrs.is_bitmap() {
            return Some(entry);
        }

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = (entry >> 4) & 0xF;

            Some(palette.get_bg16(palette_index as _, color_entry as _))
        } else if entry == 0 {
            None
        } else {
            Some(palette.get_bg256(entry as _))
        }
    }

    pub fn render(&self, output: &mut [u16], ioregs: &IoRegisters, palette: &Palette) {
        let backdrop_color = palette.get_bg256(0) | 0x8000;
        let mut backdrop_attrs = PixelAttrs::default();
        if ioregs.bldcnt.is_first_target(BACKDROP) {
            backdrop_attrs.set_first_target();
        }
        if ioregs.bldcnt.is_second_target(BACKDROP) {
            backdrop_attrs.set_second_target();
        }
        let mut backdrop = Pixel::default();
        backdrop.push(backdrop_color, backdrop_attrs);
        let mut pixels = [backdrop; 240];

        for priority in (0..4).rev() {
            for bg in (0usize..4).rev() {
                // FIXME do window checks here.
                if !ioregs.dispcnt.display_bg(bg as _) || ioregs.bgcnt[bg].priority() != priority {
                    continue;
                }

                let mut attrs = PixelAttrs::default();
                if ioregs.bldcnt.is_first_target(bg) {
                    attrs.set_first_target();
                }
                if ioregs.bldcnt.is_second_target(bg) {
                    attrs.set_second_target();
                }

                (0..240).for_each(|x| {
                    if let Some(color) = self.color_bg(bg, x, palette) {
                        pixels[x].push(color, attrs);
                    }
                });
            }

            if ioregs.dispcnt.display_obj() {
                (0..240).for_each(|x| {
                    if let Some((color, attrs)) = self.color_obj(x, priority, palette) {
                        pixels[x].push(color, attrs);
                    }
                });
            }
        }

        (0..240)
            .map(|x| (x, self.render_pixel(pixels[x], ioregs)))
            .for_each(|(x, p)| output[x] = p);
    }

    fn render_pixel(&self, pixel: Pixel, ioregs: &IoRegisters) -> u16 {
        let Pixel { mut attrs, colors } = pixel;

        let mut effect = ioregs.bldcnt.effect();

        // OBJs that are defined as 'Semi-Transparent' in OAM memory are always selected as 1st Target (regardless of BLDCNT Bit 4),
        // and are always using Alpha Blending mode (regardless of BLDCNT Bit 6-7).
        // The BLDCNT register may be used to perform Brightness effects on the OBJ (and/or other BG/BD layers).
        // However, if a semi-transparent OBJ pixel does overlap a 2nd target pixel, then semi-transparency becomes priority,
        // and the brightness effect will not take place (neither on 1st, nor 2nd target).
        if attrs.0.is_semi_transparent() {
            attrs.0.set_first_target();
            if attrs.1.is_second_target() {
                effect = Effect::AlphaBlending;
            }
        }

        if effect == Effect::None || !attrs.0.is_first_target() {
            return colors.0;
        }

        match effect {
            Effect::AlphaBlending => {
                // For this effect, the top-most non-transparent pixel must be selected as 1st Target,
                // and the next-lower non-transparent pixel must be selected as 2nd Target, if so - and only if so,
                // then color intensities of 1st and 2nd Target are mixed together by using the parameters in BLDALPHA register,
                // for each pixel each R, G, B intensities are calculated separately:
                //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
                // Otherwise - for example, if only one target exists, or if a non-transparent non-2nd-target
                // pixel is moved between the two targets, or if 2nd target has higher display priority than 1st target -
                // then only the top-most pixel is displayed (at normal intensity, regardless of BLDALPHA).
                if attrs.1.is_second_target() {
                    let eva = ioregs.bldalpha.eva_coeff();
                    let evb = ioregs.bldalpha.evb_coeff();
                    alpha_blend(colors.0, colors.1, eva, evb)
                } else {
                    colors.0
                }
            }

            Effect::BrightnessIncrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st + (31-I1st)*EVY   ;For Brightness Increase
                // The color intensities of any selected 1st target surface(s) are increased by using the parameter in BLDY register.
                let evy = ioregs.bldy.evy_coeff();
                brightness_increase(colors.0, evy)
            }

            Effect::BrightnessDecrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st - (I1st)*EVY      ;For Brightness Decrease
                // The color intensities of any selected 1st target surface(s) are decreased by using the parameter in BLDY register.
                let evy = ioregs.bldy.evy_coeff();
                brightness_decrease(colors.0, evy)
            }

            Effect::None => unreachable!(),
        }
    }
}

#[derive(Default, Copy, Clone)]
struct Pixel {
    attrs: (PixelAttrs, PixelAttrs),
    colors: (u16, u16),
}

impl Pixel {
    pub fn push(&mut self, color: u16, attrs: PixelAttrs) {
        self.colors.1 = self.colors.0;
        self.colors.0 = color;

        self.attrs.1 = self.attrs.0;
        self.attrs.0 = attrs;
    }
}

fn alpha_blend(c1: u16, c2: u16, eva: u16, evb: u16) -> u16 {
    let (r1, g1, b1) = decompose(c1);
    let (r2, g2, b2) = decompose(c2);
    //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
    let r = (r1 * eva + r2 * evb) / 16;
    let g = (g1 * eva + g2 * evb) / 16;
    let b = (b1 * eva + b2 * evb) / 16;
    recompose(r, g, b)
}

fn brightness_increase(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st + (31-I1st)*EVY
    let r = r + (((31 - r) * evy) / 16);
    let g = g + (((31 - g) * evy) / 16);
    let b = b + (((31 - b) * evy) / 16);
    recompose(r, g, b)
}

fn brightness_decrease(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st - (I1st)*EVY
    let r = r + ((r * evy) / 16);
    let g = g + ((g * evy) / 16);
    let b = b + ((b * evy) / 16);
    recompose(r, g, b)
}

fn decompose(c: u16) -> (u16, u16, u16) {
    let r = c.bits(0, 4);
    let g = c.bits(5, 9);
    let b = c.bits(10, 14);
    (r, g, b)
}

fn recompose(r: u16, g: u16, b: u16) -> u16 {
    r.min(31) | (g.min(31) << 5) | (b.min(31) << 10)
}

#[derive(Clone, Copy, Default)]
pub(crate) struct LayerAttrs {
    value: u8,
}

impl LayerAttrs {
    const BITMAP_16BPP: u8 = 0x1;
    const PALETTE_4BPP: u8 = 0x2;

    pub fn is_bitmap(&self) -> bool {
        (self.value & Self::BITMAP_16BPP) != 0
    }

    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    pub fn set_bitmap(&mut self) {
        self.value |= Self::BITMAP_16BPP;
    }

    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    pub fn set_8bpp(&mut self) {
        /* NOP */
    }
}

#[derive(Clone, Copy, Default)]
pub(crate) struct PixelAttrs {
    value: u8,
}

impl PixelAttrs {
    // # NOTE Bits 6 and 7 are used for priority

    const FIRST_TARGET: u8 = 0x1; // bit 0
    const SECOND_TARGET: u8 = 0x2; // bit 1
    const PALETTE_4BPP: u8 = 0x4; // bit 2
    const SEMI_TRANSPARENT: u8 = 0x8; // bit 3

    pub fn is_first_target(&self) -> bool {
        (self.value & Self::FIRST_TARGET) != 0
    }

    pub fn is_second_target(&self) -> bool {
        (self.value & Self::SECOND_TARGET) != 0
    }

    pub fn set_first_target(&mut self) {
        self.value |= Self::FIRST_TARGET;
    }

    pub fn set_second_target(&mut self) {
        self.value |= Self::SECOND_TARGET;
    }

    /// Only used by OBJ layer pixels
    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    /// Only used by OBJ layer pixels
    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    /// Only used by OBJ layer pixels
    pub fn set_8bpp(&mut self) {
        /* NOP */
    }

    pub fn set_semi_transparent(&mut self) {
        self.value |= Self::SEMI_TRANSPARENT;
    }

    pub fn is_semi_transparent(&self) -> bool {
        (self.value & Self::SEMI_TRANSPARENT) != 0
    }

    pub fn set_priority(&mut self, priority: u16) {
        self.value |= (priority as u8) << 6;
    }

    pub fn priority(&self) -> u16 {
        (self.value >> 6) as u16
    }
}
<commit_msg>Only pass necessary registers to render_pixel.<commit_after>use util::bits::Bits as _;

use crate::memory::{
    io::{AlphaBlendingCoeff, BrightnessCoeff, Effect, IoRegisters},
    palette::Palette,
};

pub const OBJ: usize = 4;
pub const BACKDROP: usize = 5;

pub struct LineBuffer {
    // 240 pixels for each background (BG0-3 + OBJ)
    pixels: [[u16; 240]; 5],
    layer_attrs: [LayerAttrs; 5],
}

impl Default for LineBuffer {
    fn default() -> Self {
        LineBuffer {
            pixels: [[0; 240]; 5],
            layer_attrs: [LayerAttrs::default(); 5],
        }
    }
}

impl LineBuffer {
    pub(crate) fn put(&mut self, layer: usize, x: usize, pixel: u16) {
        self.pixels[layer][x] = pixel | 0x8000;
    }

    pub(crate) fn put_obj_4bpp(&mut self, attrs: PixelAttrs, x: usize, palette: u8, entry: u8) {
        self.pixels[OBJ][x] =
            (attrs.value as u16) | ((palette as u16) << 12) | ((entry as u16) << 8);
    }

    pub(crate) fn put_obj_8bpp(&mut self, attrs: PixelAttrs, x: usize, entry: u8) {
        self.pixels[OBJ][x] = attrs.value as u16 | ((entry as u16) << 8);
    }

    pub(crate) fn put_4bpp(&mut self, layer: usize, x: usize, palette: u8, entry: u8) {
        self.pixels[layer][x] = ((palette as u16) << 4) | (entry as u16);
    }

    pub(crate) fn put_8bpp(&mut self, layer: usize, x: usize, entry: u8) {
        self.pixels[layer][x] = entry as u16;
    }

    pub(crate) fn layer_attrs_mut(&mut self, layer: usize) -> &mut LayerAttrs {
        &mut self.layer_attrs[layer]
    }

    fn color_obj(&self, x: usize, priority: u16, palette: &Palette) -> Option<(u16, PixelAttrs)> {
        let entry = self.pixels[OBJ][x];
        let attrs = PixelAttrs { value: entry as u8 };

        if attrs.priority() != priority {
            return None;
        }

        let entry = (entry >> 8) as u8;

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = entry >> 4;
            Some((
                palette.get_obj16(palette_index as _, color_entry as _),
                attrs,
            ))
        } else if entry as u8 == 0 {
            None
        } else {
            Some((palette.get_obj256(entry), attrs))
        }
    }

    fn color_bg(&self, layer: usize, x: usize, palette: &Palette) -> Option<u16> {
        let attrs = self.layer_attrs[layer];
        let entry = self.pixels[layer][x];

        if attrs.is_bitmap() {
            return Some(entry);
        }

        if attrs.is_4bpp() {
            let color_entry = entry & 0xF;
            if color_entry == 0 {
                return None;
            }
            let palette_index = (entry >> 4) & 0xF;

            Some(palette.get_bg16(palette_index as _, color_entry as _))
        } else if entry == 0 {
            None
        } else {
            Some(palette.get_bg256(entry as _))
        }
    }

    pub fn render(&self, output: &mut [u16], ioregs: &IoRegisters, palette: &Palette) {
        let backdrop_color = palette.get_bg256(0) | 0x8000;
        let mut backdrop_attrs = PixelAttrs::default();
        if ioregs.bldcnt.is_first_target(BACKDROP) {
            backdrop_attrs.set_first_target();
        }
        if ioregs.bldcnt.is_second_target(BACKDROP) {
            backdrop_attrs.set_second_target();
        }
        let mut backdrop = Pixel::default();
        backdrop.push(backdrop_color, backdrop_attrs);
        let mut pixels = [backdrop; 240];

        for priority in (0..4).rev() {
            for bg in (0usize..4).rev() {
                // FIXME do window checks here.
                if !ioregs.dispcnt.display_bg(bg as _) || ioregs.bgcnt[bg].priority() != priority {
                    continue;
                }

                let mut attrs = PixelAttrs::default();
                if ioregs.bldcnt.is_first_target(bg) {
                    attrs.set_first_target();
                }
                if ioregs.bldcnt.is_second_target(bg) {
                    attrs.set_second_target();
                }

                (0..240).for_each(|x| {
                    if let Some(color) = self.color_bg(bg, x, palette) {
                        pixels[x].push(color, attrs);
                    }
                });
            }

            if ioregs.dispcnt.display_obj() {
                (0..240).for_each(|x| {
                    if let Some((color, attrs)) = self.color_obj(x, priority, palette) {
                        pixels[x].push(color, attrs);
                    }
                });
            }
        }

        let effect = ioregs.bldcnt.effect();
        let bldalpha = ioregs.bldalpha;
        let bldy = ioregs.bldy;

        (0..240)
            .map(|x| (x, self.render_pixel(pixels[x], effect, bldalpha, bldy)))
            .for_each(|(x, p)| output[x] = p);
    }

    fn render_pixel(
        &self,
        pixel: Pixel,
        mut effect: Effect,
        bldalpha: AlphaBlendingCoeff,
        bldy: BrightnessCoeff,
    ) -> u16 {
        let Pixel { mut attrs, colors } = pixel;

        // OBJs that are defined as 'Semi-Transparent' in OAM memory are always selected as 1st Target (regardless of BLDCNT Bit 4),
        // and are always using Alpha Blending mode (regardless of BLDCNT Bit 6-7).
        // The BLDCNT register may be used to perform Brightness effects on the OBJ (and/or other BG/BD layers).
        // However, if a semi-transparent OBJ pixel does overlap a 2nd target pixel, then semi-transparency becomes priority,
        // and the brightness effect will not take place (neither on 1st, nor 2nd target).
        if attrs.0.is_semi_transparent() {
            attrs.0.set_first_target();
            if attrs.1.is_second_target() {
                effect = Effect::AlphaBlending;
            }
        }

        if effect == Effect::None || !attrs.0.is_first_target() {
            return colors.0;
        }

        match effect {
            Effect::AlphaBlending => {
                // For this effect, the top-most non-transparent pixel must be selected as 1st Target,
                // and the next-lower non-transparent pixel must be selected as 2nd Target, if so - and only if so,
                // then color intensities of 1st and 2nd Target are mixed together by using the parameters in BLDALPHA register,
                // for each pixel each R, G, B intensities are calculated separately:
                //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
                // Otherwise - for example, if only one target exists, or if a non-transparent non-2nd-target
                // pixel is moved between the two targets, or if 2nd target has higher display priority than 1st target -
                // then only the top-most pixel is displayed (at normal intensity, regardless of BLDALPHA).
                if attrs.1.is_second_target() {
                    let eva = bldalpha.eva_coeff();
                    let evb = bldalpha.evb_coeff();
                    alpha_blend(colors.0, colors.1, eva, evb)
                } else {
                    colors.0
                }
            }

            Effect::BrightnessIncrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st + (31-I1st)*EVY   ;For Brightness Increase
                // The color intensities of any selected 1st target surface(s) are increased by using the parameter in BLDY register.
                let evy = bldy.evy_coeff();
                brightness_increase(colors.0, evy)
            }

            Effect::BrightnessDecrease => {
                //  For each pixel each R, G, B intensities are calculated separately:
                //   I = I1st - (I1st)*EVY      ;For Brightness Decrease
                // The color intensities of any selected 1st target surface(s) are decreased by using the parameter in BLDY register.
                let evy = bldy.evy_coeff();
                brightness_decrease(colors.0, evy)
            }

            Effect::None => unreachable!(),
        }
    }
}

#[derive(Default, Copy, Clone)]
struct Pixel {
    attrs: (PixelAttrs, PixelAttrs),
    colors: (u16, u16),
}

impl Pixel {
    pub fn push(&mut self, color: u16, attrs: PixelAttrs) {
        self.colors.1 = self.colors.0;
        self.colors.0 = color;

        self.attrs.1 = self.attrs.0;
        self.attrs.0 = attrs;
    }
}

fn alpha_blend(c1: u16, c2: u16, eva: u16, evb: u16) -> u16 {
    let (r1, g1, b1) = decompose(c1);
    let (r2, g2, b2) = decompose(c2);
    //   I = MIN ( 31, I1st*EVA + I2nd*EVB )
    let r = (r1 * eva + r2 * evb) / 16;
    let g = (g1 * eva + g2 * evb) / 16;
    let b = (b1 * eva + b2 * evb) / 16;
    recompose(r, g, b)
}

fn brightness_increase(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st + (31-I1st)*EVY
    let r = r + (((31 - r) * evy) / 16);
    let g = g + (((31 - g) * evy) / 16);
    let b = b + (((31 - b) * evy) / 16);
    recompose(r, g, b)
}

fn brightness_decrease(c: u16, evy: u16) -> u16 {
    let (r, g, b) = decompose(c);
    //   I = I1st - (I1st)*EVY
    let r = r + ((r * evy) / 16);
    let g = g + ((g * evy) / 16);
    let b = b + ((b * evy) / 16);
    recompose(r, g, b)
}

fn decompose(c: u16) -> (u16, u16, u16) {
    let r = c.bits(0, 4);
    let g = c.bits(5, 9);
    let b = c.bits(10, 14);
    (r, g, b)
}

fn recompose(r: u16, g: u16, b: u16) -> u16 {
    r.min(31) | (g.min(31) << 5) | (b.min(31) << 10)
}

#[derive(Clone, Copy, Default)]
pub(crate) struct LayerAttrs {
    value: u8,
}

impl LayerAttrs {
    const BITMAP_16BPP: u8 = 0x1;
    const PALETTE_4BPP: u8 = 0x2;

    pub fn is_bitmap(&self) -> bool {
        (self.value & Self::BITMAP_16BPP) != 0
    }

    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    pub fn set_bitmap(&mut self) {
        self.value |= Self::BITMAP_16BPP;
    }

    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    pub fn set_8bpp(&mut self) {
        /* NOP */
    }
}

#[derive(Clone, Copy, Default)]
pub(crate) struct PixelAttrs {
    value: u8,
}

impl PixelAttrs {
    // # NOTE Bits 6 and 7 are used for priority

    const FIRST_TARGET: u8 = 0x1; // bit 0
    const SECOND_TARGET: u8 = 0x2; // bit 1
    const PALETTE_4BPP: u8 = 0x4; // bit 2
    const SEMI_TRANSPARENT: u8 = 0x8; // bit 3

    pub fn is_first_target(&self) -> bool {
        (self.value & Self::FIRST_TARGET) != 0
    }

    pub fn is_second_target(&self) -> bool {
        (self.value & Self::SECOND_TARGET) != 0
    }

    pub fn set_first_target(&mut self) {
        self.value |= Self::FIRST_TARGET;
    }

    pub fn set_second_target(&mut self) {
        self.value |= Self::SECOND_TARGET;
    }

    /// Only used by OBJ layer pixels
    pub fn is_4bpp(&self) -> bool {
        (self.value & Self::PALETTE_4BPP) != 0
    }

    /// Only used by OBJ layer pixels
    pub fn set_4bpp(&mut self) {
        self.value |= Self::PALETTE_4BPP;
    }

    /// Only used by OBJ layer pixels
    pub fn set_8bpp(&mut self) {
        /* NOP */
    }

    pub fn set_semi_transparent(&mut self) {
        self.value |= Self::SEMI_TRANSPARENT;
    }

    pub fn is_semi_transparent(&self) -> bool {
        (self.value & Self::SEMI_TRANSPARENT) != 0
    }

    pub fn set_priority(&mut self, priority: u16) {
        self.value |= (priority as u8) << 6;
    }

    pub fn priority(&self) -> u16 {
        (self.value >> 6) as u16
    }
}
<|endoftext|>


<commit_before>use types::{FromRedisValue, ToRedisArgs, RedisResult, NumberIsFloat};
use client::Client;
use connection::Connection;
use cmd::{cmd, Cmd, Pipeline};

#[deriving(PartialEq, Eq, Clone, Show)]
pub enum BitOp {
    BitAnd,
    BitOr,
    BitXor,
    BitNot,
}

macro_rules! implement_commands {
    (
        $(
            $(#[$attr:meta])+
            fn $name:ident<$($tyargs:ident : $ty:ident),*>(
                $self_:ident$(, $argname:ident: $argty:ty)*) $body:block
        )*
    ) =>
    (
        /// Implements common redis commands for connection like objects.  This
        /// allows you to send commands straight to a connection or client.  It
        /// is also implemented for redis results of clients which makes for
        /// very convenient access in some basic cases.
        ///
        /// This allows you to use nicer syntax for some common operations.
        /// For instance this code:
        ///
        /// ```rust,no_run
        /// let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        /// let con = client.get_connection().unwrap();
        /// redis::cmd("SET").arg("my_key").arg(42i).execute(&con);
        /// assert_eq!(redis::cmd("GET").arg("my_key").query(&con), Ok(42i));
        /// ```
        ///
        /// Will become this:
        ///
        /// ```rust,no_run
        /// use redis::Commands;
        /// let client = redis::Client::open("redis://127.0.0.1/");
        /// assert_eq!(client.get("my_key"), Ok(42i));
        /// ```
        pub trait Commands {
            #[doc(hidden)]
            fn perform<T: FromRedisValue>(&self, con: &Cmd) -> RedisResult<T>;

            $(
                $(#[$attr])*
                fn $name<$($tyargs: $ty,)* RV: FromRedisValue>(
                    &$self_ $(, $argname: $argty)*) -> RedisResult<RV>
                    { $self_.perform($body) }
            )*
        }

        /// Implements common redis commands for pipelines.  Unlike the regular
        /// commands trait, this returns the pipeline rather than a result
        /// directly.  Other than that it works the same however.
        pub trait PipelineCommands {
            #[doc(hidden)]
            fn perform<'a>(&'a mut self, con: &Cmd) -> &'a mut Self;

            $(
                $(#[$attr])*
                fn $name<'a $(, $tyargs: $ty)*>(
                    &'a mut $self_ $(, $argname: $argty)*) -> &'a mut Self
                    { $self_.perform($body) }
            )*
        }
    )
}

implement_commands!(
    // most common operations

    #[doc="Get the value of a key.  If key is a vec this becomes an `MGET`."]
    fn get<K: ToRedisArgs>(self, key: K) {
        cmd(if key.is_single_arg() { "GET" } else { "MGET" }).arg(key)
    }

    #[doc="Set the string value of a key."]
    fn set<K: ToRedisArgs, V: ToRedisArgs>(self, key: K, value: V) {
        cmd("SET").arg(key).arg(value)
    }

    #[doc="Set the value and expiration of a key."]
    fn set_ex<K: ToRedisArgs, V: ToRedisArgs>(self, key: K, value: V, seconds: uint) {
        cmd("SETEX").arg(key).arg(value).arg(seconds)
    }

    #[doc="Set the value of a key, only if the key does not exist"]
    fn set_nx<K: ToRedisArgs, V: ToRedisArgs>(self, key: K, value: V) {
        cmd("SETNX").arg(key).arg(value)
    }

    #[doc="Set the string value of a key and return its old value."]
    fn getset<K: ToRedisArgs, V: ToRedisArgs>(self, key: K, value: V) {
        cmd("GETSET").arg(key).arg(value)
    }

    #[doc="Delete one or more keys."]
    fn del<K: ToRedisArgs>(self, key: K) {
        cmd("DEL").arg(key)
    }

    #[doc="Determine if a key exists."]
    fn exists<K: ToRedisArgs>(self, key: K) {
        cmd("EXISTS").arg(key)
    }

    #[doc="Set a key's time to live in seconds."]
    fn expire<K: ToRedisArgs>(self, key: K, seconds: uint) {
        cmd("EXPIRE").arg(key).arg(seconds)
    }

    #[doc="Set the expiration for a key as a UNIX timestamp."]
    fn expire_at<K: ToRedisArgs>(self, key: K, ts: uint) {
        cmd("EXPIREAT").arg(key).arg(ts)
    }

    #[doc="Set a key's time to live in milliseconds."]
    fn pexpire<K: ToRedisArgs>(self, key: K, ms: uint) {
        cmd("PEXPIRE").arg(key).arg(ms)
    }

    #[doc="Set the expiration for a key as a UNIX timestamp in milliseconds."]
    fn pexpire_at<K: ToRedisArgs>(self, key: K, ts: uint) {
        cmd("PEXPIREAT").arg(key).arg(ts)
    }

    #[doc="Remove the expiration from a key."]
    fn persist<K: ToRedisArgs>(self, key: K) {
        cmd("PERSIST").arg(key)
    }

    #[doc="Rename a key."]
    fn rename<K: ToRedisArgs>(self, key: K, new_key: K) {
        cmd("RENAME").arg(key).arg(new_key)
    }

    #[doc="Rename a key, only if the new key does not exist."]
    fn rename_nx<K: ToRedisArgs>(self, key: K, new_key: K) {
        cmd("RENAMENX").arg(key).arg(new_key)
    }

    // common string operations

    #[doc="Append a value to a key."]
    fn append<K: ToRedisArgs, V: ToRedisArgs>(self, key: K, value: V) {
        cmd("APPEND").arg(key).arg(value)
    }

    #[doc="Increment the numeric value of a key by the given amount.  This
          issues a `INCR` or `INCRBYFLOAT` depending on the type."]
    fn incr<K: ToRedisArgs, V: ToRedisArgs>(self, key: K, delta: V) {
        cmd(if delta.describe_numeric_behavior() == NumberIsFloat {
            "INCRBYFLOAT"
        } else {
            "INCR"
        }).arg(key).arg(delta)
    }

    #[doc="Sets or clears the bit at offset in the string value stored at key."]
    fn setbit<K: ToRedisArgs>(self, key: K, offset: uint, value: bool) {
        cmd("SETBIT").arg(key).arg(offset).arg(value)
    }

    #[doc="Returns the bit value at offset in the string value stored at key."]
    fn getbit<K: ToRedisArgs>(self, key: K, offset: uint) {
        cmd("GETBIT").arg(key).arg(offset)
    }

    #[doc="Count set bits in a string."]
    fn bitcount<K: ToRedisArgs>(self, key: K) {
        cmd("BITCOUNT").arg(key)
    }

    #[doc="Count set bits in a string in a range."]
    fn bitcount_range<K: ToRedisArgs>(self, key: K, start: uint, end: uint) {
        cmd("BITCOUNT").arg(key).arg(start).arg(end)
    }

    #[doc="Perform a bitwise operation between multiple keys (containing string values)
        and store the result in the destination key."]
    fn bitop<K: ToRedisArgs>(self, op: BitOp, dstkey: K, srckeys: K) {
        cmd("BITOP").arg(match op {
            BitAnd => "AND",
            BitOr => "OR",
            BitXor => "XOR",
            BitNot => "NOT"
        }).arg(dstkey).arg(srckeys)
    }

    #[doc="Get the length of the value stored in a key."]
    fn strlen<K: ToRedisArgs>(self, key: K) {
        cmd("STRLEN").arg(key)
    }
)

impl Commands for Connection {
    fn perform<T: FromRedisValue>(&self, cmd: &Cmd) -> RedisResult<T> {
        cmd.query(self)
    }
}

impl Commands for Client {
    fn perform<T: FromRedisValue>(&self, cmd: &Cmd) -> RedisResult<T> {
        cmd.query(&try!(self.get_connection()))
    }
}

impl Commands for RedisResult<Client> {
    fn perform<T: FromRedisValue>(&self, cmd: &Cmd) -> RedisResult<T> {
        match self {
            &Ok(ref client) => cmd.query(&try!(client.get_connection())),
            &Err(ref x) => Err(x.clone())
        }
    }
}

impl PipelineCommands for Pipeline {
    fn perform<'a>(&'a mut self, cmd: &Cmd) -> &'a mut Pipeline {
        self.add_command(cmd)
    }
}
<commit_msg>Get rid of dummy selfs.<commit_after>use types::{FromRedisValue, ToRedisArgs, RedisResult, NumberIsFloat};
use client::Client;
use connection::Connection;
use cmd::{cmd, Cmd, Pipeline};

#[deriving(PartialEq, Eq, Clone, Show)]
pub enum BitOp {
    BitAnd,
    BitOr,
    BitXor,
    BitNot,
}

macro_rules! implement_commands {
    (
        $(
            $(#[$attr:meta])+
            fn $name:ident<$($tyargs:ident : $ty:ident),*>(
                $($argname:ident: $argty:ty),*) $body:block
        )*
    ) =>
    (
        /// Implements common redis commands for connection like objects.  This
        /// allows you to send commands straight to a connection or client.  It
        /// is also implemented for redis results of clients which makes for
        /// very convenient access in some basic cases.
        ///
        /// This allows you to use nicer syntax for some common operations.
        /// For instance this code:
        ///
        /// ```rust,no_run
        /// let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        /// let con = client.get_connection().unwrap();
        /// redis::cmd("SET").arg("my_key").arg(42i).execute(&con);
        /// assert_eq!(redis::cmd("GET").arg("my_key").query(&con), Ok(42i));
        /// ```
        ///
        /// Will become this:
        ///
        /// ```rust,no_run
        /// use redis::Commands;
        /// let client = redis::Client::open("redis://127.0.0.1/");
        /// assert_eq!(client.get("my_key"), Ok(42i));
        /// ```
        pub trait Commands {
            #[doc(hidden)]
            fn perform<T: FromRedisValue>(&self, con: &Cmd) -> RedisResult<T>;

            $(
                $(#[$attr])*
                fn $name<$($tyargs: $ty,)* RV: FromRedisValue>(
                    &self $(, $argname: $argty)*) -> RedisResult<RV>
                    { self.perform($body) }
            )*
        }

        /// Implements common redis commands for pipelines.  Unlike the regular
        /// commands trait, this returns the pipeline rather than a result
        /// directly.  Other than that it works the same however.
        pub trait PipelineCommands {
            #[doc(hidden)]
            fn perform<'a>(&'a mut self, con: &Cmd) -> &'a mut Self;

            $(
                $(#[$attr])*
                fn $name<'a $(, $tyargs: $ty)*>(
                    &'a mut self $(, $argname: $argty)*) -> &'a mut Self
                    { self.perform($body) }
            )*
        }
    )
}

implement_commands!(
    // most common operations

    #[doc="Get the value of a key.  If key is a vec this becomes an `MGET`."]
    fn get<K: ToRedisArgs>(key: K) {
        cmd(if key.is_single_arg() { "GET" } else { "MGET" }).arg(key)
    }

    #[doc="Set the string value of a key."]
    fn set<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
        cmd("SET").arg(key).arg(value)
    }

    #[doc="Set the value and expiration of a key."]
    fn set_ex<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V, seconds: uint) {
        cmd("SETEX").arg(key).arg(value).arg(seconds)
    }

    #[doc="Set the value of a key, only if the key does not exist"]
    fn set_nx<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
        cmd("SETNX").arg(key).arg(value)
    }

    #[doc="Set the string value of a key and return its old value."]
    fn getset<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
        cmd("GETSET").arg(key).arg(value)
    }

    #[doc="Delete one or more keys."]
    fn del<K: ToRedisArgs>(key: K) {
        cmd("DEL").arg(key)
    }

    #[doc="Determine if a key exists."]
    fn exists<K: ToRedisArgs>(key: K) {
        cmd("EXISTS").arg(key)
    }

    #[doc="Set a key's time to live in seconds."]
    fn expire<K: ToRedisArgs>(key: K, seconds: uint) {
        cmd("EXPIRE").arg(key).arg(seconds)
    }

    #[doc="Set the expiration for a key as a UNIX timestamp."]
    fn expire_at<K: ToRedisArgs>(key: K, ts: uint) {
        cmd("EXPIREAT").arg(key).arg(ts)
    }

    #[doc="Set a key's time to live in milliseconds."]
    fn pexpire<K: ToRedisArgs>(key: K, ms: uint) {
        cmd("PEXPIRE").arg(key).arg(ms)
    }

    #[doc="Set the expiration for a key as a UNIX timestamp in milliseconds."]
    fn pexpire_at<K: ToRedisArgs>(key: K, ts: uint) {
        cmd("PEXPIREAT").arg(key).arg(ts)
    }

    #[doc="Remove the expiration from a key."]
    fn persist<K: ToRedisArgs>(key: K) {
        cmd("PERSIST").arg(key)
    }

    #[doc="Rename a key."]
    fn rename<K: ToRedisArgs>(key: K, new_key: K) {
        cmd("RENAME").arg(key).arg(new_key)
    }

    #[doc="Rename a key, only if the new key does not exist."]
    fn rename_nx<K: ToRedisArgs>(key: K, new_key: K) {
        cmd("RENAMENX").arg(key).arg(new_key)
    }

    // common string operations

    #[doc="Append a value to a key."]
    fn append<K: ToRedisArgs, V: ToRedisArgs>(key: K, value: V) {
        cmd("APPEND").arg(key).arg(value)
    }

    #[doc="Increment the numeric value of a key by the given amount.  This
          issues a `INCR` or `INCRBYFLOAT` depending on the type."]
    fn incr<K: ToRedisArgs, V: ToRedisArgs>(key: K, delta: V) {
        cmd(if delta.describe_numeric_behavior() == NumberIsFloat {
            "INCRBYFLOAT"
        } else {
            "INCR"
        }).arg(key).arg(delta)
    }

    #[doc="Sets or clears the bit at offset in the string value stored at key."]
    fn setbit<K: ToRedisArgs>(key: K, offset: uint, value: bool) {
        cmd("SETBIT").arg(key).arg(offset).arg(value)
    }

    #[doc="Returns the bit value at offset in the string value stored at key."]
    fn getbit<K: ToRedisArgs>(key: K, offset: uint) {
        cmd("GETBIT").arg(key).arg(offset)
    }

    #[doc="Count set bits in a string."]
    fn bitcount<K: ToRedisArgs>(key: K) {
        cmd("BITCOUNT").arg(key)
    }

    #[doc="Count set bits in a string in a range."]
    fn bitcount_range<K: ToRedisArgs>(key: K, start: uint, end: uint) {
        cmd("BITCOUNT").arg(key).arg(start).arg(end)
    }

    #[doc="Perform a bitwise operation between multiple keys (containing string values)
        and store the result in the destination key."]
    fn bitop<K: ToRedisArgs>(op: BitOp, dstkey: K, srckeys: K) {
        cmd("BITOP").arg(match op {
            BitAnd => "AND",
            BitOr => "OR",
            BitXor => "XOR",
            BitNot => "NOT"
        }).arg(dstkey).arg(srckeys)
    }

    #[doc="Get the length of the value stored in a key."]
    fn strlen<K: ToRedisArgs>(key: K) {
        cmd("STRLEN").arg(key)
    }
)

impl Commands for Connection {
    fn perform<T: FromRedisValue>(&self, cmd: &Cmd) -> RedisResult<T> {
        cmd.query(self)
    }
}

impl Commands for Client {
    fn perform<T: FromRedisValue>(&self, cmd: &Cmd) -> RedisResult<T> {
        cmd.query(&try!(self.get_connection()))
    }
}

impl Commands for RedisResult<Client> {
    fn perform<T: FromRedisValue>(&self, cmd: &Cmd) -> RedisResult<T> {
        match self {
            &Ok(ref client) => cmd.query(&try!(client.get_connection())),
            &Err(ref x) => Err(x.clone())
        }
    }
}

impl PipelineCommands for Pipeline {
    fn perform<'a>(&'a mut self, cmd: &Cmd) -> &'a mut Pipeline {
        self.add_command(cmd)
    }
}
<|endoftext|>


<commit_before>extern crate cursive;

use cursive::{Cursive, Printer};
use cursive::traits::*;
use cursive::event::{Event, EventResult};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(KeyCodeView::new(10).fixed_size((70, 10)));

    siv.run();
}

struct KeyCodeView {
    history: Vec<String>,
    size: usize,
}

impl KeyCodeView {
    fn new(size: usize) -> Self {
        KeyCodeView {
            history: Vec::new(),
            size: size,
        }
    }
}

impl View for KeyCodeView {
    fn draw(&self, printer: &Printer) {
        for (y, line) in self.history.iter().enumerate() {
            printer.print((0, y), line);
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        let line = format!("{:?}", event);
        self.history.push(line);

        while self.history.len() > self.size {
            self.history.remove(0);
        }

        EventResult::Consumed(None)
    }
}
<commit_msg>Use full-width window for key_codes example<commit_after>extern crate cursive;

use cursive::{Cursive, Printer};
use cursive::traits::*;
use cursive::event::{Event, EventResult};

fn main() {
    let mut siv = Cursive::new();

    siv.add_layer(KeyCodeView::new(10).full_width().fixed_height(10));

    siv.run();
}

struct KeyCodeView {
    history: Vec<String>,
    size: usize,
}

impl KeyCodeView {
    fn new(size: usize) -> Self {
        KeyCodeView {
            history: Vec::new(),
            size: size,
        }
    }
}

impl View for KeyCodeView {
    fn draw(&self, printer: &Printer) {
        for (y, line) in self.history.iter().enumerate() {
            printer.print((0, y), line);
        }
    }

    fn on_event(&mut self, event: Event) -> EventResult {
        let line = format!("{:?}", event);
        self.history.push(line);

        while self.history.len() > self.size {
            self.history.remove(0);
        }

        EventResult::Consumed(None)
    }
}
<|endoftext|>