<commit_before>use crate::publish_rate_limit::PublishRateLimit;
use crate::{env, env_optional, uploaders::Uploader, Env, Replica};

#[derive(Debug)]
pub struct Config {
    pub uploader: Uploader,
    pub session_key: String,
    pub gh_client_id: String,
    pub gh_client_secret: String,
    pub gh_base_url: String,
    pub db_primary_config: DbPoolConfig,
    pub db_replica_config: Option<DbPoolConfig>,
    pub env: Env,
    pub max_upload_size: u64,
    pub max_unpack_size: u64,
    pub mirror: Replica,
    pub api_protocol: String,
    pub publish_rate_limit: PublishRateLimit,
    pub blocked_traffic: Vec<(String, Vec<String>)>,
    pub max_allowed_page_offset: u32,
    pub page_offset_ua_blocklist: Vec<String>,
    pub domain_name: String,
    pub allowed_origins: Vec<String>,
    pub downloads_persist_interval_ms: usize,
    pub ownership_invitations_expiration_days: u64,
    pub metrics_authorization_token: Option<String>,
    pub use_test_database_pool: bool,
}

#[derive(Debug)]
pub struct DbPoolConfig {
    pub url: String,
    pub read_only_mode: bool,
}

impl Default for Config {
    /// Returns a default value for the application's config
    ///
    /// Sets the following default values:
    ///
    /// - `Config::max_upload_size`: 10MiB
    /// - `Config::api_protocol`: `https`
    /// - `Config::ownership_invitations_expiration_days`: 30
    ///
    /// Pulls values from the following environment variables:
    ///
    /// - `MIRROR`: Is this instance of cargo_registry a mirror of crates.io.
    /// - `HEROKU`: Is this instance of cargo_registry currently running on Heroku.
    /// - `S3_BUCKET`: The S3 bucket used to store crate files. If not present during development,
    ///    cargo_registry will fall back to a local uploader.
    /// - `S3_REGION`: The region in which the bucket was created. Optional if US standard.
    /// - `S3_ACCESS_KEY`: The access key to interact with S3. Optional if running a mirror.
    /// - `S3_SECRET_KEY`: The secret key to interact with S3. Optional if running a mirror.
    /// - `SESSION_KEY`: The key used to sign and encrypt session cookies.
    /// - `GH_CLIENT_ID`: The client ID of the associated GitHub application.
    /// - `GH_CLIENT_SECRET`: The client secret of the associated GitHub application.
    /// - `DATABASE_URL`: The URL of the postgres database to use.
    /// - `READ_ONLY_REPLICA_URL`: The URL of an optional postgres read-only replica database.
    /// - `BLOCKED_TRAFFIC`: A list of headers and environment variables to use for blocking
    ///   traffic. See the `block_traffic` module for more documentation.
    /// - `DOWNLOADS_PERSIST_INTERVAL_MS`: how frequent to persist download counts (in ms).
    /// - `METRICS_AUTHORIZATION_TOKEN`: authorization token needed to query metrics. If missing,
    ///   querying metrics will be completely disabled.
    /// - `DB_OFFLINE`: If set to `leader` then use the read-only follower as if it was the leader.
    ///   If set to `follower` then act as if `READ_ONLY_REPLICA_URL` was unset.
    /// - `READ_ONLY_MODE`: If defined (even as empty) then force all connections to be read-only.
    /// - `WEB_MAX_ALLOWED_PAGE_OFFSET`: Page offsets larger than this value are rejected. Defaults
    ///   to 200.
    /// - `WEB_PAGE_OFFSET_UA_BLOCKLIST`: A comma seperated list of user-agent substrings that will
    ///   be blocked if `WEB_MAX_ALLOWED_PAGE_OFFSET` is exceeded.
    ///
    /// # Panics
    ///
    /// This function panics if `DB_OFFLINE=leader` but `READ_ONLY_REPLICA_URL` is unset.
    fn default() -> Config {
        let api_protocol = String::from("https");
        let mirror = if dotenv::var("MIRROR").is_ok() {
            Replica::ReadOnlyMirror
        } else {
            Replica::Primary
        };
        let heroku = dotenv::var("HEROKU").is_ok();
        let cargo_env = if heroku {
            Env::Production
        } else {
            Env::Development
        };

        let leader_url = env("DATABASE_URL");
        let follower_url = dotenv::var("READ_ONLY_REPLICA_URL").ok();
        let read_only_mode = dotenv::var("READ_ONLY_MODE").is_ok();
        let (db_primary_config, db_replica_config) = match dotenv::var("DB_OFFLINE").as_deref() {
            // The actual leader is down, use the follower in read-only mode as the primary and
            // don't configure a replica.
            Ok("leader") => (
                DbPoolConfig {
                    url: follower_url
                        .expect("Must set `READ_ONLY_REPLICA_URL` when using `DB_OFFLINE=leader`."),
                    read_only_mode: true,
                },
                None,
            ),
            // The follower is down, don't configure the replica.
            Ok("follower") => (
                DbPoolConfig {
                    url: leader_url,
                    read_only_mode,
                },
                None,
            ),
            _ => (
                DbPoolConfig {
                    url: leader_url,
                    read_only_mode,
                },
                follower_url.map(|url| DbPoolConfig {
                    url,
                    // Always enable read-only mode for the follower. In staging, we attach the
                    // same leader database to both environment variables and this ensures the
                    // connection is opened read-only even when attached to a writeable database.
                    read_only_mode: true,
                }),
            ),
        };

        let uploader = match (cargo_env, mirror) {
            (Env::Production, Replica::Primary) => {
                // `env` panics if these vars are not set, and in production for a primary instance,
                // that's what we want since we don't want to be able to start the server if the
                // server doesn't know where to upload crates.
                Uploader::S3 {
                    bucket: s3::Bucket::new(
                        env("S3_BUCKET"),
                        dotenv::var("S3_REGION").ok(),
                        env("S3_ACCESS_KEY"),
                        env("S3_SECRET_KEY"),
                        &api_protocol,
                    ),
                    cdn: dotenv::var("S3_CDN").ok(),
                }
            }
            (Env::Production, Replica::ReadOnlyMirror) => {
                // Read-only mirrors don't need access key or secret key since by definition,
                // they'll only need to read from a bucket, not upload.
                //
                // Read-only mirrors might have access key or secret key, so use them if those
                // environment variables are set.
                //
                // Read-only mirrors definitely need bucket though, so that they know where
                // to serve crate files from.
                Uploader::S3 {
                    bucket: s3::Bucket::new(
                        env("S3_BUCKET"),
                        dotenv::var("S3_REGION").ok(),
                        dotenv::var("S3_ACCESS_KEY").unwrap_or_default(),
                        dotenv::var("S3_SECRET_KEY").unwrap_or_default(),
                        &api_protocol,
                    ),
                    cdn: dotenv::var("S3_CDN").ok(),
                }
            }
            // In Development mode, either running as a primary instance or a read-only mirror
            _ => {
                if dotenv::var("S3_BUCKET").is_ok() {
                    // If we've set the `S3_BUCKET` variable to any value, use all of the values
                    // for the related S3 environment variables and configure the app to upload to
                    // and read from S3 like production does. All values except for bucket are
                    // optional, like production read-only mirrors.
                    println!("Using S3 uploader");
                    Uploader::S3 {
                        bucket: s3::Bucket::new(
                            env("S3_BUCKET"),
                            dotenv::var("S3_REGION").ok(),
                            dotenv::var("S3_ACCESS_KEY").unwrap_or_default(),
                            dotenv::var("S3_SECRET_KEY").unwrap_or_default(),
                            &api_protocol,
                        ),
                        cdn: dotenv::var("S3_CDN").ok(),
                    }
                } else {
                    // If we don't set the `S3_BUCKET` variable, we'll use a development-only
                    // uploader that makes it possible to run and publish to a locally-running
                    // crates.io instance without needing to set up an account and a bucket in S3.
                    println!(
                        "Using local uploader, crate files will be in the local_uploads directory"
                    );
                    Uploader::Local
                }
            }
        };
        let allowed_origins = env("WEB_ALLOWED_ORIGINS")
            .split(',')
            .map(ToString::to_string)
            .collect();
        Config {
            uploader,
            session_key: env("SESSION_KEY"),
            gh_client_id: env("GH_CLIENT_ID"),
            gh_client_secret: env("GH_CLIENT_SECRET"),
            gh_base_url: "https://api.github.com".to_string(),
            db_primary_config,
            db_replica_config,
            env: cargo_env,
            max_upload_size: 10 * 1024 * 1024, // 10 MB default file upload size limit
            max_unpack_size: 512 * 1024 * 1024, // 512 MB max when decompressed
            mirror,
            api_protocol,
            publish_rate_limit: Default::default(),
            blocked_traffic: blocked_traffic(),
            max_allowed_page_offset: env_optional("WEB_MAX_ALLOWED_PAGE_OFFSET").unwrap_or(200),
            page_offset_ua_blocklist: env_optional::<String>("WEB_PAGE_OFFSET_UA_BLOCKLIST")
                .unwrap_or_default()
                .split(',')
                .map(String::from)
                .collect(),
            domain_name: domain_name(),
            allowed_origins,
            downloads_persist_interval_ms: dotenv::var("DOWNLOADS_PERSIST_INTERVAL_MS")
                .map(|interval| {
                    interval
                        .parse()
                        .expect("invalid DOWNLOADS_PERSIST_INTERVAL_MS")
                })
                .unwrap_or(60_000), // 1 minute
            ownership_invitations_expiration_days: 30,
            metrics_authorization_token: dotenv::var("METRICS_AUTHORIZATION_TOKEN").ok(),
            use_test_database_pool: false,
        }
    }
}

pub(crate) fn domain_name() -> String {
    dotenv::var("DOMAIN_NAME").unwrap_or_else(|_| "crates.io".into())
}

fn blocked_traffic() -> Vec<(String, Vec<String>)> {
    let pattern_list = dotenv::var("BLOCKED_TRAFFIC").unwrap_or_default();
    parse_traffic_patterns(&pattern_list)
        .map(|(header, value_env_var)| {
            let value_list = dotenv::var(value_env_var).unwrap_or_default();
            let values = value_list.split(',').map(String::from).collect();
            (header.into(), values)
        })
        .collect()
}

fn parse_traffic_patterns(patterns: &str) -> impl Iterator<Item = (&str, &str)> {
    patterns.split_terminator(',').map(|pattern| {
        if let Some(idx) = pattern.find('=') {
            (&pattern[..idx], &pattern[(idx + 1)..])
        } else {
            panic!(
                "BLOCKED_TRAFFIC must be in the form HEADER=VALUE_ENV_VAR, \
                 got invalid pattern {}",
                pattern
            )
        }
    })
}

#[test]
fn parse_traffic_patterns_splits_on_comma_and_looks_for_equal_sign() {
    let pattern_string_1 = "Foo=BAR,Bar=BAZ";
    let pattern_string_2 = "Baz=QUX";
    let pattern_string_3 = "";

    let patterns_1 = parse_traffic_patterns(pattern_string_1).collect::<Vec<_>>();
    assert_eq!(vec![("Foo", "BAR"), ("Bar", "BAZ")], patterns_1);

    let patterns_2 = parse_traffic_patterns(pattern_string_2).collect::<Vec<_>>();
    assert_eq!(vec![("Baz", "QUX")], patterns_2);

    assert_none!(parse_traffic_patterns(pattern_string_3).next());
}
<commit_msg>Auto merge of #3638 - jtgeibel:blocklist-rev-4, r=pietroalbini<commit_after>use crate::publish_rate_limit::PublishRateLimit;
use crate::{env, env_optional, uploaders::Uploader, Env, Replica};

#[derive(Debug)]
pub struct Config {
    pub uploader: Uploader,
    pub session_key: String,
    pub gh_client_id: String,
    pub gh_client_secret: String,
    pub gh_base_url: String,
    pub db_primary_config: DbPoolConfig,
    pub db_replica_config: Option<DbPoolConfig>,
    pub env: Env,
    pub max_upload_size: u64,
    pub max_unpack_size: u64,
    pub mirror: Replica,
    pub api_protocol: String,
    pub publish_rate_limit: PublishRateLimit,
    pub blocked_traffic: Vec<(String, Vec<String>)>,
    pub max_allowed_page_offset: u32,
    pub page_offset_ua_blocklist: Vec<String>,
    pub domain_name: String,
    pub allowed_origins: Vec<String>,
    pub downloads_persist_interval_ms: usize,
    pub ownership_invitations_expiration_days: u64,
    pub metrics_authorization_token: Option<String>,
    pub use_test_database_pool: bool,
}

#[derive(Debug)]
pub struct DbPoolConfig {
    pub url: String,
    pub read_only_mode: bool,
}

impl Default for Config {
    /// Returns a default value for the application's config
    ///
    /// Sets the following default values:
    ///
    /// - `Config::max_upload_size`: 10MiB
    /// - `Config::api_protocol`: `https`
    /// - `Config::ownership_invitations_expiration_days`: 30
    ///
    /// Pulls values from the following environment variables:
    ///
    /// - `MIRROR`: Is this instance of cargo_registry a mirror of crates.io.
    /// - `HEROKU`: Is this instance of cargo_registry currently running on Heroku.
    /// - `S3_BUCKET`: The S3 bucket used to store crate files. If not present during development,
    ///    cargo_registry will fall back to a local uploader.
    /// - `S3_REGION`: The region in which the bucket was created. Optional if US standard.
    /// - `S3_ACCESS_KEY`: The access key to interact with S3. Optional if running a mirror.
    /// - `S3_SECRET_KEY`: The secret key to interact with S3. Optional if running a mirror.
    /// - `SESSION_KEY`: The key used to sign and encrypt session cookies.
    /// - `GH_CLIENT_ID`: The client ID of the associated GitHub application.
    /// - `GH_CLIENT_SECRET`: The client secret of the associated GitHub application.
    /// - `DATABASE_URL`: The URL of the postgres database to use.
    /// - `READ_ONLY_REPLICA_URL`: The URL of an optional postgres read-only replica database.
    /// - `BLOCKED_TRAFFIC`: A list of headers and environment variables to use for blocking
    ///   traffic. See the `block_traffic` module for more documentation.
    /// - `DOWNLOADS_PERSIST_INTERVAL_MS`: how frequent to persist download counts (in ms).
    /// - `METRICS_AUTHORIZATION_TOKEN`: authorization token needed to query metrics. If missing,
    ///   querying metrics will be completely disabled.
    /// - `DB_OFFLINE`: If set to `leader` then use the read-only follower as if it was the leader.
    ///   If set to `follower` then act as if `READ_ONLY_REPLICA_URL` was unset.
    /// - `READ_ONLY_MODE`: If defined (even as empty) then force all connections to be read-only.
    /// - `WEB_MAX_ALLOWED_PAGE_OFFSET`: Page offsets larger than this value are rejected. Defaults
    ///   to 200.
    /// - `WEB_PAGE_OFFSET_UA_BLOCKLIST`: A comma seperated list of user-agent substrings that will
    ///   be blocked if `WEB_MAX_ALLOWED_PAGE_OFFSET` is exceeded. Including an empty string in the
    ///   list will block *all* user-agents exceeding the offset. If not set or empty, no blocking
    ///   will occur.
    ///
    /// # Panics
    ///
    /// This function panics if `DB_OFFLINE=leader` but `READ_ONLY_REPLICA_URL` is unset.
    fn default() -> Config {
        let api_protocol = String::from("https");
        let mirror = if dotenv::var("MIRROR").is_ok() {
            Replica::ReadOnlyMirror
        } else {
            Replica::Primary
        };
        let heroku = dotenv::var("HEROKU").is_ok();
        let cargo_env = if heroku {
            Env::Production
        } else {
            Env::Development
        };

        let leader_url = env("DATABASE_URL");
        let follower_url = dotenv::var("READ_ONLY_REPLICA_URL").ok();
        let read_only_mode = dotenv::var("READ_ONLY_MODE").is_ok();
        let (db_primary_config, db_replica_config) = match dotenv::var("DB_OFFLINE").as_deref() {
            // The actual leader is down, use the follower in read-only mode as the primary and
            // don't configure a replica.
            Ok("leader") => (
                DbPoolConfig {
                    url: follower_url
                        .expect("Must set `READ_ONLY_REPLICA_URL` when using `DB_OFFLINE=leader`."),
                    read_only_mode: true,
                },
                None,
            ),
            // The follower is down, don't configure the replica.
            Ok("follower") => (
                DbPoolConfig {
                    url: leader_url,
                    read_only_mode,
                },
                None,
            ),
            _ => (
                DbPoolConfig {
                    url: leader_url,
                    read_only_mode,
                },
                follower_url.map(|url| DbPoolConfig {
                    url,
                    // Always enable read-only mode for the follower. In staging, we attach the
                    // same leader database to both environment variables and this ensures the
                    // connection is opened read-only even when attached to a writeable database.
                    read_only_mode: true,
                }),
            ),
        };

        let uploader = match (cargo_env, mirror) {
            (Env::Production, Replica::Primary) => {
                // `env` panics if these vars are not set, and in production for a primary instance,
                // that's what we want since we don't want to be able to start the server if the
                // server doesn't know where to upload crates.
                Uploader::S3 {
                    bucket: s3::Bucket::new(
                        env("S3_BUCKET"),
                        dotenv::var("S3_REGION").ok(),
                        env("S3_ACCESS_KEY"),
                        env("S3_SECRET_KEY"),
                        &api_protocol,
                    ),
                    cdn: dotenv::var("S3_CDN").ok(),
                }
            }
            (Env::Production, Replica::ReadOnlyMirror) => {
                // Read-only mirrors don't need access key or secret key since by definition,
                // they'll only need to read from a bucket, not upload.
                //
                // Read-only mirrors might have access key or secret key, so use them if those
                // environment variables are set.
                //
                // Read-only mirrors definitely need bucket though, so that they know where
                // to serve crate files from.
                Uploader::S3 {
                    bucket: s3::Bucket::new(
                        env("S3_BUCKET"),
                        dotenv::var("S3_REGION").ok(),
                        dotenv::var("S3_ACCESS_KEY").unwrap_or_default(),
                        dotenv::var("S3_SECRET_KEY").unwrap_or_default(),
                        &api_protocol,
                    ),
                    cdn: dotenv::var("S3_CDN").ok(),
                }
            }
            // In Development mode, either running as a primary instance or a read-only mirror
            _ => {
                if dotenv::var("S3_BUCKET").is_ok() {
                    // If we've set the `S3_BUCKET` variable to any value, use all of the values
                    // for the related S3 environment variables and configure the app to upload to
                    // and read from S3 like production does. All values except for bucket are
                    // optional, like production read-only mirrors.
                    println!("Using S3 uploader");
                    Uploader::S3 {
                        bucket: s3::Bucket::new(
                            env("S3_BUCKET"),
                            dotenv::var("S3_REGION").ok(),
                            dotenv::var("S3_ACCESS_KEY").unwrap_or_default(),
                            dotenv::var("S3_SECRET_KEY").unwrap_or_default(),
                            &api_protocol,
                        ),
                        cdn: dotenv::var("S3_CDN").ok(),
                    }
                } else {
                    // If we don't set the `S3_BUCKET` variable, we'll use a development-only
                    // uploader that makes it possible to run and publish to a locally-running
                    // crates.io instance without needing to set up an account and a bucket in S3.
                    println!(
                        "Using local uploader, crate files will be in the local_uploads directory"
                    );
                    Uploader::Local
                }
            }
        };
        let allowed_origins = env("WEB_ALLOWED_ORIGINS")
            .split(',')
            .map(ToString::to_string)
            .collect();
        let page_offset_ua_blocklist = match env_optional::<String>("WEB_PAGE_OFFSET_UA_BLOCKLIST")
        {
            None => vec![],
            Some(s) if s.is_empty() => vec![],
            Some(s) => s.split(',').map(String::from).collect(),
        };
        Config {
            uploader,
            session_key: env("SESSION_KEY"),
            gh_client_id: env("GH_CLIENT_ID"),
            gh_client_secret: env("GH_CLIENT_SECRET"),
            gh_base_url: "https://api.github.com".to_string(),
            db_primary_config,
            db_replica_config,
            env: cargo_env,
            max_upload_size: 10 * 1024 * 1024, // 10 MB default file upload size limit
            max_unpack_size: 512 * 1024 * 1024, // 512 MB max when decompressed
            mirror,
            api_protocol,
            publish_rate_limit: Default::default(),
            blocked_traffic: blocked_traffic(),
            max_allowed_page_offset: env_optional("WEB_MAX_ALLOWED_PAGE_OFFSET").unwrap_or(200),
            page_offset_ua_blocklist,
            domain_name: domain_name(),
            allowed_origins,
            downloads_persist_interval_ms: dotenv::var("DOWNLOADS_PERSIST_INTERVAL_MS")
                .map(|interval| {
                    interval
                        .parse()
                        .expect("invalid DOWNLOADS_PERSIST_INTERVAL_MS")
                })
                .unwrap_or(60_000), // 1 minute
            ownership_invitations_expiration_days: 30,
            metrics_authorization_token: dotenv::var("METRICS_AUTHORIZATION_TOKEN").ok(),
            use_test_database_pool: false,
        }
    }
}

pub(crate) fn domain_name() -> String {
    dotenv::var("DOMAIN_NAME").unwrap_or_else(|_| "crates.io".into())
}

fn blocked_traffic() -> Vec<(String, Vec<String>)> {
    let pattern_list = dotenv::var("BLOCKED_TRAFFIC").unwrap_or_default();
    parse_traffic_patterns(&pattern_list)
        .map(|(header, value_env_var)| {
            let value_list = dotenv::var(value_env_var).unwrap_or_default();
            let values = value_list.split(',').map(String::from).collect();
            (header.into(), values)
        })
        .collect()
}

fn parse_traffic_patterns(patterns: &str) -> impl Iterator<Item = (&str, &str)> {
    patterns.split_terminator(',').map(|pattern| {
        if let Some(idx) = pattern.find('=') {
            (&pattern[..idx], &pattern[(idx + 1)..])
        } else {
            panic!(
                "BLOCKED_TRAFFIC must be in the form HEADER=VALUE_ENV_VAR, \
                 got invalid pattern {}",
                pattern
            )
        }
    })
}

#[test]
fn parse_traffic_patterns_splits_on_comma_and_looks_for_equal_sign() {
    let pattern_string_1 = "Foo=BAR,Bar=BAZ";
    let pattern_string_2 = "Baz=QUX";
    let pattern_string_3 = "";

    let patterns_1 = parse_traffic_patterns(pattern_string_1).collect::<Vec<_>>();
    assert_eq!(vec![("Foo", "BAR"), ("Bar", "BAZ")], patterns_1);

    let patterns_2 = parse_traffic_patterns(pattern_string_2).collect::<Vec<_>>();
    assert_eq!(vec![("Baz", "QUX")], patterns_2);

    assert_none!(parse_traffic_patterns(pattern_string_3).next());
}
<|endoftext|>


<commit_before>// Copyright © 2015-2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
//! Mappings for the contents of dxgitype.h
use shared::d3d9types::D3DCOLORVALUE;
use shared::dxgiformat::DXGI_FORMAT;
use shared::minwindef::{BOOL, DWORD, UINT};
pub const DXGI_CPU_ACCESS_NONE: DWORD = 0;
pub const DXGI_CPU_ACCESS_DYNAMIC: DWORD = 1;
pub const DXGI_CPU_ACCESS_READ_WRITE: DWORD = 2;
pub const DXGI_CPU_ACCESS_SCRATCH: DWORD = 3;
pub const DXGI_CPU_ACCESS_FIELD: DWORD = 15;
ENUM!{enum DXGI_USAGE {
    DXGI_USAGE_SHADER_INPUT = 1 << (0 + 4),
    DXGI_USAGE_RENDER_TARGET_OUTPUT = 1 << (1 + 4),
    DXGI_USAGE_BACK_BUFFER = 1 << (2 + 4),
    DXGI_USAGE_SHARED = 1 << (3 + 4),
    DXGI_USAGE_READ_ONLY = 1 << (4 + 4),
    DXGI_USAGE_DISCARD_ON_PRESENT = 1 << (5 + 4),
    DXGI_USAGE_UNORDERED_ACCESS = 1 << (6 + 4),
}}
STRUCT!{struct DXGI_RGB {
    Red: f32,
    Green: f32,
    Blue: f32,
}}
pub type DXGI_RGBA = D3DCOLORVALUE;
STRUCT!{struct DXGI_GAMMA_CONTROL {
    Scale: DXGI_RGB,
    Offset: DXGI_RGB,
    GammaCurve: [DXGI_RGB; 1025],
}}
STRUCT!{struct DXGI_GAMMA_CONTROL_CAPABILITIES {
    ScaleAndOffsetSupported: BOOL,
    MaxConvertedValue: f32,
    MinConvertedValue: f32,
    NumGammaControlPoints: UINT,
    ControlPointPositions: [f32; 1025],
}}
STRUCT!{struct DXGI_RATIONAL {
    Numerator: UINT,
    Denominator: UINT,
}}
ENUM!{enum DXGI_MODE_SCANLINE_ORDER {
    DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
    DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE,
    DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST,
    DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST,
}}
ENUM!{enum DXGI_MODE_SCALING {
    DXGI_MODE_SCALING_UNSPECIFIED,
    DXGI_MODE_SCALING_CENTERED,
    DXGI_MODE_SCALING_STRETCHED,
}}
ENUM!{enum DXGI_MODE_ROTATION {
    DXGI_MODE_ROTATION_UNSPECIFIED,
    DXGI_MODE_ROTATION_IDENTITY,
    DXGI_MODE_ROTATION_ROTATE90,
    DXGI_MODE_ROTATION_ROTATE180,
    DXGI_MODE_ROTATION_ROTATE270,
}}
STRUCT!{struct DXGI_MODE_DESC {
    Width: UINT,
    Height: UINT,
    RefreshRate: DXGI_RATIONAL,
    Format: DXGI_FORMAT,
    ScanlineOrdering: DXGI_MODE_SCANLINE_ORDER,
    Scaling: DXGI_MODE_SCALING,
}}
STRUCT!{struct DXGI_SAMPLE_DESC {
    Count: UINT,
    Quality: UINT,
}}
ENUM!{enum DXGI_COLOR_SPACE_TYPE {
    DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0x0,
    DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 0x1,
    DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 0x2,
    DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 0x3,
    DXGI_COLOR_SPACE_RESERVED = 0x4,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 0x5,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 0x6,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 0x7,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 0x8,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 0x9,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 0xA,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 0xB,
    DXGI_COLOR_SPACE_CUSTOM = 0xFFFFFFFF,
}}
pub const DXGI_CENTER_MULTISAMPLE_QUALITY_PATTERN: UINT = 0xfffffffe;
pub const DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN: UINT = 0xffffffff;
<commit_msg>update shared::dxgitype (#401)<commit_after>// Copyright © 2015-2017 winapi-rs developers
// Licensed under the Apache License, Version 2.0
// <LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your option.
// All files in the project carrying such notice may not be copied, modified, or distributed
// except according to those terms.
//! Mappings for the contents of dxgitype.h
use shared::d3d9types::D3DCOLORVALUE;
use shared::dxgiformat::DXGI_FORMAT;
use shared::minwindef::{UINT, BOOL, DWORD, BYTE};

pub const DXGI_CPU_ACCESS_NONE: DWORD = 0;
pub const DXGI_CPU_ACCESS_DYNAMIC: DWORD = 1;
pub const DXGI_CPU_ACCESS_READ_WRITE: DWORD = 2;
pub const DXGI_CPU_ACCESS_SCRATCH: DWORD = 3;
pub const DXGI_CPU_ACCESS_FIELD: DWORD = 15;
ENUM!{enum DXGI_USAGE {
    DXGI_USAGE_SHADER_INPUT = 1 << (0 + 4),
    DXGI_USAGE_RENDER_TARGET_OUTPUT = 1 << (1 + 4),
    DXGI_USAGE_BACK_BUFFER = 1 << (2 + 4),
    DXGI_USAGE_SHARED = 1 << (3 + 4),
    DXGI_USAGE_READ_ONLY = 1 << (4 + 4),
    DXGI_USAGE_DISCARD_ON_PRESENT = 1 << (5 + 4),
    DXGI_USAGE_UNORDERED_ACCESS = 1 << (6 + 4),
}}
STRUCT!{struct DXGI_RGB {
    Red: f32,
    Green: f32,
    Blue: f32,
}}
pub type DXGI_RGBA = D3DCOLORVALUE;
STRUCT!{struct DXGI_GAMMA_CONTROL {
    Scale: DXGI_RGB,
    Offset: DXGI_RGB,
    GammaCurve: [DXGI_RGB; 1025],
}}
STRUCT!{struct DXGI_GAMMA_CONTROL_CAPABILITIES {
    ScaleAndOffsetSupported: BOOL,
    MaxConvertedValue: f32,
    MinConvertedValue: f32,
    NumGammaControlPoints: UINT,
    ControlPointPositions: [f32; 1025],
}}
STRUCT!{struct DXGI_RATIONAL {
    Numerator: UINT,
    Denominator: UINT,
}}
ENUM!{enum DXGI_MODE_SCANLINE_ORDER {
    DXGI_MODE_SCANLINE_ORDER_UNSPECIFIED,
    DXGI_MODE_SCANLINE_ORDER_PROGRESSIVE,
    DXGI_MODE_SCANLINE_ORDER_UPPER_FIELD_FIRST,
    DXGI_MODE_SCANLINE_ORDER_LOWER_FIELD_FIRST,
}}
ENUM!{enum DXGI_MODE_SCALING {
    DXGI_MODE_SCALING_UNSPECIFIED,
    DXGI_MODE_SCALING_CENTERED,
    DXGI_MODE_SCALING_STRETCHED,
}}
ENUM!{enum DXGI_MODE_ROTATION {
    DXGI_MODE_ROTATION_UNSPECIFIED,
    DXGI_MODE_ROTATION_IDENTITY,
    DXGI_MODE_ROTATION_ROTATE90,
    DXGI_MODE_ROTATION_ROTATE180,
    DXGI_MODE_ROTATION_ROTATE270,
}}
STRUCT!{struct DXGI_MODE_DESC {
    Width: UINT,
    Height: UINT,
    RefreshRate: DXGI_RATIONAL,
    Format: DXGI_FORMAT,
    ScanlineOrdering: DXGI_MODE_SCANLINE_ORDER,
    Scaling: DXGI_MODE_SCALING,
}}
STRUCT!{struct DXGI_SAMPLE_DESC {
    Count: UINT,
    Quality: UINT,
}}
STRUCT!{struct DXGI_JPEG_DC_HUFFMAN_TABLE {
    CodeCounts: [BYTE; 12],
    CodeValues: [BYTE; 12],
}}
STRUCT!{struct DXGI_JPEG_AC_HUFFMAN_TABLE {
    CodeCounts: [BYTE; 16],
    CodeValues: [BYTE; 162],
}}
STRUCT!{struct DXGI_JPEG_QUANTIZATION_TABLE {
    Elements: [BYTE; 64],
}}
ENUM!{enum DXGI_COLOR_SPACE_TYPE {
    DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P709 = 0,
    DXGI_COLOR_SPACE_RGB_FULL_G10_NONE_P709 = 1,
    DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P709 = 2,
    DXGI_COLOR_SPACE_RGB_STUDIO_G22_NONE_P2020 = 3,
    DXGI_COLOR_SPACE_RESERVED = 4,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_NONE_P709_X601 = 5,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P601 = 6,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P601 = 7,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P709 = 8,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P709 = 9,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_LEFT_P2020 = 10,
    DXGI_COLOR_SPACE_YCBCR_FULL_G22_LEFT_P2020 = 11,
    DXGI_COLOR_SPACE_RGB_FULL_G2084_NONE_P2020 = 12,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_LEFT_P2020 = 13,
    DXGI_COLOR_SPACE_RGB_STUDIO_G2084_NONE_P2020 = 14,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G22_TOPLEFT_P2020 = 15,
    DXGI_COLOR_SPACE_YCBCR_STUDIO_G2084_TOPLEFT_P2020 = 16,
    DXGI_COLOR_SPACE_RGB_FULL_G22_NONE_P2020 = 17,
    DXGI_COLOR_SPACE_CUSTOM = 0xFFFFFFFF,
}}
pub const DXGI_CENTER_MULTISAMPLE_QUALITY_PATTERN: UINT = 0xfffffffe;
pub const DXGI_STANDARD_MULTISAMPLE_QUALITY_PATTERN: UINT = 0xffffffff;
<|endoftext|>


<commit_before>#[test]
fn empty_regex_empty_match() {
    let re = regex!("");
    assert_eq!(vec![(0, 0)], findall!(re, ""));
}

#[test]
fn empty_regex_nonempty_match() {
    let re = regex!("");
    assert_eq!(vec![(0, 0), (1, 1), (2, 2), (3, 3)], findall!(re, "abc"));
}

#[test]
fn one_zero_length_match() {
    let re = regex!(r"\d*");
    assert_eq!(vec![(0, 0), (1, 2), (3, 4)], findall!(re, "a1b2"));
}

#[test]
fn many_zero_length_match() {
    let re = regex!(r"\d*");
    assert_eq!(vec![(0, 0), (1, 2), (3, 3), (4, 4), (5, 6)],
               findall!(re, "a1bbb2"));
}

#[test]
fn many_sequential_zero_length_match() {
    let re = regex!(r"\d?");
    assert_eq!(vec![(0, 0), (1, 2), (2, 3), (4, 5), (6, 6)],
               findall!(re, "a12b3c"));
}

#[test]
fn quoted_bracket_set() {
    let re = regex!(r"([\x{5b}\x{5d}])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
    let re = regex!(r"([\[\]])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
}

#[test]
fn first_range_starts_with_left_bracket() {
    let re = regex!(r"([[-z])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
}

#[test]
fn range_ends_with_escape() {
    let re = regex!(r"([\[-\x{5d}])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
}

#[test]
fn empty_match_find_iter() {
    let re = regex!(r".*?");
    assert_eq!(vec![(0, 0), (1, 1), (2, 2), (3, 3)], findall!(re, "abc"));
}

#[test]
fn empty_match_captures_iter() {
    let re = regex!(r".*?");
    let ms: Vec<_> = re.captures_iter(text!("abc"))
                       .map(|c| c.pos(0).unwrap())
                       .collect();
    assert_eq!(ms, vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
}

#[test]
fn capture_names() {
    let re = regex!(r"(.)(?P.)");
    assert_eq!(3, re.captures_len());
    assert_eq!((3, Some(3)), re.capture_names().size_hint());
    assert_eq!(vec![None, None, Some("a")],
               re.capture_names().collect::<Vec<_>>());
}

#[test]
fn regex_string() {
    assert_eq!(r"[a-zA-Z0-9]+", regex!(r"[a-zA-Z0-9]+").as_str());
    assert_eq!(r"[a-zA-Z0-9]+", &format!("{}", regex!(r"[a-zA-Z0-9]+")));
    assert_eq!(r"[a-zA-Z0-9]+", &format!("{:?}", regex!(r"[a-zA-Z0-9]+")));
}

#[test]
fn capture_index() {
    let re = regex!(r"^(?P<name>.+)$");
    let cap = re.captures(t!("abc")).unwrap();
    assert_eq!(&cap[0], t!("abc"));
    assert_eq!(&cap[1], t!("abc"));
    assert_eq!(&cap["name"], t!("abc"));
}

#[test]
#[should_panic]
#[cfg_attr(all(target_env = "msvc", target_pointer_width = "32"), ignore)]
fn capture_index_panic_usize() {
    let re = regex!(r"^(?P<name>.+)$");
    let cap = re.captures(t!("abc")).unwrap();
    let _ = cap[2];
}

#[test]
#[should_panic]
#[cfg_attr(all(target_env = "msvc", target_pointer_width = "32"), ignore)]
fn capture_index_panic_name() {
    let re = regex!(r"^(?P<name>.+)$");
    let cap = re.captures(t!("abc")).unwrap();
    let _ = cap["bad name"];
}

#[test]
fn capture_index_lifetime() {
    // This is a test of whether the types on `caps["..."]` are general
    // enough. If not, this will fail to typecheck.
    fn inner(s: &str) -> usize {
        let re = regex!(r"(?P<number>\d+)");
        let caps = re.captures(t!(s)).unwrap();
        caps["number"].len()
    }
    assert_eq!(3, inner("123"));
}

#[test]
fn capture_misc() {
    let re = regex!(r"(.)(?Pa)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();

    assert_eq!(5, cap.len());

    assert_eq!(Some((0, 3)), cap.pos(0));
    assert_eq!(None, cap.pos(2));
    assert_eq!(Some((2, 3)), cap.pos(4));

    assert_eq!(Some(t!("abc")), cap.at(0));
    assert_eq!(None, cap.at(2));
    assert_eq!(Some(t!("c")), cap.at(4));

    assert_eq!(None, cap.name("a"));
    assert_eq!(Some(t!("c")), cap.name("b"));
}

#[test]
fn capture_iter() {
    let re = regex!(r"(.)(?P.)(.)(?P.)");
    let cap = re.captures(t!("abcd")).unwrap();
    assert_eq!(5, cap.len());

    let expected = vec![
        t!("abcd"), t!("a"), t!("b"), t!("c"), t!("d"),
    ].into_iter().map(Some).collect::<Vec<_>>();
    let got = cap.iter().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_missing() {
    let re = regex!(r"(.)(?Pa)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();
    assert_eq!(5, cap.len());

    let expected = vec![
        Some(t!("abc")), Some(t!("a")), None, Some(t!("b")), Some(t!("c")),
    ];
    let got = cap.iter().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_pos() {
    let re = regex!(r"(.)(?P.)(.)(?P.)");
    let cap = re.captures(t!("abcd")).unwrap();

    let expected = vec![
        (0, 4), (0, 1), (1, 2), (2, 3), (3, 4),
    ].into_iter().map(Some).collect::<Vec<_>>();
    let got = cap.iter_pos().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_pos_missing() {
    let re = regex!(r"(.)(?Pa)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();

    let expected = vec![
        Some((0, 3)), Some((0, 1)), None, Some((1, 2)), Some((2, 3)),
    ];
    let got = cap.iter_pos().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_named() {
    let re = regex!(r"(.)(?P.)(.)(?P.)");
    let cap = re.captures(t!("abcd")).unwrap();

    let expected1 = vec![
        ("a", Some(t!("b"))), ("b", Some(t!("d"))),
    ];
    let expected2 = vec![
        ("b", Some(t!("d"))), ("a", Some(t!("b"))),
    ];
    let got = cap.iter_named().collect::<Vec<_>>();
    assert!(got == expected1 || got == expected2);
}

#[test]
fn capture_iter_named_missing() {
    let re = regex!(r"(.)(?P.)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();

    let expected1 = vec![
        ("a", None), ("b", Some(t!("c"))),
    ];
    let expected2 = vec![
        ("b", Some(t!("c"))), ("a", None),
    ];
    let got = cap.iter_named().collect::<Vec<_>>();
    assert!(got == expected1 || got == expected2);
}

expand!(expand1, r"(?P<foo>\w+)", "abc", "$foo", "abc");
expand!(expand2, r"(?P<foo>\w+)", "abc", "$0", "abc");
expand!(expand3, r"(?P<foo>\w+)", "abc", "$1", "abc");
expand!(expand4, r"(?P<foo>\w+)", "abc", "$$1", "$1");
expand!(expand5, r"(?P<foo>\w+)", "abc", "$$foo", "$foo");
expand!(expand6, r"(?P\w+)\s+(?P\d+)",
        "abc 123", "$b$a", "123abc");
expand!(expand7, r"(?P\w+)\s+(?P\d+)",
        "abc 123", "z$bz$az", "z");
expand!(expand8, r"(?P\w+)\s+(?P\d+)",
        "abc 123", ".$b.$a.", ".123.abc.");
expand!(expand9, r"(?P\w+)\s+(?P\d+)",
        "abc 123", " $b $a ", " 123 abc ");
expand!(expand10, r"(?P\w+)\s+(?P\d+)",
        "abc 123", "$bz$az", "");

split!(split1, r"\s+", "a b\nc\td\n\t e",
       &[t!("a"), t!("b"), t!("c"), t!("d"), t!("e")]);
split!(split2, r"\b", "a b c",
       &[t!(""), t!("a"), t!(" "), t!("b"), t!(" "), t!("c")]);
<commit_msg>Add a regression test.<commit_after>#[test]
fn empty_regex_empty_match() {
    let re = regex!("");
    assert_eq!(vec![(0, 0)], findall!(re, ""));
}

#[test]
fn empty_regex_nonempty_match() {
    let re = regex!("");
    assert_eq!(vec![(0, 0), (1, 1), (2, 2), (3, 3)], findall!(re, "abc"));
}

#[test]
fn one_zero_length_match() {
    let re = regex!(r"\d*");
    assert_eq!(vec![(0, 0), (1, 2), (3, 4)], findall!(re, "a1b2"));
}

#[test]
fn many_zero_length_match() {
    let re = regex!(r"\d*");
    assert_eq!(vec![(0, 0), (1, 2), (3, 3), (4, 4), (5, 6)],
               findall!(re, "a1bbb2"));
}

#[test]
fn many_sequential_zero_length_match() {
    let re = regex!(r"\d?");
    assert_eq!(vec![(0, 0), (1, 2), (2, 3), (4, 5), (6, 6)],
               findall!(re, "a12b3c"));
}

#[test]
fn quoted_bracket_set() {
    let re = regex!(r"([\x{5b}\x{5d}])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
    let re = regex!(r"([\[\]])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
}

#[test]
fn first_range_starts_with_left_bracket() {
    let re = regex!(r"([[-z])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
}

#[test]
fn range_ends_with_escape() {
    let re = regex!(r"([\[-\x{5d}])");
    assert_eq!(vec![(0, 1), (1, 2)], findall!(re, "[]"));
}

#[test]
fn empty_match_find_iter() {
    let re = regex!(r".*?");
    assert_eq!(vec![(0, 0), (1, 1), (2, 2), (3, 3)], findall!(re, "abc"));
}

#[test]
fn empty_match_captures_iter() {
    let re = regex!(r".*?");
    let ms: Vec<_> = re.captures_iter(text!("abc"))
                       .map(|c| c.pos(0).unwrap())
                       .collect();
    assert_eq!(ms, vec![(0, 0), (1, 1), (2, 2), (3, 3)]);
}

#[test]
fn capture_names() {
    let re = regex!(r"(.)(?P.)");
    assert_eq!(3, re.captures_len());
    assert_eq!((3, Some(3)), re.capture_names().size_hint());
    assert_eq!(vec![None, None, Some("a")],
               re.capture_names().collect::<Vec<_>>());
}

#[test]
fn regex_string() {
    assert_eq!(r"[a-zA-Z0-9]+", regex!(r"[a-zA-Z0-9]+").as_str());
    assert_eq!(r"[a-zA-Z0-9]+", &format!("{}", regex!(r"[a-zA-Z0-9]+")));
    assert_eq!(r"[a-zA-Z0-9]+", &format!("{:?}", regex!(r"[a-zA-Z0-9]+")));
}

#[test]
fn capture_index() {
    let re = regex!(r"^(?P<name>.+)$");
    let cap = re.captures(t!("abc")).unwrap();
    assert_eq!(&cap[0], t!("abc"));
    assert_eq!(&cap[1], t!("abc"));
    assert_eq!(&cap["name"], t!("abc"));
}

#[test]
#[should_panic]
#[cfg_attr(all(target_env = "msvc", target_pointer_width = "32"), ignore)]
fn capture_index_panic_usize() {
    let re = regex!(r"^(?P<name>.+)$");
    let cap = re.captures(t!("abc")).unwrap();
    let _ = cap[2];
}

#[test]
#[should_panic]
#[cfg_attr(all(target_env = "msvc", target_pointer_width = "32"), ignore)]
fn capture_index_panic_name() {
    let re = regex!(r"^(?P<name>.+)$");
    let cap = re.captures(t!("abc")).unwrap();
    let _ = cap["bad name"];
}

#[test]
fn capture_index_lifetime() {
    // This is a test of whether the types on `caps["..."]` are general
    // enough. If not, this will fail to typecheck.
    fn inner(s: &str) -> usize {
        let re = regex!(r"(?P<number>\d+)");
        let caps = re.captures(t!(s)).unwrap();
        caps["number"].len()
    }
    assert_eq!(3, inner("123"));
}

#[test]
fn capture_misc() {
    let re = regex!(r"(.)(?Pa)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();

    assert_eq!(5, cap.len());

    assert_eq!(Some((0, 3)), cap.pos(0));
    assert_eq!(None, cap.pos(2));
    assert_eq!(Some((2, 3)), cap.pos(4));

    assert_eq!(Some(t!("abc")), cap.at(0));
    assert_eq!(None, cap.at(2));
    assert_eq!(Some(t!("c")), cap.at(4));

    assert_eq!(None, cap.name("a"));
    assert_eq!(Some(t!("c")), cap.name("b"));
}

#[test]
fn capture_iter() {
    let re = regex!(r"(.)(?P.)(.)(?P.)");
    let cap = re.captures(t!("abcd")).unwrap();
    assert_eq!(5, cap.len());

    let expected = vec![
        t!("abcd"), t!("a"), t!("b"), t!("c"), t!("d"),
    ].into_iter().map(Some).collect::<Vec<_>>();
    let got = cap.iter().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_missing() {
    let re = regex!(r"(.)(?Pa)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();
    assert_eq!(5, cap.len());

    let expected = vec![
        Some(t!("abc")), Some(t!("a")), None, Some(t!("b")), Some(t!("c")),
    ];
    let got = cap.iter().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_pos() {
    let re = regex!(r"(.)(?P.)(.)(?P.)");
    let cap = re.captures(t!("abcd")).unwrap();

    let expected = vec![
        (0, 4), (0, 1), (1, 2), (2, 3), (3, 4),
    ].into_iter().map(Some).collect::<Vec<_>>();
    let got = cap.iter_pos().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_pos_missing() {
    let re = regex!(r"(.)(?Pa)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();

    let expected = vec![
        Some((0, 3)), Some((0, 1)), None, Some((1, 2)), Some((2, 3)),
    ];
    let got = cap.iter_pos().collect::<Vec<_>>();
    assert_eq!(expected, got);
}

#[test]
fn capture_iter_named() {
    let re = regex!(r"(.)(?P.)(.)(?P.)");
    let cap = re.captures(t!("abcd")).unwrap();

    let expected1 = vec![
        ("a", Some(t!("b"))), ("b", Some(t!("d"))),
    ];
    let expected2 = vec![
        ("b", Some(t!("d"))), ("a", Some(t!("b"))),
    ];
    let got = cap.iter_named().collect::<Vec<_>>();
    assert!(got == expected1 || got == expected2);
}

#[test]
fn capture_iter_named_missing() {
    let re = regex!(r"(.)(?P.)?(.)(?P.)");
    let cap = re.captures(t!("abc")).unwrap();

    let expected1 = vec![
        ("a", None), ("b", Some(t!("c"))),
    ];
    let expected2 = vec![
        ("b", Some(t!("c"))), ("a", None),
    ];
    let got = cap.iter_named().collect::<Vec<_>>();
    assert!(got == expected1 || got == expected2);
}

expand!(expand1, r"(?P<foo>\w+)", "abc", "$foo", "abc");
expand!(expand2, r"(?P<foo>\w+)", "abc", "$0", "abc");
expand!(expand3, r"(?P<foo>\w+)", "abc", "$1", "abc");
expand!(expand4, r"(?P<foo>\w+)", "abc", "$$1", "$1");
expand!(expand5, r"(?P<foo>\w+)", "abc", "$$foo", "$foo");
expand!(expand6, r"(?P\w+)\s+(?P\d+)",
        "abc 123", "$b$a", "123abc");
expand!(expand7, r"(?P\w+)\s+(?P\d+)",
        "abc 123", "z$bz$az", "z");
expand!(expand8, r"(?P\w+)\s+(?P\d+)",
        "abc 123", ".$b.$a.", ".123.abc.");
expand!(expand9, r"(?P\w+)\s+(?P\d+)",
        "abc 123", " $b $a ", " 123 abc ");
expand!(expand10, r"(?P\w+)\s+(?P\d+)",
        "abc 123", "$bz$az", "");

split!(split1, r"\s+", "a b\nc\td\n\t e",
       &[t!("a"), t!("b"), t!("c"), t!("d"), t!("e")]);
split!(split2, r"\b", "a b c",
       &[t!(""), t!("a"), t!(" "), t!("b"), t!(" "), t!("c")]);
split!(split3, r"a$", "a", &[t!("")]);
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

use std::collections::HashSet;
use std::ffi::OsStr;
use std::process::{Command, ExitStatus, Output};

static EFF_WORDLIST: &'static str = include_str!(concat!(env!("OUT_DIR"), "/eff_wordlist.txt"));

struct Result {
    stdout: String,
    stderr: String,
    status: ExitStatus,
}

impl From<Output> for Result {
    fn from(output: Output) -> Result {
        Result {
            status: output.status,
            stdout: String::from_utf8(output.stdout).expect("Failed to decode stdout"),
            stderr: String::from_utf8(output.stderr).expect("Failed to decode stderr"),
        }
    }
}

fn run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    Command::new("cargo")
        .arg("run")
        .arg("--quiet")
        .arg("--")
        .args(args)
        .output()
        .expect("Failed to run xkpwgen for testing")
        .into()
}

fn assert_run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    let result = run(args);
    assert!(result.status.success(),
            "xkpwgen failed with status {} and output:
stdout:
{}
stderr:
{}
",
            result.status,
            result.stdout,
            result.stderr);
    result
}

fn all_words<'a>(s: &'a str, sep: &str) -> Vec<&'a str> {
    s.lines().flat_map(|w| w.split(sep)).collect()
}

macro_rules! repeat_run {
    ($result:ident, $command:expr, $body:block) => {
        {
            for _ in 0..10 {
                let $result = assert_run($command);
                $body;
            }
        }
    };

    ($result:ident, $body:block) => {
        {
        let command: Vec<String> = Vec::new();
        repeat_run!($result, &command, $body);
        }
    };
}

fn assert_license_and_warranty(s: &str) {
    assert!(s.contains("GNU GPL version 3 or later"),
            "License name missing in output:
{}",
            s);
    assert!(s.contains("<http://gnu.org/licenses/gpl.html>"),
            "License URL missing in output:
{}",
            s);
    assert!(s.contains("There is NO WARRANTY, to the extent permitted by law."),
            "Warranty disclaimer missing in output:
{}",
            s);
}

#[test]
fn it_includes_license_and_warranty_in_help() {
    assert_license_and_warranty(&assert_run(&["--help"]).stdout);
}

#[test]
fn it_includes_license_and_warranty_in_version() {
    assert_license_and_warranty(&assert_run(&["--version"]).stdout);
}

#[test]
fn it_uses_only_words_from_the_wordlist() {
    let words = EFF_WORDLIST.lines().collect::<HashSet<_>>();
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(words.contains(word), "Word {} not in EFF wordlist!", word);
        }
    });
}

#[test]
fn it_generates_five_phrases_by_default() {
    repeat_run!(result, {
        assert_eq!(result.stdout.lines().count(), 5);
    })
}

#[test]
fn it_generates_the_given_number_of_passwords() {
    repeat_run!(result, &["-n", "10"], {
        assert_eq!(result.stdout.lines().count(), 10);
    })
}

#[test]
fn it_generates_four_words_per_password_by_default() {
    repeat_run!(result, {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 4);
        }
    })
}

#[test]
fn it_generates_the_given_number_of_words_per_password() {
    repeat_run!(result, &["-l", "9"], {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 9)
        }
    });
}

#[test]
fn it_generates_no_words_with_whitespace() {
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(!word.contains(|c: char| c.is_whitespace()),
                    "Word {} contained whitespace!",
                    word);
        }
    })
}

#[test]
fn it_generates_no_empty_words() {
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(word.len() > 0, "Got empty word");
        }
    })
}

#[test]
fn it_generates_different_passwords() {
    repeat_run!(result, &["-n", "100"], {
        let mut seen_passwords = HashSet::with_capacity(100);
        let mut duplicate_passwords = HashSet::new();
        for password in result.stdout.lines() {
            if !seen_passwords.insert(password) {
                duplicate_passwords.insert(password);
            }
        }

        assert!(duplicate_passwords.is_empty(),
                "Duplicate passwords found: {}",
                duplicate_passwords.into_iter().collect::<Vec<_>>().join(", "));

    });
}

#[test]
fn it_uses_the_original_eff_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    assert_eq!(stdout.lines().collect::<Vec<_>>(),
               EFF_WORDLIST.lines().collect::<Vec<_>>());
}
<commit_msg>Cleanup CLI tests<commit_after>/*
 * Copyright (C) 2017  Sebastian Wiesner <swiesner@lunaryorn.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

extern crate xkpwgen;

use std::collections::HashSet;
use std::ffi::OsStr;
use std::process::{Command, ExitStatus, Output};
use xkpwgen::wordlist;

struct Result {
    stdout: String,
    stderr: String,
    status: ExitStatus,
}

impl From<Output> for Result {
    fn from(output: Output) -> Result {
        Result {
            status: output.status,
            stdout: String::from_utf8(output.stdout).expect("Failed to decode stdout"),
            stderr: String::from_utf8(output.stderr).expect("Failed to decode stderr"),
        }
    }
}

fn run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    Command::new("cargo")
        .arg("run")
        .arg("--quiet")
        .arg("--")
        .args(args)
        .output()
        .expect("Failed to run xkpwgen for testing")
        .into()
}

fn assert_run<S: AsRef<OsStr>>(args: &[S]) -> Result {
    let result = run(args);
    assert!(result.status.success(),
            "xkpwgen failed with status {} and output:
stdout:
{}
stderr:
{}
",
            result.status,
            result.stdout,
            result.stderr);
    result
}

fn all_words<'a>(s: &'a str, sep: &str) -> Vec<&'a str> {
    s.lines().flat_map(|w| w.split(sep)).collect()
}

macro_rules! repeat_run {
    ($result:ident, $command:expr, $body:block) => {
        {
            for _ in 0..10 {
                let $result = assert_run($command);
                $body;
            }
        }
    };

    ($result:ident, $body:block) => {
        {
        let command: Vec<String> = Vec::new();
        repeat_run!($result, &command, $body);
        }
    };
}

fn assert_license_and_warranty(s: &str) {
    assert!(s.contains("GNU GPL version 3 or later"),
            "License name missing in output:
{}",
            s);
    assert!(s.contains("<http://gnu.org/licenses/gpl.html>"),
            "License URL missing in output:
{}",
            s);
    assert!(s.contains("There is NO WARRANTY, to the extent permitted by law."),
            "Warranty disclaimer missing in output:
{}",
            s);
}

#[test]
fn it_includes_license_and_warranty_in_help() {
    assert_license_and_warranty(&assert_run(&["--help"]).stdout);
}

#[test]
fn it_includes_license_and_warranty_in_version() {
    assert_license_and_warranty(&assert_run(&["--version"]).stdout);
}

#[test]
fn it_uses_only_words_from_the_wordlist() {
    let words = wordlist::builtin_words().into_iter().collect::<HashSet<_>>();
    repeat_run!(result, {
        for word in all_words(&result.stdout, " ") {
            assert!(words.contains(word), "Word {} not in EFF wordlist!", word);
        }
    });
}

#[test]
fn it_generates_five_phrases_by_default() {
    repeat_run!(result, {
        assert_eq!(result.stdout.lines().count(), 5);
    })
}

#[test]
fn it_generates_the_given_number_of_passwords() {
    repeat_run!(result, &["-n", "10"], {
        assert_eq!(result.stdout.lines().count(), 10);
    })
}

#[test]
fn it_generates_four_words_per_password_by_default() {
    repeat_run!(result, {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 4);
        }
    })
}

#[test]
fn it_generates_the_given_number_of_words_per_password() {
    repeat_run!(result, &["-l", "9"], {
        for password in result.stdout.lines() {
            assert_eq!(all_words(password, " ").len(), 9)
        }
    });
}

#[test]
fn it_generates_different_passwords() {
    repeat_run!(result, &["-n", "100"], {
        let mut seen_passwords = HashSet::with_capacity(100);
        let mut duplicate_passwords = HashSet::new();
        for password in result.stdout.lines() {
            if !seen_passwords.insert(password) {
                duplicate_passwords.insert(password);
            }
        }

        assert!(duplicate_passwords.is_empty(),
                "Duplicate passwords found: {}",
                duplicate_passwords.into_iter().collect::<Vec<_>>().join(", "));

    });
}

#[test]
fn it_uses_the_original_eff_wordlist() {
    let stdout = assert_run(&["--words"]).stdout;
    assert_eq!(stdout.lines().collect::<Vec<_>>(),
               wordlist::builtin_words());
}
<|endoftext|>


<commit_before>#![feature(globs,macro_rules,phase)]

#[phase(plugin,link)]
extern crate nom;

use nom::{IResult,Producer,FileProducer,ProducerState,FlatMapper,Mapper,Mapper2,line_ending,not_line_ending, space, alphanumeric, is_alphanumeric};
use nom::IResult::*;

use std::str;
use std::fmt::Show;

fn empty_result(i:&[u8]) -> IResult<&[u8], ()> { Done(i,()) }
tag!(semicolon ";".as_bytes());
o!(comment_body<&[u8],&[u8]> semicolon ~ not_line_ending ~ );
o!(comment<&[u8], ()> comment_body line_ending ~ empty_result ~);
opt!(opt_comment<&[u8],&[u8]> comment_body);

tag!(lsb "[".as_bytes());
tag!(rsb "]".as_bytes());
fn category_name(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == ']' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}
o!(category<&[u8], &str> lsb ~ category_name ~ rsb line_ending);

tag!(equal "=".as_bytes());
fn not_equal(input:&[u8]) -> IResult<&[u8], &[u8]> {
  for idx in range(0, input.len()) {
    if input[idx] == '=' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx))
    }
  }
  Done("".as_bytes(), input)
}

fn value_parser(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == '\n' as u8 || input[idx] == ';' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}

fn parameter_parser(input: &[u8]) -> IResult<&[u8], &str> {
  alphanumeric(input).map_res(str::from_utf8)
}

opt!(opt_line_ending<&[u8],&[u8]> line_ending);
o!(value<&[u8],&str> space equal space ~ value_parser ~ space opt_comment opt_line_ending);
chain!(key_value<&[u8],(&str,&str)>, ||{(key, val)},  key: parameter_parser, val: value,);

many0!(keys_and_values<&[u8], (&str, &str) > key_value);

#[test]
fn parse_comment_test() {
  let ini_file = ";comment
[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let ini_without_comment = "[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let res = Done((), ini_file.as_bytes()).flat_map(comment);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_comment.as_bytes(), ()));
}

#[test]
fn parse_category_test() {
  let ini_file = "[category]
parameter=value
key = value2";

  let ini_without_category = "parameter=value
key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(category);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category.as_bytes(), "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = "parameter=value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = "parameter = value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = "parameter=value;abc
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = "parameter=value;abc
key = value2

[category]";

  let ini_without_key_value = "
[category]";

  let res = Done((), ini_file.as_bytes()).flat_map(keys_and_values);
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), vec![("parameter", "value"), ("key", "value2")]));
}
<commit_msg>produce a hashmap from key-value components<commit_after>#![feature(globs,macro_rules,phase)]

#[phase(plugin,link)]
extern crate nom;

use nom::{IResult,Producer,FileProducer,ProducerState,FlatMapper,Mapper,Mapper2,line_ending,not_line_ending, space, alphanumeric, is_alphanumeric};
use nom::IResult::*;

use std::str;
use std::collections::HashMap;
use std::fmt::Show;

fn empty_result(i:&[u8]) -> IResult<&[u8], ()> { Done(i,()) }
tag!(semicolon ";".as_bytes());
o!(comment_body<&[u8],&[u8]> semicolon ~ not_line_ending ~ );
o!(comment<&[u8], ()> comment_body line_ending ~ empty_result ~);
opt!(opt_comment<&[u8],&[u8]> comment_body);

tag!(lsb "[".as_bytes());
tag!(rsb "]".as_bytes());
fn category_name(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == ']' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}
o!(category<&[u8], &str> lsb ~ category_name ~ rsb line_ending);

tag!(equal "=".as_bytes());
fn not_equal(input:&[u8]) -> IResult<&[u8], &[u8]> {
  for idx in range(0, input.len()) {
    if input[idx] == '=' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx))
    }
  }
  Done("".as_bytes(), input)
}

fn value_parser(input:&[u8]) -> IResult<&[u8], &str> {
  for idx in range(0, input.len()) {
    if input[idx] == '\n' as u8 || input[idx] == ';' as u8 {
      return Done(input.slice_from(idx), input.slice(0, idx)).map_res(str::from_utf8)
    }
  }
  Done("".as_bytes(), input).map_res(str::from_utf8)
}

fn parameter_parser(input: &[u8]) -> IResult<&[u8], &str> {
  alphanumeric(input).map_res(str::from_utf8)
}

opt!(opt_line_ending<&[u8],&[u8]> line_ending);
o!(value<&[u8],&str> space equal space ~ value_parser ~ space opt_comment opt_line_ending);
chain!(key_value<&[u8],(&str,&str)>, ||{(key, val)},  key: parameter_parser, val: value,);

fn keys_and_values<'a>(input: &'a[u8], mut z: HashMap<&'a str, &'a str>) -> IResult<&'a[u8], HashMap<&'a str, &'a str> > {
  fold0_impl!(<&[u8], HashMap<&str, &str> >, |mut h:HashMap<&'a str, &'a str>, (k, v)| {
    h.insert(k,v);
    h
  }, key_value, input, z);

}

#[test]
fn parse_comment_test() {
  let ini_file = ";comment
[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let ini_without_comment = "[category]
parameter=value
key = value2

[other]
number = 1234
str = a b cc dd ; comment";

  let res = Done((), ini_file.as_bytes()).flat_map(comment);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_comment.as_bytes(), ()));
}

#[test]
fn parse_category_test() {
  let ini_file = "[category]
parameter=value
key = value2";

  let ini_without_category = "parameter=value
key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(category);
  println!("{}", res);
  match res {
    IResult::Done(i, o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_category.as_bytes(), "category"));
}

#[test]
fn parse_key_value_test() {
  let ini_file = "parameter=value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_space_test() {
  let ini_file = "parameter = value
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_key_value_with_comment_test() {
  let ini_file = "parameter=value;abc
key = value2";

  let ini_without_key_value = "key = value2";

  let res = Done((), ini_file.as_bytes()).flat_map(key_value);
  println!("{}", res);
  match res {
    IResult::Done(i, (o1, o2)) => println!("i: {} | o: ({},{})", str::from_utf8(i), o1, o2),
    _ => println!("error")
  }

  assert_eq!(res, Done(ini_without_key_value.as_bytes(), ("parameter", "value")));
}

#[test]
fn parse_multiple_keys_and_values_test() {
  let ini_file = "parameter=value;abc
key = value2

[category]";

  let ini_without_key_value = "
[category]";

  let mut h: HashMap<&str, &str> = HashMap::new();
  let res = keys_and_values(ini_file.as_bytes(), h);
  println!("{}", res);
  match res {
    IResult::Done(i, ref o) => println!("i: {} | o: {}", str::from_utf8(i), o),
    _ => println!("error")
  }

  let mut expected: HashMap<&str, &str> = HashMap::new();
  expected.insert("parameter", "value");
  expected.insert("key", "value2");
  assert_eq!(res, Done(ini_without_key_value.as_bytes(), expected));
}
<|endoftext|>


<commit_before>extern crate mtl;
extern crate cocoa;

use mtl::{CompileOptions, Device, LanguageVersion, PreprocessorMacroValue, SpecificLanguageVersion};
use mtl::LibraryError;
use mtl::{FromRaw, FromRawError, IntoRaw};
use mtl::sys::{MTLCompileOptions, MTLLanguageVersion};
use cocoa::base::{BOOL, nil};
use cocoa::foundation::NSString;

#[test]
fn get_device() {
    let device = Device::system_default_device();
    assert!(device.is_ok());
}

#[test]
fn get_all_devices() {
    let all_devices = Device::enumerate_all_system_devices();
    assert!(!all_devices.is_empty());
    for device in all_devices {
        assert!(device.is_ok());
    }
}

#[test]
fn get_device_name() {
    let device = Device::system_default_device().unwrap();
    assert!(!device.name().into_owned().is_empty());
}

#[test]
fn set_get_command_queue_label() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();

    let dummy_command_queue_name = "foo";
    command_queue.set_label(dummy_command_queue_name);

    assert_eq!(command_queue.get_label().into_owned(),
               dummy_command_queue_name.to_string());
}

#[test]
fn insert_debug_capture_boundary_on_command_queue() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();
    command_queue.insert_debug_capture_boundary();

    // if nothing panics/fails here, everything is okay
}

#[test]
fn device_from_to_raw() {
    let device = Device::system_default_device().unwrap();
    let raw = device.into_raw();
    let device = Device::from_raw(raw);
    assert!(device.is_ok());
}

#[test]
fn device_from_wrong_type() {
    let some_string = unsafe { NSString::alloc(nil).init_str("Hello") };
    let device_result = Device::from_raw(some_string);
    match device_result {
        Result::Err(FromRawError::WrongPointerType) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn device_from_nullptr() {
    let device_result = Device::from_raw(nil);
    match device_result {
        Result::Err(FromRawError::NilPointer) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn test_compile_opts_creation_is_correct() {
    let mut options = CompileOptions::default();

    let fast_math_enabled = true;
    options.fast_math_enabled = Some(fast_math_enabled);
    options.language_version = LanguageVersion::Specific(SpecificLanguageVersion::Version_1_0);
    options.preprocessor_macros.insert("Foo".into(), PreprocessorMacroValue::Integral(54));
    options.preprocessor_macros.insert("Bar".into(), PreprocessorMacroValue::Floating(32.0));
    options.preprocessor_macros.insert("Baz".into(), PreprocessorMacroValue::String("Hi".into()));

    let native_options = options.mtl_compile_options();

    unsafe {
        assert_eq!(fast_math_enabled as BOOL,
                   native_options.fastMathEnabled());
        assert_eq!(native_options.languageVersion(),
                   MTLLanguageVersion::MTLLanguageVersion1_0);
    }
}

#[test]
fn create_invalid_shader() {
    let mut device = Device::system_default_device().unwrap();
    let bad_shader = r"abcdefghijklmnopqrstuvwxyz";
    match device.new_library_with_source(bad_shader, &Default::default()) {
        Ok(_) => panic!("Incorrect result: expected an error"),
        Err(LibraryError::SourceError) => assert!(true),
        _ => panic!("Incorrect result: unexpected error"),
    }
}

#[test]
fn device_create_library_with_valid_shader_code_and_get_fn_names() {
    let mut device = Device::system_default_device().unwrap();
    // Shader source taken from http://metalbyexample.com/up-and-running-2/
    const SHADER: &'static str = r"
        using namespace metal;

        struct ColoredVertex
        {
            float4 position [[position]];
            float4 color;
        };

        vertex ColoredVertex vertex_main(constant float4 *position [[buffer(0)]],
                                         constant float4 *color [[buffer(1)]],
                                         uint vid [[vertex_id]])
        {
            ColoredVertex vert;
            vert.position = position[vid];
            vert.color = color[vid];
            return vert;
        }

        fragment float4 fragment_main(ColoredVertex vert [[stage_in]])
        {
            return vert.color;
        }
    ";
    let library = device.new_library_with_source(SHADER, &Default::default()).ok().unwrap();
    let names = library.function_names();
    assert_eq!(names[0], "vertex_main");
    assert_eq!(names[1], "fragment_main");
}
<commit_msg>Make a static str const<commit_after>extern crate mtl;
extern crate cocoa;

use mtl::{CompileOptions, Device, LanguageVersion, PreprocessorMacroValue, SpecificLanguageVersion};
use mtl::LibraryError;
use mtl::{FromRaw, FromRawError, IntoRaw};
use mtl::sys::{MTLCompileOptions, MTLLanguageVersion};
use cocoa::base::{BOOL, nil};
use cocoa::foundation::NSString;

#[test]
fn get_device() {
    let device = Device::system_default_device();
    assert!(device.is_ok());
}

#[test]
fn get_all_devices() {
    let all_devices = Device::enumerate_all_system_devices();
    assert!(!all_devices.is_empty());
    for device in all_devices {
        assert!(device.is_ok());
    }
}

#[test]
fn get_device_name() {
    let device = Device::system_default_device().unwrap();
    assert!(!device.name().into_owned().is_empty());
}

#[test]
fn set_get_command_queue_label() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();

    let dummy_command_queue_name = "foo";
    command_queue.set_label(dummy_command_queue_name);

    assert_eq!(command_queue.get_label().into_owned(),
               dummy_command_queue_name.to_string());
}

#[test]
fn insert_debug_capture_boundary_on_command_queue() {
    let mut device = Device::system_default_device().unwrap();
    let mut command_queue = device.new_command_queue().unwrap();
    command_queue.insert_debug_capture_boundary();

    // if nothing panics/fails here, everything is okay
}

#[test]
fn device_from_to_raw() {
    let device = Device::system_default_device().unwrap();
    let raw = device.into_raw();
    let device = Device::from_raw(raw);
    assert!(device.is_ok());
}

#[test]
fn device_from_wrong_type() {
    let some_string = unsafe { NSString::alloc(nil).init_str("Hello") };
    let device_result = Device::from_raw(some_string);
    match device_result {
        Result::Err(FromRawError::WrongPointerType) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn device_from_nullptr() {
    let device_result = Device::from_raw(nil);
    match device_result {
        Result::Err(FromRawError::NilPointer) => {
            // pass
        }
        _ => assert!(false),
    }
}

#[test]
fn test_compile_opts_creation_is_correct() {
    let mut options = CompileOptions::default();

    let fast_math_enabled = true;
    options.fast_math_enabled = Some(fast_math_enabled);
    options.language_version = LanguageVersion::Specific(SpecificLanguageVersion::Version_1_0);
    options.preprocessor_macros.insert("Foo".into(), PreprocessorMacroValue::Integral(54));
    options.preprocessor_macros.insert("Bar".into(), PreprocessorMacroValue::Floating(32.0));
    options.preprocessor_macros.insert("Baz".into(), PreprocessorMacroValue::String("Hi".into()));

    let native_options = options.mtl_compile_options();

    unsafe {
        assert_eq!(fast_math_enabled as BOOL,
                   native_options.fastMathEnabled());
        assert_eq!(native_options.languageVersion(),
                   MTLLanguageVersion::MTLLanguageVersion1_0);
    }
}

#[test]
fn create_invalid_shader() {
    let mut device = Device::system_default_device().unwrap();
    const BAD_SHADER: &'static str = r"abcdefghijklmnopqrstuvwxyz";
    match device.new_library_with_source(BAD_SHADER, &Default::default()) {
        Ok(_) => panic!("Incorrect result: expected an error"),
        Err(LibraryError::SourceError) => assert!(true),
        _ => panic!("Incorrect result: unexpected error"),
    }
}

#[test]
fn device_create_library_with_valid_shader_code_and_get_fn_names() {
    let mut device = Device::system_default_device().unwrap();
    // Shader source taken from http://metalbyexample.com/up-and-running-2/
    const SHADER: &'static str = r"
        using namespace metal;

        struct ColoredVertex
        {
            float4 position [[position]];
            float4 color;
        };

        vertex ColoredVertex vertex_main(constant float4 *position [[buffer(0)]],
                                         constant float4 *color [[buffer(1)]],
                                         uint vid [[vertex_id]])
        {
            ColoredVertex vert;
            vert.position = position[vid];
            vert.color = color[vid];
            return vert;
        }

        fragment float4 fragment_main(ColoredVertex vert [[stage_in]])
        {
            return vert.color;
        }
    ";
    let library = device.new_library_with_source(SHADER, &Default::default()).ok().unwrap();
    let names = library.function_names();
    assert_eq!(names[0], "vertex_main");
    assert_eq!(names[1], "fragment_main");
}
<|endoftext|>


<commit_before>#[macro_use(owned_tree, shared_tree)]
extern crate entmut;

// This will define macros for generalized tests of Nav and Editor impls.
#[cfg(test)]
#[macro_use]
mod view_tests;

#[cfg(test)]
#[macro_use]
mod owned {
    view_tests!(owned_tree);
}

#[cfg(shared)]
#[macro_use]
mod owned {
    view_tests!(shared_tree);
}
<commit_msg>Invoke correct macro for testing shared::Tree.<commit_after>#[macro_use(owned_tree, shared_tree)]
extern crate entmut;

// This will define macros for generalized tests of Nav and Editor impls.
#[cfg(test)]
#[macro_use]
mod view_tests;

#[cfg(test)]
#[macro_use]
mod owned {
    view_tests!(owned_tree);
}

#[cfg(test)]
#[macro_use]
mod shared {
    view_tests!(shared_tree);
}
<|endoftext|>


<commit_before>extern crate presort;

use presort::PermutedVec;
use std::rc::Rc;
use std::cell::RefCell;

struct TreeNode<T> {
	//whether this node's data has changed
	dirty_val: bool,
	//whether some descendant is dirty
	dirty_descendant: bool,
	//number of children with unchanged decendent counts
	stable_children: usize,
	//parent and index in parent's child vec
	parent: Option<(Tree<T>, usize)>,
	//this node's data
    data: T,
    decendent_count: i32,
    children: Vec<Tree<T>>,
}

type Tree<T> = Rc<RefCell<TreeNode<T>>>;

trait IncTree<T: PartialEq + Clone> {
    fn new_node(data: T) -> Tree<T>;
	fn dirty_decendents(&self);
	fn dirty_parents(&self);
	fn mark_recount(&self, child_index: usize, new_nodes: i32);
	fn get_data(&self) -> T;
	fn set_data(&self, data: T);
	fn get_child(&self, child_num: usize) -> Tree<T>;
	fn set_child(&self, child_num: usize, child: Tree<T>);
	fn push_child(&self, child: Tree<T>);
	fn pop_child(&self) -> Option<Tree<T>>;
}

impl<T: PartialEq + Clone> IncTree<T> for Tree<T> {
	fn new_node(data: T) -> Tree<T>{
		Rc::new(RefCell::new(TreeNode {
			dirty_val: true,
			dirty_descendant: false,
			stable_children: 0,
			parent: None,
			data: data,
			decendent_count: 0,
			children: vec![]
		}))
	}

	//sets dirty flags, but leaves counts
	fn dirty_decendents(&self) {
		let mut tree = self.borrow_mut();
		tree.dirty_val = true;
		tree.dirty_descendant = true;
		for kid in &tree.children {
			kid.dirty_decendents();
		}
	}

	fn dirty_parents(&self){
		let mut parent = None;
		if let Some((ref p_tree,_)) = self.borrow().parent {
			parent = Some(p_tree.clone()); // Rc clone
		}
		if let Some(parent) = parent {
			parent.borrow_mut().dirty_descendant = true;
			parent.dirty_parents();
		}
	}

	//when a child changes number of decendents, we mark it in
	//this tree and bubble it up. This updates both
	//`decendent_count` and `stable_children`
	fn mark_recount(&self, child_index: usize, new_nodes: i32) {
		let mut tree = self.borrow_mut();
		tree.decendent_count += new_nodes;
		if tree.stable_children > child_index {
			tree.stable_children = child_index;
		}
		if let Some((ref parent, index)) = tree.parent {
			parent.mark_recount(index, new_nodes)
		}
	}

	fn get_data(&self) -> T {
		self.borrow().data.clone()
	}

	fn set_data(&self, data: T) {
		{
			let mut tree = self.borrow_mut();
			if tree.data == data {return}
			tree.data = data;
			tree.dirty_val = true;
		}
		self.dirty_parents();
	}

	fn get_child(&self, child_num: usize) -> Tree<T> {
		self.borrow().children[child_num].clone() //Rc clone
	}

	fn set_child(&self, child_num: usize, child: Tree<T>) {
		let mut tree = self.borrow_mut();
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), child_num)); //Rc clone
		child.dirty_parents();
		let mut nodes: i32 = 0;
		{
			//handle a change in the number of decendents
			let ref old_child = tree.children[child_num].borrow();
			if old_child.decendent_count != child.borrow().decendent_count {
				nodes -= old_child.decendent_count;
				nodes += child.borrow().decendent_count;
				child.borrow_mut().stable_children = 0;
				self.mark_recount(child_num, nodes);
			}
		}
		tree.children[child_num] = child;
	}

	fn push_child(&self, child: Tree<T>) {
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), self.borrow().children.len())); //Rc clone
		child.dirty_parents();
		let index;
		let nodes;
		{
			let mut tree = self.borrow_mut();
			nodes = 1 + child.borrow().decendent_count;
			index = tree.children.len();
			tree.children.push(child);
		}
		self.mark_recount(index, nodes);
	}

	fn pop_child(&self) -> Option<Tree<T>> {
		//assume all new values are dirty
		let child = self.borrow_mut().children.pop();
		let nodes = match child {
			None => 0,
			Some(ref c) => 1 + c.borrow().decendent_count,
		};
		let index = std::cmp::max(self.borrow().children.len(),0);
		self.mark_recount(index, -nodes);
		child
	}
}

fn dump<T: Clone>(tree: &Tree<T>, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
    vec.push(tree.data.clone());
    tree.dirty_val = false;
    for kid in &tree.children { dump(&kid, vec); }
    tree.dirty_descendant = false;
    tree.stable_children = tree.children.len();
}

fn update<T: Clone>(tree: &Tree<T>, start_index: usize, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
	let mut index = start_index;
	if tree.dirty_val {
		if vec.len() <= index {
			vec.push(tree.data.clone())
		} else {
			vec.set(index, tree.data.clone());
		}
		tree.dirty_val = false;
	}
	if !tree.dirty_descendant {return}
	index += 1;

	//all the children with unchanged decendent counts
	for counted_kid in 0..tree.stable_children{
		update(&tree.children[counted_kid], index, vec);
		index += 1 + tree.children[counted_kid].borrow_mut().decendent_count as usize;
	}
	if tree.stable_children < tree.children.len() {
		{
			//first changed child may have unchanged decendents
			let first_changed = &tree.children[tree.stable_children];
			update(&first_changed, index, vec);
			index += 1 + first_changed.borrow().decendent_count as usize;
		}
		//dump any additional children
		if (tree.stable_children + 1) < tree.children.len() {
			vec.truncate(index);
			for uncounted_kid in (tree.stable_children + 1)..tree.children.len() {
				dump(&tree.children[uncounted_kid], vec);
			}
			tree.stable_children = tree.children.len();
		}
	}
}

#[test]
fn test_tree() {
    let main_tree = Tree::new_node(37);
    let mut vec = PermutedVec::new();
    dump(&main_tree, &mut vec);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&37]);
}

#[test]
fn test_update() {
	println!("start first dump");
	let main_tree = Tree::new_node(37);
	main_tree.push_child(Tree::new_node(42));
	main_tree.push_child(Tree::new_node(20));
	main_tree.push_child(Tree::new_node(63));
	let mut vec = PermutedVec::new();
	dump(&main_tree, &mut vec);
	println!("finished first dump");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&20,&37,&42,&63]);

	println!("start first update");
    main_tree.get_child(1).set_data(25);
    update(&main_tree, 0, &mut vec);
	println!("finished first update");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&25,&37,&42,&63]);

	println!("start add branch");
    let branch = main_tree.get_child(2);
    branch.push_child(Tree::new_node(47));
    branch.push_child(Tree::new_node(53));
    branch.push_child(Tree::new_node(61));
    branch.push_child(Tree::new_node(57));
    update(&main_tree, 0, &mut vec);
	println!("finished add branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&61,&63]
    );

	println!("start update branch");
    branch.get_child(2).set_data(77);
    update(&main_tree, 0, &mut vec);
	println!("finished update branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start early insertion");
    let branch = main_tree.get_child(1);
    branch.push_child(Tree::new_node(1));
    update(&main_tree, 0, &mut vec);
    println!("finished early insertion");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start deep add");
    let branch = main_tree.get_child(2).get_child(2);
    let new_branch = Tree::new_node(100);
    new_branch.push_child(Tree::new_node(101));
    branch.push_child(new_branch);
    update(&main_tree, 0, &mut vec);
    println!("finished deep add");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&101]
    );

    println!("start deep edit");
    let branch = branch.get_child(0).get_child(0);
    branch.set_data(105);
    update(&main_tree, 0, &mut vec);
    println!("finished deep edit");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&105]
    );

    println!("start pop branch");
    let branch = main_tree.get_child(2).get_child(2);
    branch.pop_child();
    update(&main_tree, 0, &mut vec);
    println!("finished pop branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77]
    );

}
<commit_msg>Renamed dirty_parents to dirty_ancestors.<commit_after>extern crate presort;

use presort::PermutedVec;
use std::rc::Rc;
use std::cell::RefCell;

struct TreeNode<T> {
	//whether this node's data has changed
	dirty_val: bool,
	//whether some descendant is dirty
	dirty_descendant: bool,
	//number of children with unchanged decendent counts
	stable_children: usize,
	//parent and index in parent's child vec
	parent: Option<(Tree<T>, usize)>,
	//this node's data
    data: T,
    decendent_count: i32,
    children: Vec<Tree<T>>,
}

type Tree<T> = Rc<RefCell<TreeNode<T>>>;

trait IncTree<T: PartialEq + Clone> {
    fn new_node(data: T) -> Tree<T>;
	fn dirty_decendents(&self);
	fn dirty_ancestors(&self);
	fn mark_recount(&self, child_index: usize, new_nodes: i32);
	fn get_data(&self) -> T;
	fn set_data(&self, data: T);
	fn get_child(&self, child_num: usize) -> Tree<T>;
	fn set_child(&self, child_num: usize, child: Tree<T>);
	fn push_child(&self, child: Tree<T>);
	fn pop_child(&self) -> Option<Tree<T>>;
}

impl<T: PartialEq + Clone> IncTree<T> for Tree<T> {
	fn new_node(data: T) -> Tree<T>{
		Rc::new(RefCell::new(TreeNode {
			dirty_val: true,
			dirty_descendant: false,
			stable_children: 0,
			parent: None,
			data: data,
			decendent_count: 0,
			children: vec![]
		}))
	}

	//sets dirty flags, but leaves counts
	fn dirty_decendents(&self) {
		let mut tree = self.borrow_mut();
		tree.dirty_val = true;
		tree.dirty_descendant = true;
		for kid in &tree.children {
			kid.dirty_decendents();
		}
	}

	fn dirty_ancestors(&self){
		let mut parent = None;
		if let Some((ref p_tree,_)) = self.borrow().parent {
			parent = Some(p_tree.clone()); // Rc clone
		}
		if let Some(parent) = parent {
			parent.borrow_mut().dirty_descendant = true;
			parent.dirty_ancestors();
		}
	}

	//when a child changes number of decendents, we mark it in
	//this tree and bubble it up. This updates both
	//`decendent_count` and `stable_children`
	fn mark_recount(&self, child_index: usize, new_nodes: i32) {
		let mut tree = self.borrow_mut();
		tree.decendent_count += new_nodes;
		if tree.stable_children > child_index {
			tree.stable_children = child_index;
		}
		if let Some((ref parent, index)) = tree.parent {
			parent.mark_recount(index, new_nodes)
		}
	}

	fn get_data(&self) -> T {
		self.borrow().data.clone()
	}

	fn set_data(&self, data: T) {
		{
			let mut tree = self.borrow_mut();
			if tree.data == data {return}
			tree.data = data;
			tree.dirty_val = true;
		}
		self.dirty_ancestors();
	}

	fn get_child(&self, child_num: usize) -> Tree<T> {
		self.borrow().children[child_num].clone() //Rc clone
	}

	fn set_child(&self, child_num: usize, child: Tree<T>) {
		let mut tree = self.borrow_mut();
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), child_num)); //Rc clone
		child.dirty_ancestors();
		let mut nodes: i32 = 0;
		{
			//handle a change in the number of decendents
			let ref old_child = tree.children[child_num].borrow();
			if old_child.decendent_count != child.borrow().decendent_count {
				nodes -= old_child.decendent_count;
				nodes += child.borrow().decendent_count;
				child.borrow_mut().stable_children = 0;
				self.mark_recount(child_num, nodes);
			}
		}
		tree.children[child_num] = child;
	}

	fn push_child(&self, child: Tree<T>) {
		//assume all new values are dirty
		child.dirty_decendents();
		child.borrow_mut().parent = Some((self.clone(), self.borrow().children.len())); //Rc clone
		child.dirty_ancestors();
		let index;
		let nodes;
		{
			let mut tree = self.borrow_mut();
			nodes = 1 + child.borrow().decendent_count;
			index = tree.children.len();
			tree.children.push(child);
		}
		self.mark_recount(index, nodes);
	}

	fn pop_child(&self) -> Option<Tree<T>> {
		//assume all new values are dirty
		let child = self.borrow_mut().children.pop();
		let nodes = match child {
			None => 0,
			Some(ref c) => 1 + c.borrow().decendent_count,
		};
		let index = std::cmp::max(self.borrow().children.len(),0);
		self.mark_recount(index, -nodes);
		child
	}
}

fn dump<T: Clone>(tree: &Tree<T>, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
    vec.push(tree.data.clone());
    tree.dirty_val = false;
    for kid in &tree.children { dump(&kid, vec); }
    tree.dirty_descendant = false;
    tree.stable_children = tree.children.len();
}

fn update<T: Clone>(tree: &Tree<T>, start_index: usize, vec: &mut PermutedVec<T>) {
	let mut tree = tree.borrow_mut();
	let mut index = start_index;
	if tree.dirty_val {
		if vec.len() <= index {
			vec.push(tree.data.clone())
		} else {
			vec.set(index, tree.data.clone());
		}
		tree.dirty_val = false;
	}
	if !tree.dirty_descendant {return}
	index += 1;

	//all the children with unchanged decendent counts
	for counted_kid in 0..tree.stable_children{
		update(&tree.children[counted_kid], index, vec);
		index += 1 + tree.children[counted_kid].borrow_mut().decendent_count as usize;
	}
	if tree.stable_children < tree.children.len() {
		{
			//first changed child may have unchanged decendents
			let first_changed = &tree.children[tree.stable_children];
			update(&first_changed, index, vec);
			index += 1 + first_changed.borrow().decendent_count as usize;
		}
		//dump any additional children
		if (tree.stable_children + 1) < tree.children.len() {
			vec.truncate(index);
			for uncounted_kid in (tree.stable_children + 1)..tree.children.len() {
				dump(&tree.children[uncounted_kid], vec);
			}
			tree.stable_children = tree.children.len();
		}
	}
}

#[test]
fn test_tree() {
    let main_tree = Tree::new_node(37);
    let mut vec = PermutedVec::new();
    dump(&main_tree, &mut vec);
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&37]);
}

#[test]
fn test_update() {
	println!("start first dump");
	let main_tree = Tree::new_node(37);
	main_tree.push_child(Tree::new_node(42));
	main_tree.push_child(Tree::new_node(20));
	main_tree.push_child(Tree::new_node(63));
	let mut vec = PermutedVec::new();
	dump(&main_tree, &mut vec);
	println!("finished first dump");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&20,&37,&42,&63]);

	println!("start first update");
    main_tree.get_child(1).set_data(25);
    update(&main_tree, 0, &mut vec);
	println!("finished first update");
    assert_eq!(vec.sorted_iter().collect::<Vec<&usize>>(), vec![&25,&37,&42,&63]);

	println!("start add branch");
    let branch = main_tree.get_child(2);
    branch.push_child(Tree::new_node(47));
    branch.push_child(Tree::new_node(53));
    branch.push_child(Tree::new_node(61));
    branch.push_child(Tree::new_node(57));
    update(&main_tree, 0, &mut vec);
	println!("finished add branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&61,&63]
    );

	println!("start update branch");
    branch.get_child(2).set_data(77);
    update(&main_tree, 0, &mut vec);
	println!("finished update branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start early insertion");
    let branch = main_tree.get_child(1);
    branch.push_child(Tree::new_node(1));
    update(&main_tree, 0, &mut vec);
    println!("finished early insertion");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77]
    );

    println!("start deep add");
    let branch = main_tree.get_child(2).get_child(2);
    let new_branch = Tree::new_node(100);
    new_branch.push_child(Tree::new_node(101));
    branch.push_child(new_branch);
    update(&main_tree, 0, &mut vec);
    println!("finished deep add");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&101]
    );

    println!("start deep edit");
    let branch = branch.get_child(0).get_child(0);
    branch.set_data(105);
    update(&main_tree, 0, &mut vec);
    println!("finished deep edit");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77,&100,&105]
    );

    println!("start pop branch");
    let branch = main_tree.get_child(2).get_child(2);
    branch.pop_child();
    update(&main_tree, 0, &mut vec);
    println!("finished pop branch");
    assert_eq!(
    	vec.sorted_iter().collect::<Vec<&usize>>(),
    	vec![&1,&25,&37,&42,&47,&53,&57,&63,&77]
    );

}
<|endoftext|>