<commit_before>#[macro_use]
extern crate criterion;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate named_type_derive;

#[macro_use]
extern crate derivative;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate serde_json;

extern crate byteorder;
extern crate indy;
extern crate indy_crypto;
extern crate uuid;
extern crate named_type;
extern crate rmp_serde;
extern crate rust_base58;
extern crate time;
extern crate serde;

// Workaround to share some utils code based on indy sdk types between tests and indy sdk
use indy::api as api;

#[path = "../tests/utils/mod.rs"]
#[macro_use]
mod utils;

use utils::wallet::WalletUtils;
use utils::test::TestUtils;
use utils::constants::*;

use criterion::{Criterion, Benchmark};

mod create {
    use super::*;

    fn setup() {
        TestUtils::cleanup_storage();
    }

    fn create_argon2i_mod((): ()) {
        WalletUtils::create_wallet(DEFAULT_WALLET_CONFIG, WALLET_CREDENTIALS_ARAGON2I_MOD).unwrap();
    }

    fn create_argon2i_int((): ()) {
        WalletUtils::create_wallet(DEFAULT_WALLET_CONFIG, WALLET_CREDENTIALS_ARAGON2I_INT).unwrap();
    }

    fn create_raw((): ()) {
        WalletUtils::create_wallet(DEFAULT_WALLET_CONFIG, WALLET_CREDENTIALS_RAW).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        c.bench(
            "wallet_create",
            Benchmark::new("create_argon2i_mod", |b| b.iter_with_setup(setup, create_argon2i_mod))
                .sample_size(10));

        c.bench(
            "wallet_create",
            Benchmark::new("create_argon2i_int", |b| b.iter_with_setup(setup, create_argon2i_int))
                .sample_size(20));

        c.bench(
            "wallet_create",
            Benchmark::new("create_raw", |b| b.iter_with_setup(setup, create_raw))
                .sample_size(50));
    }
}

criterion_group!(benches, create::bench);
criterion_main!(benches);<commit_msg>wallet api is almost covered<commit_after>#[macro_use]
extern crate criterion;

#[macro_use]
extern crate lazy_static;

#[macro_use]
extern crate named_type_derive;

#[macro_use]
extern crate derivative;

#[macro_use]
extern crate serde_derive;

#[macro_use]
extern crate serde_json;

extern crate byteorder;
extern crate indy;
extern crate indy_crypto;
extern crate uuid;
extern crate named_type;
extern crate rmp_serde;
extern crate rust_base58;
extern crate time;
extern crate serde;
extern crate rand;

// Workaround to share some utils code based on indy sdk types between tests and indy sdk
use indy::api as api;

#[path = "../tests/utils/mod.rs"]
#[macro_use]
mod utils;

use utils::wallet::WalletUtils;
use utils::non_secrets::NonSecretsUtils;
use utils::test::TestUtils;
use utils::constants::*;

use criterion::{Criterion, Benchmark, ParameterizedBenchmark};

mod wallet {
    use super::*;
    use utils::sequence::SequenceUtils;
    use rand::Rng;

    pub const COUNT: usize = 1000;
    pub const TYPE_1: &'static str = "type_1";
    pub const TYPE_2: &'static str = "type_2";

    pub fn _type(suffix: usize) -> String {
        if suffix % 2 != 0 { TYPE_1.to_string() } else { TYPE_2.to_string() }
    }

    pub fn _id(suffix: usize) -> String { format!("id_{}", suffix) }

    pub fn _value(suffix: usize) -> String {
        format!("value_{}", suffix)
    }

    pub fn _tags(suffix: usize) -> String {
        json!({
            "tag_id_1":  format!("tag_value_{}_1", suffix),
            "tag_id_2":  format!("tag_value_{}_2", suffix),
            "~tag_id_3":  format!("{}", suffix),
        }).to_string()
    }

    pub fn add_records(wallet_handle: i32) {
        for i in 0..COUNT {
            NonSecretsUtils::add_wallet_record(wallet_handle,
                                               &_type(i),
                                               &_id(i),
                                               &_value(i),
                                               Some(&_tags(i))).unwrap();
        }
    }

    pub fn setup() -> i32 {
        TestUtils::cleanup_storage();

        let config = json!({
            "id": format!("default-wallet_id-{}", SequenceUtils::get_next_id())
        }).to_string();

        WalletUtils::create_wallet(&config, WALLET_CREDENTIALS_RAW).unwrap();
        let wallet_handle = WalletUtils::open_wallet(&config, WALLET_CREDENTIALS_RAW).unwrap();

        add_records(wallet_handle);

        wallet_handle
    }

    pub fn get_rand_key() -> (String, String) {
        let i = rand::thread_rng().gen_range(0, COUNT);
        (_type(i), _id(i))
    }
}

mod create {
    use super::*;

    fn setup() {
        TestUtils::cleanup_storage();
    }

    fn create_wallet(credentials: &str) {
        WalletUtils::create_wallet(DEFAULT_WALLET_CONFIG, credentials).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        c.bench(
            "wallet_create",
            ParameterizedBenchmark::new(
                "wallet_create",
                |b, credentials| b.iter_with_setup(setup, |()| create_wallet(credentials)),
                vec![WALLET_CREDENTIALS_ARAGON2I_MOD, WALLET_CREDENTIALS_ARAGON2I_INT, WALLET_CREDENTIALS_RAW],
            ).sample_size(10),
        );
    }
}

mod open {
    use super::*;
    use utils::sequence::SequenceUtils;

    fn setup(credentials: &str) -> String {
        TestUtils::cleanup_storage();

        let config = json!({
            "id": format!("default-wallet_id-{}", SequenceUtils::get_next_id())
        }).to_string();

        WalletUtils::create_wallet(&config, credentials).unwrap();
        config
    }

    fn open_wallet(config: &str, credentials: &str) {
        WalletUtils::open_wallet(config, credentials).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        c.bench(
            "wallet_open",
            ParameterizedBenchmark::new(
                "wallet_open",
                |b, credentials| b.iter_with_setup(|| setup(credentials), |config: String| open_wallet(&config, credentials)),
                vec![WALLET_CREDENTIALS_ARAGON2I_MOD, WALLET_CREDENTIALS_ARAGON2I_INT, WALLET_CREDENTIALS_RAW],
            ).sample_size(10),
        );
    }
}

mod close {
    use super::*;

    fn setup(credentials: &str) -> i32 {
        TestUtils::cleanup_storage();
        WalletUtils::create_wallet(DEFAULT_WALLET_CONFIG, credentials).unwrap();
        WalletUtils::open_wallet(DEFAULT_WALLET_CONFIG, credentials).unwrap()
    }

    fn close_wallet(handle: i32) {
        WalletUtils::close_wallet(handle).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        c.bench(
            "wallet_close",
            ParameterizedBenchmark::new(
                "wallet_close",
                |b, credentials| b.iter_with_setup(|| setup(credentials), |handle: i32| close_wallet(handle)),
                vec![WALLET_CREDENTIALS_ARAGON2I_MOD, WALLET_CREDENTIALS_ARAGON2I_INT, WALLET_CREDENTIALS_RAW],
            ).sample_size(10),
        );
    }
}

mod delete {
    use super::*;

    fn setup(credentials: &str) {
        TestUtils::cleanup_storage();
        WalletUtils::create_wallet(DEFAULT_WALLET_CONFIG, credentials).unwrap();
    }

    fn delete_wallet(credentials: &str) {
        WalletUtils::delete_wallet(DEFAULT_WALLET_CONFIG, credentials).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        c.bench(
            "wallet_delete",
            ParameterizedBenchmark::new(
                "wallet_delete",
                |b, credentials| b.iter_with_setup(|| setup(credentials), |()| delete_wallet(credentials)),
                vec![WALLET_CREDENTIALS_ARAGON2I_MOD, WALLET_CREDENTIALS_ARAGON2I_INT, WALLET_CREDENTIALS_RAW],
            ).sample_size(10),
        );
    }
}

mod get_record {
    use super::*;

    fn get_record(wallet_handle: i32, type_: &str, id: &str) {
        NonSecretsUtils::get_wallet_record(wallet_handle, type_, id, "{}").unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        let wallet_handle = wallet::setup();

        c.bench(
            "wallet_get_record",
            Benchmark::new("wallet_get_record", move |b|
                b.iter_with_setup(wallet::get_rand_key, |(type_, id): (String, String)| get_record(wallet_handle, &type_, &id)))
                .sample_size(50));
    }
}

mod delete_record {
    use super::*;

    static mut INDEX: usize = 0;

    fn setup() -> (String, String) {
        unsafe {
            INDEX = INDEX + 1 + 5;//TODO
            (wallet::_type(INDEX), wallet::_id(INDEX))
        }
    }

    fn delete_record(wallet_handle: i32, type_: &str, id: &str) {
        NonSecretsUtils::delete_wallet_record(wallet_handle, type_, id).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        let wallet_handle = wallet::setup();

        c.bench(
            "wallet_delete_record",
            Benchmark::new("wallet_delete_record", move |b|
                b.iter_with_setup(setup, |(type_, id): (String, String)| delete_record(wallet_handle, &type_, &id)))
                .sample_size(10));
    }
}

mod add_record {
    use super::*;

    static mut INDEX: usize = wallet::COUNT;

    fn setup() -> (String, String, String, String) {
        unsafe {
            INDEX = INDEX + 1;
            (wallet::_type(INDEX), wallet::_id(INDEX), wallet::_value(INDEX), wallet::_tags(INDEX))
        }
    }

    fn add_record(wallet_handle: i32, type_: &str, id: &str, value: &str, tags: &str) {
        NonSecretsUtils::add_wallet_record(wallet_handle, type_, id, value, Some(tags)).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        let wallet_handle = wallet::setup();

        c.bench(
            "wallet_add_record",
            Benchmark::new("wallet_add_record", move |b|
                b.iter_with_setup(
                    setup, |(type_, id, value, tags): (String, String, String, String)|
                        add_record(wallet_handle, &type_, &id, &value, &tags)))
                .sample_size(10));
    }
}

mod add_record_tags {
    use super::*;

    fn setup() -> (String, String, String) {
        let (type_, id) = wallet::get_rand_key();
        (type_, id, r#"{"tag_1": "value_1", "~tag_2": "value_2"}"#.to_string())
    }

    fn add_record_tags(wallet_handle: i32, type_: &str, id: &str, tags: &str) {
        NonSecretsUtils::add_wallet_record_tags(wallet_handle, type_, id, tags).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        let wallet_handle = wallet::setup();

        c.bench(
            "wallet_add_record_tags",
            Benchmark::new("wallet_add_record_tags", move |b|
                b.iter_with_setup(
                    setup,
                    |(type_, id, tags): (String, String, String)| add_record_tags(wallet_handle, &type_, &id, &tags)))
                .sample_size(10));
    }
}

mod delete_record_tags {
    use super::*;

    fn setup() -> (String, String, String) {
        let (type_, id) = wallet::get_rand_key();
        (type_, id, r#"["tag_id_1"]"#.to_string())
    }

    fn delete_record_tags(wallet_handle: i32, type_: &str, id: &str, tag_names: &str) {
        NonSecretsUtils::delete_wallet_record_tags(wallet_handle, type_, id, tag_names).unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        let wallet_handle = wallet::setup();

        c.bench(
            "wallet_delete_record_tags",
            Benchmark::new("wallet_delete_record_tags", move |b|
                b.iter_with_setup(
                    setup,
                    |(type_, id, tag_names): (String, String, String)| delete_record_tags(wallet_handle, &type_, &id, &tag_names)))
                .sample_size(10));
    }
}

mod search_records {
    use super::*;

    fn open_search(wallet_handle: i32, query: &str) {
        NonSecretsUtils::open_wallet_search(wallet_handle, wallet::TYPE_1, query, "{}").unwrap();
    }

    pub fn bench(c: &mut Criterion) {
        let wallet_handle = wallet::setup();

        let queries = vec![
            r#"{}"#,
            r#"{
                "tag_id_1": "tag_value_10_1"
            }"#,
            r#"{
                "~tag_id_3": {
                    "$gt": "30"
                }
            }"#,
            r#"{
                "tag_id_1": {
                    "$in": ["tag_value_10_1", "tag_value_11_1", "tag_value_70_1", "tag_value_71_1"]
                }
            }"#,
            r#"{
                "tag_id_1": "tag_value_11_1",
                "~tag_id_3": "10"
            }"#,
            r#"{
                "$or": [
                    {"tag_id_1": "tag_value_11_1"},
                    {"tag_id_3": "90"}
                ]
            }"#
        ];

        c.bench(
            "wallet_search",
            ParameterizedBenchmark::new(
                "wallet_search",
                move |b, query| b.iter(|| open_search(wallet_handle, query)),
                queries,
            ).sample_size(10),
        );
    }
}

criterion_group!(benches, create::bench,
                          open::bench,
                          close::bench,
                          delete::bench,
                          get_record::bench,
                          delete_record::bench,
                          add_record::bench,
                          add_record_tags::bench,
                          delete_record_tags::bench,
                          search_records::bench);
//criterion_group!(benches,  search_records::bench);
criterion_main!(benches);<|endoftext|>


<commit_before>//! OS-specific functionality.

#![stable(feature = "os", since = "1.0.0")]
#![allow(missing_docs, nonstandard_style, missing_debug_implementations)]

pub mod raw;

// The code below could be written clearer using `cfg_if!`. However, the items below are
// publicly exported by `std` and external tools can have trouble analysing them because of the use
// of a macro that is not vendored by Rust and included in the toolchain.
// See https://github.com/rust-analyzer/rust-analyzer/issues/6038.

// On certain platforms right now the "main modules" modules that are
// documented don't compile (missing things in `libc` which is empty),
// so just omit them with an empty module and add the "unstable" attribute.

#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
pub mod fortanix_sgx;

// Unix, linux, wasi and windows are handled a bit differently.
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod unix {}
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod linux {}
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod wasi {}
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod windows {}

// unix
#[cfg(not(any(
    all(target_arch = "wasm32", not(target_os = "wasi")),
    all(target_vendor = "fortanix", target_env = "sgx")
)))]
#[cfg(all(not(doc), target_os = "hermit"))]
#[path = "hermit/mod.rs"]
pub mod unix;
#[cfg(not(any(
    all(target_arch = "wasm32", not(target_os = "wasi")),
    all(target_vendor = "fortanix", target_env = "sgx")
)))]
#[cfg(any(unix, doc))]
pub mod unix;

// linux
#[cfg(not(any(
    all(target_arch = "wasm32", not(target_os = "wasi")),
    all(target_vendor = "fortanix", target_env = "sgx")
)))]
#[cfg(any(target_os = "linux", target_os = "l4re", doc))]
pub mod linux;

// wasi
#[cfg(not(any(
    all(target_arch = "wasm32", not(target_os = "wasi")),
    all(target_vendor = "fortanix", target_env = "sgx")
)))]
#[cfg(any(target_os = "wasi", doc))]
pub mod wasi;

// windows
#[cfg(not(any(
    all(target_arch = "wasm32", not(target_os = "wasi")),
    all(target_vendor = "fortanix", target_env = "sgx")
)))]
#[cfg(any(windows, doc))]
pub mod windows;

// Others.
#[cfg(target_os = "android")]
pub mod android;
#[cfg(target_os = "dragonfly")]
pub mod dragonfly;
#[cfg(target_os = "emscripten")]
pub mod emscripten;
#[cfg(target_os = "espidf")]
pub mod espidf;
#[cfg(target_os = "freebsd")]
pub mod freebsd;
#[cfg(target_os = "fuchsia")]
pub mod fuchsia;
#[cfg(target_os = "haiku")]
pub mod haiku;
#[cfg(target_os = "illumos")]
pub mod illumos;
#[cfg(target_os = "ios")]
pub mod ios;
#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "netbsd")]
pub mod netbsd;
#[cfg(target_os = "openbsd")]
pub mod openbsd;
#[cfg(target_os = "redox")]
pub mod redox;
#[cfg(target_os = "solaris")]
pub mod solaris;

#[cfg(target_os = "vxworks")]
pub mod vxworks;

#[cfg(any(unix, target_os = "wasi", doc))]
mod fd;
<commit_msg>Move fortanix module position in std::os reexports for alpha sort<commit_after>//! OS-specific functionality.

#![stable(feature = "os", since = "1.0.0")]
#![allow(missing_docs, nonstandard_style, missing_debug_implementations)]

pub mod raw;

// The code below could be written clearer using `cfg_if!`. However, the items below are
// publicly exported by `std` and external tools can have trouble analysing them because of the use
// of a macro that is not vendored by Rust and included in the toolchain.
// See https://github.com/rust-analyzer/rust-analyzer/issues/6038.

// On certain platforms right now the "main modules" modules that are
// documented don't compile (missing things in `libc` which is empty),
// so just omit them with an empty module and add the "unstable" attribute.

// Unix, linux, wasi and windows are handled a bit differently.
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod unix {}
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod linux {}
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod wasi {}
#[cfg(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
))]
#[unstable(issue = "none", feature = "std_internals")]
pub mod windows {}

// unix
#[cfg(not(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
)))]
#[cfg(target_os = "hermit")]
#[path = "hermit/mod.rs"]
pub mod unix;
#[cfg(not(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
)))]
#[cfg(all(not(target_os = "hermit"), any(unix, doc)))]
pub mod unix;

// linux
#[cfg(not(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
)))]
#[cfg(any(target_os = "linux", target_os = "l4re", doc))]
pub mod linux;

// wasi
#[cfg(not(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
)))]
#[cfg(any(target_os = "wasi", doc))]
pub mod wasi;

// windows
#[cfg(not(all(
    doc,
    any(
        all(target_arch = "wasm32", not(target_os = "wasi")),
        all(target_vendor = "fortanix", target_env = "sgx")
    )
)))]
#[cfg(any(windows, doc))]
pub mod windows;

// Others.
#[cfg(target_os = "android")]
pub mod android;
#[cfg(target_os = "dragonfly")]
pub mod dragonfly;
#[cfg(target_os = "emscripten")]
pub mod emscripten;
#[cfg(target_os = "espidf")]
pub mod espidf;
#[cfg(all(target_vendor = "fortanix", target_env = "sgx"))]
pub mod fortanix_sgx;
#[cfg(target_os = "freebsd")]
pub mod freebsd;
#[cfg(target_os = "fuchsia")]
pub mod fuchsia;
#[cfg(target_os = "haiku")]
pub mod haiku;
#[cfg(target_os = "illumos")]
pub mod illumos;
#[cfg(target_os = "ios")]
pub mod ios;
#[cfg(target_os = "macos")]
pub mod macos;
#[cfg(target_os = "netbsd")]
pub mod netbsd;
#[cfg(target_os = "openbsd")]
pub mod openbsd;
#[cfg(target_os = "redox")]
pub mod redox;
#[cfg(target_os = "solaris")]
pub mod solaris;

#[cfg(target_os = "vxworks")]
pub mod vxworks;

#[cfg(any(unix, target_os = "wasi", doc))]
mod fd;
<|endoftext|>


<commit_before>use std::task::spawn;
use std::comm::channel;

fn main () {
    let (sender, receiver) = channel::<int>();

    for child_number in range(0, 20) {
        let child_sender = sender.clone();
        spawn(proc() {
            child_sender.send(child_number);
        });
    }

    for _ in range(0, 20) {
        let received = receiver.recv();
        println!("Message received from child {}", received);
    }
}
<commit_msg>Update for rust changes<commit_after>use std::task::spawn;
use std::comm::channel;

fn main () {
    let (sender, receiver) = channel::<int>();

    for child_number in range(0, 20i) {
        let child_sender = sender.clone();
        spawn(proc() {
            child_sender.send(child_number);
        });
    }

    for _ in range(0, 20i) {
        let received = receiver.recv();
        println!("Message received from child {}", received);
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate criterion;
extern crate fitsio;

use criterion::Criterion;

fn opening_files(c: &mut Criterion) {
    let filename = "../testdata/full_example.fits";
    c.bench_function("opening and closing files", move |b| b.iter(|| {
        {
            let _f = fitsio::FitsFile::open(filename).unwrap();
            /* Implicit drop */
        }
    }));
}

criterion_group!(benches, opening_files);
criterion_main!(benches);
<commit_msg>Include full_example example as benchmark<commit_after>#[macro_use]
extern crate criterion;
#[macro_use]
extern crate fitsio_derive;
extern crate fitsio;
extern crate tempdir;

use criterion::Criterion;
use fitsio::images::{ImageDescription, ImageType};
use fitsio::tables::{ColumnDataType, ColumnDescription, FitsRow};
use fitsio::FitsFile;
use tempdir::TempDir;

fn opening_files(c: &mut Criterion) {
    let filename = "../testdata/full_example.fits";
    c.bench_function("opening and closing files", move |b| {
        b.iter(|| {
            {
                let _f = fitsio::FitsFile::open(filename).unwrap();
                /* Implicit drop */
            }
        })
    });
}

/* Benchmark the example code */
fn full_example(c: &mut Criterion) {
    /* Create a temporary directory to work from */
    let tmp_dir = TempDir::new("fitsio").unwrap();
    let file_path = tmp_dir.path().join("example.fits");

    c.bench_function("full example", move |b| {
        b.iter(|| {
            // creating a new file with 512 rows and 1024 columns
            let primary_hdu_description = ImageDescription {
                data_type: ImageType::Double,
                dimensions: &[512, 1024],
            };

            {
                // .overwrite ensures that if the file already exists, the existing file is removed first.
                let mut fitsfile = FitsFile::create(&file_path)
                    .with_custom_primary(&primary_hdu_description)
                    .overwrite()
                    .open()
                    .unwrap();

                /* We will now add some dummy header keys. We add:
                 * - the name of the project (String)
                 * - the exposure time (f32)
                 * - the image id (i64)
                 */

                /* First we get the primary HDU */
                let hdu = fitsfile.primary_hdu().unwrap();

                /* Now we add the header keys */
                hdu.write_key(&mut fitsfile, "PROJECT", "My First Astronomy Project")
                    .unwrap();

                /* Now the exposure time */
                hdu.write_key(&mut fitsfile, "EXPTIME", 15.2f32).unwrap();

                /* And finally the image id */
                hdu.write_key(&mut fitsfile, "IMAGE_ID", 20180101010005i64)
                    .unwrap();

                /* Now we write some dummy data to the primary HDU. We write the full image in this case.
                 * */

                let dummy_data: Vec<f32> = (0..(1024 * 512))
                    .map(|val| (val as f32) * 12.5 + 102.5)
                    .collect();

                hdu.write_image(&mut fitsfile, &dummy_data).unwrap();

                /* We can create a new image with the following */
                let image_description = ImageDescription {
                    data_type: ImageType::Long,
                    dimensions: &[256, 256],
                };
                fitsfile.create_image("IMG", &image_description).unwrap();

                /* Now we add a new table HDU called "DATA"
                 *
                 * We add three columns:
                 *
                 * 1. Object id (i32)
                 * 2. Object name (String, up to 10 characters)
                 * 3. Object magnitude (f32)
                 * */
                let col1 = ColumnDescription::new("OBJ_ID")
                    .with_type(ColumnDataType::Int)
                    .create()
                    .unwrap();
                let col2 = ColumnDescription::new("NAME")
                    .with_type(ColumnDataType::String)
                    .that_repeats(10)
                    .create()
                    .unwrap();
                let col3 = ColumnDescription::new("MAG")
                    .with_type(ColumnDataType::Float)
                    .create()
                    .unwrap();
                let columns = &[col1, col2, col3];
                let table_hdu = fitsfile.create_table("DATA", columns).unwrap();

                /* Add some data to the columns */
                let n_rows = 10;
                let obj_id_data: Vec<i32> = (0..n_rows).collect();
                let name_data: Vec<String> = (0..n_rows).map(|idx| format!("N{}", idx)).collect();
                let mag_data: Vec<f32> = (0..n_rows).map(|idx| -0.2 * idx as f32).collect();

                table_hdu
                    .write_col(&mut fitsfile, "OBJ_ID", &obj_id_data)
                    .unwrap();
                table_hdu
                    .write_col(&mut fitsfile, "NAME", &name_data)
                    .unwrap();
                table_hdu
                    .write_col(&mut fitsfile, "MAG", &mag_data)
                    .unwrap();

                /* `fitsfile` is dropped here at the end of the scope, which closes fhe file on disk */
            }

            /* Here we re-open the file. We want to adjust things, so we use the `edit` method */
            let mut fitsfile = FitsFile::edit(&file_path).unwrap();

            /* Get the primary HDU and read a section of the image data */
            let phdu = fitsfile.primary_hdu().unwrap();

            /* Let's say we have a region around a star that we want to extract. The star is at (25, 25,
             * 1-indexed) and we want to extract a 5x5 box around it. This means we want to read rows 19 to
             * 19, and columns 19 to 29 (0-indexed). The range arguments are exclusive of the upper bound,
             * so we must use 19..29 for each axis.
             *
             * Note: this is not performant, and if this is the desired use of `fitsio`, I would not
             * suggest this approach. */
            let _image_data: Vec<f32> = phdu.read_region(&mut fitsfile, &[&(19..29), &(19..29)])
                .unwrap();

            /* We can now get the column data. Let's assume we want all of the magnitudes for objects near
             * this one (by index). This object is at index 4 (0-indexed) and we want to read one value
             * before and after it. */
            let table_hdu = fitsfile.hdu("DATA").unwrap();
            let _magnitudes: Vec<f32> = table_hdu
                .read_col_range(&mut fitsfile, "MAG", &(3..6))
                .unwrap();

            /* Read a single row from the file. Columns can be renamed to allow for more rustic attribute
             * names */
            #[derive(Default, FitsRow)]
            struct Row {
                #[fitsio(colname = "OBJ_ID")]
                obj_id: i32,
                #[fitsio(colname = "NAME")]
                name: String,
                #[fitsio(colname = "MAG")]
                mag: f32,
            }
            let _row: Row = table_hdu.row(&mut fitsfile, 4).unwrap();
        })
    });
}

criterion_group!(benches, opening_files, full_example);
criterion_main!(benches);
<|endoftext|>


<commit_before>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{
    components::activity_indicator,
    ctx_help::CtxHelp,
    extensions::MergeAttrs as _,
    font_awesome, font_awesome_outline,
    generated::css_classes::C,
    page::{activity, logs},
    route::Route,
    GMsg, ServerDate, HELP_PATH,
};
use iml_wire_types::{
    warp_drive::{ArcCache, Locks},
    Session,
};
use seed::{prelude::*, *};

#[derive(Default)]
pub struct Model {
    section: Option<Section>,
}

pub enum Section {
    Activity(activity::Model),
    Logs(logs::Model),
}

#[derive(Clone, PartialEq, Debug)]
pub enum SectionSelector {
    Activity,
    Logs,
}

impl From<SectionSelector> for Section {
    fn from(section: SectionSelector) -> Self {
        match section {
            SectionSelector::Activity => Self::Activity(activity::Model::default()),
            SectionSelector::Logs => Self::Logs(logs::Model::default()),
        }
    }
}

impl From<&Section> for SectionSelector {
    fn from(section: &Section) -> Self {
        match section {
            Section::Activity(_) => Self::Activity,
            Section::Logs(_) => Self::Logs,
        }
    }
}

#[derive(Clone, Debug)]
pub enum Msg {
    ActivitySection(activity::Msg),
    LogsSection(logs::Msg),
    Open(SectionSelector),
    Close,
}

pub fn update(msg: Msg, records: &ArcCache, model: &mut Model, orders: &mut impl Orders<Msg, GMsg>) {
    match msg {
        Msg::Open(section) => {
            if model.section.as_ref().map(|x| x.into()).as_ref() == Some(&section) {
                orders.send_msg(Msg::Close);
                return;
            }

            let section = section.into();

            match &section {
                Section::Activity(_) => activity::init(&mut orders.proxy(Msg::ActivitySection)),
                Section::Logs(_) => logs::init(&mut orders.proxy(Msg::LogsSection)),
            }

            model.section = Some(section);
        }
        Msg::Close => {
            model.section = None;
        }
        Msg::ActivitySection(msg) => {
            if let Some(Section::Activity(x)) = &mut model.section {
                activity::update(msg, records, x, &mut orders.proxy(Msg::ActivitySection))
            }
        }
        Msg::LogsSection(msg) => {
            if let Some(Section::Logs(x)) = &mut model.section {
                logs::update(msg, x, &mut orders.proxy(Msg::LogsSection))
            }
        }
    }
}

fn toggle_section(model: &Model) -> Node<Msg> {
    let (icon, msg) = match model.section.as_ref() {
        Some(_) => ("chevron-right", Msg::Close),
        None => ("chevron-left", Msg::Open(SectionSelector::Activity)),
    };

    div![
        class![C.flex, C.justify_center],
        a![
            class![C.cursor_pointer],
            simple_ev(Ev::Click, msg),
            font_awesome(class![C.inline, C.w_4, C.h_4, C.text_gray_700, C.mt_6], icon)
        ]
    ]
}

pub fn view(
    model: &Model,
    route: &Route,
    cache: &ArcCache,
    activity_health: &activity_indicator::ActivityHealth,
    session: Option<&Session>,
    all_locks: &Locks,
    sd: &ServerDate,
) -> Node<Msg> {
    div![
        class![
            C.flex,
            C.lg__fixed => model.section.is_some(),
            C.lg__right_0 => model.section.is_some()
        ],
        div![
            class![
                C.flex,
                C.flex_col,
                C.bg_blue_1000,
                C.lg__w_24,
                C.lg__h_main_content,
                C.hidden,
                C.lg__grid,
                C.lg__grid_rows_4,
            ],
            toggle_section(model),
            side_panel_buttons(route, activity_health).merge_attrs(class![C.row_span_2])
        ],
        // Status interaction panel
        match model.section.as_ref() {
            Some(Section::Activity(x)) => {
                section_container(
                    activity::view(x, session, all_locks, sd)
                        .els()
                        .map_msg(Msg::ActivitySection),
                )
            }
            Some(Section::Logs(x)) => {
                section_container(logs::view(x, cache).els().map_msg(Msg::LogsSection))
            }
            None => empty![],
        }
    ]
}

fn side_panel_buttons(route: &Route, activity_health: &activity_indicator::ActivityHealth) -> Node<Msg> {
    div![
        class![C.grid, C.grid_rows_3],
        div![
            class![
                // C.bg_menu_active => model.route == Route::Activity,
                C.flex,
                C.flex_col,
                C.group,
                C.items_center,
                C.justify_center,
                C.text_gray_500
                // C.text_gray_500 => model.route != Route::Activity,
                // C.text_white => model.route == Route::Activity,
            ],
            a![
                class![C.cursor_pointer],
                simple_ev(Ev::Click, Msg::Open(SectionSelector::Activity)),
                activity_indicator::view(activity_health),
            ],
            a![
                class![C.text_sm, C.group_hover__text_active, C.pt_2, C.cursor_pointer],
                simple_ev(Ev::Click, Msg::Open(SectionSelector::Activity)),
                "Activity"
            ]
        ],
        div![
            class![
                // C.bg_menu_active => model.route == Route::Logs,
                C.flex,
                C.flex_col,
                C.group,
                C.items_center,
                C.justify_center,
            ],
            a![
                class![C.cursor_pointer],
                simple_ev(Ev::Click, Msg::Open(SectionSelector::Logs)),
                font_awesome(class![C.h_8, C.w_8, C.text_pink_600], "book"),
            ],
            a![
                class![C.text_sm, C.group_hover__text_active, C.text_gray_500, C.pt_2],
                simple_ev(Ev::Click, Msg::Open(SectionSelector::Logs)),
                "Logs"
            ]
        ],
        div![
            class![C.flex, C.flex_col, C.group, C.justify_center, C.items_center],
            context_sensitive_help_link(route).els()
        ]
    ]
}

fn section_container(children: impl View<Msg>) -> Node<Msg> {
    div![
        class![
            C.flex,
            C.flex_col,
            C.bg_blue_1000,
            C.lg__h_main_content,
            C.pt_6,
            C.overflow_y_scroll,
            C.flex_grow,
            C.w_132
        ],
        children.els()
    ]
}

fn context_sensitive_help_link<T: 'static>(route: &Route) -> impl View<T> {
    let at = attrs! {
       At::Target => "_blank", // open the link in a new tab
       At::Href => format!(
           "/{}/docs/Graphical_User_Interface_9_0.html{}",
           HELP_PATH,
           route.help_link().unwrap_or_else(|| "".into())
       )
    };

    nodes![
        a![
            &at,
            font_awesome_outline(class![C.h_8, C.w_8, C.text_blue_400], "question-circle")
        ],
        a![
            &at,
            class![C.text_sm, C.group_hover__text_active, C.text_gray_500, C.pt_2],
            "Support"
        ]
    ]
}
<commit_msg>Hide the Logs icon from the anonymous user (#1756)<commit_after>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

use crate::{
    components::activity_indicator,
    ctx_help::CtxHelp,
    extensions::MergeAttrs as _,
    font_awesome, font_awesome_outline,
    generated::css_classes::C,
    page::{activity, logs},
    restrict,
    route::Route,
    GMsg, ServerDate, HELP_PATH,
};
use iml_wire_types::{
    warp_drive::{ArcCache, Locks},
    GroupType, Session,
};
use seed::{prelude::*, *};

#[derive(Default)]
pub struct Model {
    section: Option<Section>,
}

pub enum Section {
    Activity(activity::Model),
    Logs(logs::Model),
}

#[derive(Clone, PartialEq, Debug)]
pub enum SectionSelector {
    Activity,
    Logs,
}

impl From<SectionSelector> for Section {
    fn from(section: SectionSelector) -> Self {
        match section {
            SectionSelector::Activity => Self::Activity(activity::Model::default()),
            SectionSelector::Logs => Self::Logs(logs::Model::default()),
        }
    }
}

impl From<&Section> for SectionSelector {
    fn from(section: &Section) -> Self {
        match section {
            Section::Activity(_) => Self::Activity,
            Section::Logs(_) => Self::Logs,
        }
    }
}

#[derive(Clone, Debug)]
pub enum Msg {
    ActivitySection(activity::Msg),
    LogsSection(logs::Msg),
    Open(SectionSelector),
    Close,
}

pub fn update(msg: Msg, records: &ArcCache, model: &mut Model, orders: &mut impl Orders<Msg, GMsg>) {
    match msg {
        Msg::Open(section) => {
            if model.section.as_ref().map(|x| x.into()).as_ref() == Some(&section) {
                orders.send_msg(Msg::Close);
                return;
            }

            let section = section.into();

            match &section {
                Section::Activity(_) => activity::init(&mut orders.proxy(Msg::ActivitySection)),
                Section::Logs(_) => logs::init(&mut orders.proxy(Msg::LogsSection)),
            }

            model.section = Some(section);
        }
        Msg::Close => {
            model.section = None;
        }
        Msg::ActivitySection(msg) => {
            if let Some(Section::Activity(x)) = &mut model.section {
                activity::update(msg, records, x, &mut orders.proxy(Msg::ActivitySection))
            }
        }
        Msg::LogsSection(msg) => {
            if let Some(Section::Logs(x)) = &mut model.section {
                logs::update(msg, x, &mut orders.proxy(Msg::LogsSection))
            }
        }
    }
}

fn toggle_section(model: &Model) -> Node<Msg> {
    let (icon, msg) = match model.section.as_ref() {
        Some(_) => ("chevron-right", Msg::Close),
        None => ("chevron-left", Msg::Open(SectionSelector::Activity)),
    };

    div![
        class![C.flex, C.justify_center],
        a![
            class![C.cursor_pointer],
            simple_ev(Ev::Click, msg),
            font_awesome(class![C.inline, C.w_4, C.h_4, C.text_gray_700, C.mt_6], icon)
        ]
    ]
}

pub fn view(
    model: &Model,
    route: &Route,
    cache: &ArcCache,
    activity_health: &activity_indicator::ActivityHealth,
    session: Option<&Session>,
    all_locks: &Locks,
    sd: &ServerDate,
) -> Node<Msg> {
    div![
        class![
            C.flex,
            C.lg__fixed => model.section.is_some(),
            C.lg__right_0 => model.section.is_some()
        ],
        div![
            class![
                C.flex,
                C.flex_col,
                C.bg_blue_1000,
                C.lg__w_24,
                C.lg__h_main_content,
                C.hidden,
                C.lg__grid,
                C.lg__grid_rows_4,
            ],
            toggle_section(model),
            side_panel_buttons(session, route, activity_health).merge_attrs(class![C.row_span_2])
        ],
        // Status interaction panel
        match model.section.as_ref() {
            Some(Section::Activity(x)) => {
                section_container(
                    activity::view(x, session, all_locks, sd)
                        .els()
                        .map_msg(Msg::ActivitySection),
                )
            }
            Some(Section::Logs(x)) => {
                section_container(logs::view(x, cache).els().map_msg(Msg::LogsSection))
            }
            None => empty![],
        }
    ]
}

fn side_panel_buttons(
    session: Option<&Session>,
    route: &Route,
    activity_health: &activity_indicator::ActivityHealth,
) -> Node<Msg> {
    div![
        class![C.grid, C.grid_rows_3],
        div![
            class![
                // C.bg_menu_active => model.route == Route::Activity,
                C.flex,
                C.flex_col,
                C.group,
                C.items_center,
                C.justify_center,
                C.text_gray_500
                // C.text_gray_500 => model.route != Route::Activity,
                // C.text_white => model.route == Route::Activity,
            ],
            a![
                class![C.cursor_pointer],
                simple_ev(Ev::Click, Msg::Open(SectionSelector::Activity)),
                activity_indicator::view(activity_health),
            ],
            a![
                class![C.text_sm, C.group_hover__text_active, C.pt_2, C.cursor_pointer],
                simple_ev(Ev::Click, Msg::Open(SectionSelector::Activity)),
                "Activity"
            ]
        ],
        restrict::view(
            session,
            GroupType::FilesystemAdministrators,
            div![
                class![
                    // C.bg_menu_active => model.route == Route::Logs,
                    C.flex,
                    C.flex_col,
                    C.group,
                    C.items_center,
                    C.justify_center,
                ],
                a![
                    class![C.cursor_pointer],
                    simple_ev(Ev::Click, Msg::Open(SectionSelector::Logs)),
                    font_awesome(class![C.h_8, C.w_8, C.text_pink_600], "book"),
                ],
                a![
                    class![C.text_sm, C.group_hover__text_active, C.text_gray_500, C.pt_2],
                    simple_ev(Ev::Click, Msg::Open(SectionSelector::Logs)),
                    "Logs"
                ]
            ]
        ),
        div![
            class![C.flex, C.flex_col, C.group, C.justify_center, C.items_center],
            context_sensitive_help_link(route).els()
        ]
    ]
}

fn section_container(children: impl View<Msg>) -> Node<Msg> {
    div![
        class![
            C.flex,
            C.flex_col,
            C.bg_blue_1000,
            C.lg__h_main_content,
            C.pt_6,
            C.overflow_y_scroll,
            C.flex_grow,
            C.w_132
        ],
        children.els()
    ]
}

fn context_sensitive_help_link<T: 'static>(route: &Route) -> impl View<T> {
    let at = attrs! {
       At::Target => "_blank", // open the link in a new tab
       At::Href => format!(
           "/{}/docs/Graphical_User_Interface_9_0.html{}",
           HELP_PATH,
           route.help_link().unwrap_or_else(|| "".into())
       )
    };

    nodes![
        a![
            &at,
            font_awesome_outline(class![C.h_8, C.w_8, C.text_blue_400], "question-circle")
        ],
        a![
            &at,
            class![C.text_sm, C.group_hover__text_active, C.text_gray_500, C.pt_2],
            "Support"
        ]
    ]
}
<|endoftext|>


<commit_before>use std::cell::Cell;
use std::convert::From;
use std::fmt::{self, Write};

use router::Collider;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct URI<'a> {
    uri: &'a str,
    path: &'a str,
    query: Option<&'a str>,
    segment_count: Cell<Option<usize>>,
}

impl<'a> URI<'a> {
    pub fn new<T: AsRef<str> + ?Sized>(uri: &'a T) -> URI<'a> {
        let uri = uri.as_ref();

        let (path, query) = match uri.find('?') {
            Some(index) => (&uri[..index], Some(&uri[(index + 1)..])),
            None => (uri, None),
        };

        URI {
            segment_count: Cell::new(None),
            uri: uri,
            path: path,
            query: query,
        }
    }

    pub fn segment_count(&self) -> usize {
        self.segment_count.get().unwrap_or_else(|| {
            let count = self.segments().count();
            self.segment_count.set(Some(count));
            count
        })
    }

    pub fn segments(&self) -> Segments<'a> {
        Segments(self.path)
    }

    pub fn query(&self) -> Option<&'a str> {
        self.query
    }

    pub fn as_str(&self) -> &'a str {
        self.uri
    }
}

impl<'a> fmt::Display for URI<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut last = '\0';
        for c in self.uri.chars() {
            if !(c == '/' && last == '/') {
                f.write_char(c)?;
            }

            last = c;
        }

        Ok(())
    }
}

unsafe impl<'a> Sync for URI<'a> { /* It's safe! */ }

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct URIBuf {
    uri: String,
    segment_count: Cell<Option<usize>>,
}

// I don't like repeating all of this stuff. Is there a better way?
impl URIBuf {
    pub fn segment_count(&self) -> usize {
        self.segment_count.get().unwrap_or_else(|| {
            let count = self.segments().count();
            self.segment_count.set(Some(count));
            count
        })
    }

    pub fn segments(&self) -> Segments {
        self.as_uri_uncached().segments()
    }

    fn as_uri_uncached(&self) -> URI {
        URI::new(self.uri.as_str())
    }

    pub fn as_uri(&self) -> URI {
        let mut uri = URI::new(self.uri.as_str());
        uri.segment_count = self.segment_count.clone();
        uri
    }

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

    pub fn to_string(&self) -> String {
        self.uri.clone()
    }
}

unsafe impl Sync for URIBuf { /* It's safe! */ }

impl fmt::Display for URIBuf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_uri_uncached().fmt(f)
    }
}

impl From<String> for URIBuf {
    fn from(uri: String) -> URIBuf {
        URIBuf {
            segment_count: Cell::new(None),
            uri: uri,
        }
    }
}

impl<'a> From<&'a str> for URIBuf {
    fn from(uri: &'a str) -> URIBuf {
        URIBuf {
            segment_count: Cell::new(None),
            uri: uri.to_string(),
        }
    }
}

impl<'a, 'b> Collider<URI<'b>> for URI<'a> {
    fn collides_with(&self, other: &URI<'b>) -> bool {
        let mut trailing = false;
        for (seg_a, seg_b) in self.segments().zip(other.segments()) {
            if seg_a.ends_with("..>") || seg_b.ends_with("..>") {
                trailing = true;
                break;
            }

            if !seg_a.collides_with(seg_b) {
                return false;
            }
        }

        if !trailing && (self.segment_count() != other.segment_count()) {
            return false;
        }

        true
    }
}

#[derive(Clone, Debug)]
pub struct Segments<'a>(&'a str);

impl<'a> Iterator for Segments<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        // Find the start of the next segment (first that's not '/').
        let i = match self.0.find(|c| c != '/') {
            Some(index) => index,
            None => return None,
        };

        // Get the index of the first character that _is_ a '/' after start.
        // j = index of first character after i (hence the i +) that's not a '/'
        let rest = &self.0[i..];
        let j = rest.find('/').map_or(self.0.len(), |j| i + j);

        // Save the result, update the iterator, and return!
        let result = Some(&self.0[i..j]);
        self.0 = &self.0[j..];
        result
    }

    // TODO: Potentially take a second parameter with Option<cached count> and
    // return it here if it's Some. The downside is that a decision has to be
    // made about -when- to compute and cache that count. A place to do it is in
    // the segments() method. But this means that the count will always be
    // computed regardless of whether it's needed. Maybe this is ok. We'll see.
    // fn count(self) -> usize where Self: Sized {
    //     self.1.unwrap_or_else(self.fold(0, |cnt, _| cnt + 1))
    // }
}

#[cfg(test)]
mod tests {
    use super::{URI, URIBuf};

    fn seg_count(path: &str, expected: usize) -> bool {
        let actual = URI::new(path).segment_count();
        let actual_buf = URIBuf::from(path).segment_count();
        if actual != expected || actual_buf != expected {
            trace_!("Count mismatch: expected {}, got {}.", expected, actual);
            trace_!("{}", if actual != expected { "lifetime" } else { "buf" });
            trace_!("Segments (for {}):", path);
            for (i, segment) in URI::new(path).segments().enumerate() {
                trace_!("{}: {}", i, segment);
            }
        }

        actual == expected && actual_buf == expected
    }

    fn eq_segments(path: &str, expected: &[&str]) -> bool {
        let uri = URI::new(path);
        let actual: Vec<&str> = uri.segments().collect();

        let uri_buf = URIBuf::from(path);
        let actual_buf: Vec<&str> = uri_buf.segments().collect();

        actual == expected && actual_buf == expected
    }

    #[test]
    fn simple_segment_count() {
        assert!(seg_count("", 0));
        assert!(seg_count("/", 0));
        assert!(seg_count("a", 1));
        assert!(seg_count("/a", 1));
        assert!(seg_count("a/", 1));
        assert!(seg_count("/a/", 1));
        assert!(seg_count("/a/b", 2));
        assert!(seg_count("/a/b/", 2));
        assert!(seg_count("a/b/", 2));
        assert!(seg_count("ab/", 1));
    }

    #[test]
    fn segment_count() {
        assert!(seg_count("////", 0));
        assert!(seg_count("//a//", 1));
        assert!(seg_count("//abc//", 1));
        assert!(seg_count("//abc/def/", 2));
        assert!(seg_count("//////abc///def//////////", 2));
        assert!(seg_count("a/b/c/d/e/f/g", 7));
        assert!(seg_count("/a/b/c/d/e/f/g", 7));
        assert!(seg_count("/a/b/c/d/e/f/g/", 7));
        assert!(seg_count("/a/b/cdjflk/d/e/f/g", 7));
        assert!(seg_count("//aaflja/b/cdjflk/d/e/f/g", 7));
        assert!(seg_count("/a   /b", 2));
    }

    #[test]
    fn single_segments_match() {
        assert!(eq_segments("", &[]));
        assert!(eq_segments("a", &["a"]));
        assert!(eq_segments("/a", &["a"]));
        assert!(eq_segments("/a/", &["a"]));
        assert!(eq_segments("a/", &["a"]));
        assert!(eq_segments("///a/", &["a"]));
        assert!(eq_segments("///a///////", &["a"]));
        assert!(eq_segments("a///////", &["a"]));
        assert!(eq_segments("//a", &["a"]));
        assert!(eq_segments("", &[]));
        assert!(eq_segments("abc", &["abc"]));
        assert!(eq_segments("/a", &["a"]));
        assert!(eq_segments("/abc/", &["abc"]));
        assert!(eq_segments("abc/", &["abc"]));
        assert!(eq_segments("///abc/", &["abc"]));
        assert!(eq_segments("///abc///////", &["abc"]));
        assert!(eq_segments("abc///////", &["abc"]));
        assert!(eq_segments("//abc", &["abc"]));
    }

    #[test]
    fn multi_segments_match() {
        assert!(eq_segments("a/b/c", &["a", "b", "c"]));
        assert!(eq_segments("/a/b", &["a", "b"]));
        assert!(eq_segments("/a///b", &["a", "b"]));
        assert!(eq_segments("a/b/c/d", &["a", "b", "c", "d"]));
        assert!(eq_segments("///a///////d////c", &["a", "d", "c"]));
        assert!(eq_segments("abc/abc", &["abc", "abc"]));
        assert!(eq_segments("abc/abc/", &["abc", "abc"]));
        assert!(eq_segments("///abc///////a", &["abc", "a"]));
        assert!(eq_segments("/////abc/b", &["abc", "b"]));
        assert!(eq_segments("//abc//c////////d", &["abc", "c", "d"]));
    }

    #[test]
    fn multi_segments_match_funky_chars() {
        assert!(eq_segments("a/b/c!!!", &["a", "b", "c!!!"]));
        assert!(eq_segments("a  /b", &["a  ", "b"]));
        assert!(eq_segments("  a/b", &["  a", "b"]));
        assert!(eq_segments("  a/b  ", &["  a", "b  "]));
        assert!(eq_segments("  a///b  ", &["  a", "b  "]));
        assert!(eq_segments("  ab  ", &["  ab  "]));
    }

    #[test]
    fn segment_mismatch() {
        assert!(!eq_segments("", &["a"]));
        assert!(!eq_segments("a", &[]));
        assert!(!eq_segments("/a/a", &["a"]));
        assert!(!eq_segments("/a/b", &["b", "a"]));
        assert!(!eq_segments("/a/a/b", &["a", "b"]));
        assert!(!eq_segments("///a/", &[]));
    }
}
<commit_msg>Add fragment support to URI type.<commit_after>//! Borrowed and owned string types for absolute URIs.
//!

use std::cell::Cell;
use std::convert::From;
use std::fmt::{self, Write};

use router::Collider;

// TODO: Reconsider deriving PartialEq and Eq to make "//a/b" == "/a/b".
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct URI<'a> {
    uri: &'a str,
    path: &'a str,
    query: Option<&'a str>,
    fragment: Option<&'a str>,
    segment_count: Cell<Option<usize>>,
}

impl<'a> URI<'a> {
    pub fn new<T: AsRef<str> + ?Sized>(uri: &'a T) -> URI<'a> {
        let uri = uri.as_ref();

        let qmark = uri.find('?');
        let hmark = qmark.map(|i| uri[(i + 1)..].find('#').map(|j| j + i + 1))
            .unwrap_or_else(|| uri.find('#'));

        let (path, query, fragment) = match (qmark, hmark) {
            (Some(i), Some(j)) => (&uri[..i], Some(&uri[(i+1)..j]), Some(&uri[(j+1)..])),
            (Some(i), None) => (&uri[..i], Some(&uri[(i+1)..]), None),
            (None, Some(j)) => (&uri[..j], None, Some(&uri[(j+1)..])),
            (None, None) => (uri, None, None),
        };

        URI {
            segment_count: Cell::new(None),
            uri: uri,
            path: path,
            query: query,
            fragment: fragment,
        }
    }

    #[inline(always)]
    pub fn segment_count(&self) -> usize {
        self.segment_count.get().unwrap_or_else(|| {
            let count = self.segments().count();
            self.segment_count.set(Some(count));
            count
        })
    }

    #[inline(always)]
    pub fn segments(&self) -> Segments<'a> {
        Segments(self.path)
    }

    #[inline(always)]
    pub fn query(&self) -> Option<&'a str> {
        self.query
    }

    #[inline(always)]
    pub fn fragment(&self) -> Option<&'a str> {
        self.fragment
    }

    #[inline(always)]
    pub fn as_str(&self) -> &'a str {
        self.uri
    }
}

impl<'a> fmt::Display for URI<'a> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut last = '\0';
        for c in self.uri.chars() {
            if !(c == '/' && last == '/') {
                f.write_char(c)?;
            }

            last = c;
        }

        Ok(())
    }
}

unsafe impl<'a> Sync for URI<'a> { /* It's safe! */ }

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct URIBuf {
    uri: String,
    segment_count: Cell<Option<usize>>,
}

// I don't like repeating all of this stuff. Is there a better way?
impl URIBuf {
    pub fn segment_count(&self) -> usize {
        self.segment_count.get().unwrap_or_else(|| {
            let count = self.segments().count();
            self.segment_count.set(Some(count));
            count
        })
    }

    #[inline(always)]
    pub fn segments(&self) -> Segments {
        self.as_uri_uncached().segments()
    }

    #[inline(always)]
    fn as_uri_uncached(&self) -> URI {
        URI::new(self.uri.as_str())
    }

    #[inline(always)]
    pub fn as_uri(&self) -> URI {
        let mut uri = URI::new(self.uri.as_str());
        uri.segment_count = self.segment_count.clone();
        uri
    }

    #[inline(always)]
    pub fn as_str(&self) -> &str {
        self.uri.as_str()
    }

    #[inline(always)]
    pub fn to_string(&self) -> String {
        self.uri.clone()
    }
}

unsafe impl Sync for URIBuf { /* It's safe! */ }

impl fmt::Display for URIBuf {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        self.as_uri_uncached().fmt(f)
    }
}

impl From<String> for URIBuf {
    #[inline(always)]
    fn from(uri: String) -> URIBuf {
        URIBuf {
            segment_count: Cell::new(None),
            uri: uri,
        }
    }
}

impl<'a> From<&'a str> for URIBuf {
    #[inline(always)]
    fn from(uri: &'a str) -> URIBuf {
        URIBuf {
            segment_count: Cell::new(None),
            uri: uri.to_string(),
        }
    }
}

impl<'a, 'b> Collider<URI<'b>> for URI<'a> {
    fn collides_with(&self, other: &URI<'b>) -> bool {
        for (seg_a, seg_b) in self.segments().zip(other.segments()) {
            if seg_a.ends_with("..>") || seg_b.ends_with("..>") {
                return true;
            }

            if !seg_a.collides_with(seg_b) {
                return false;
            }
        }

        if self.segment_count() != other.segment_count() {
            return false;
        }

        true
    }
}

#[derive(Clone, Debug)]
pub struct Segments<'a>(&'a str);

impl<'a> Iterator for Segments<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        // Find the start of the next segment (first that's not '/').
        let i = match self.0.find(|c| c != '/') {
            Some(index) => index,
            None => return None,
        };

        // Get the index of the first character that _is_ a '/' after start.
        // j = index of first character after i (hence the i +) that's not a '/'
        let rest = &self.0[i..];
        let j = rest.find('/').map_or(self.0.len(), |j| i + j);

        // Save the result, update the iterator, and return!
        let result = Some(&self.0[i..j]);
        self.0 = &self.0[j..];
        result
    }

    // TODO: Potentially take a second parameter with Option<cached count> and
    // return it here if it's Some. The downside is that a decision has to be
    // made about -when- to compute and cache that count. A place to do it is in
    // the segments() method. But this means that the count will always be
    // computed regardless of whether it's needed. Maybe this is ok. We'll see.
    // fn count(self) -> usize where Self: Sized {
    //     self.1.unwrap_or_else(self.fold(0, |cnt, _| cnt + 1))
    // }
}

#[cfg(test)]
mod tests {
    use super::{URI, URIBuf};

    fn seg_count(path: &str, expected: usize) -> bool {
        let actual = URI::new(path).segment_count();
        let actual_buf = URIBuf::from(path).segment_count();
        if actual != expected || actual_buf != expected {
            trace_!("Count mismatch: expected {}, got {}.", expected, actual);
            trace_!("{}", if actual != expected { "lifetime" } else { "buf" });
            trace_!("Segments (for {}):", path);
            for (i, segment) in URI::new(path).segments().enumerate() {
                trace_!("{}: {}", i, segment);
            }
        }

        actual == expected && actual_buf == expected
    }

    fn eq_segments(path: &str, expected: &[&str]) -> bool {
        let uri = URI::new(path);
        let actual: Vec<&str> = uri.segments().collect();

        let uri_buf = URIBuf::from(path);
        let actual_buf: Vec<&str> = uri_buf.segments().collect();

        actual == expected && actual_buf == expected
    }

    #[test]
    fn simple_segment_count() {
        assert!(seg_count("", 0));
        assert!(seg_count("/", 0));
        assert!(seg_count("a", 1));
        assert!(seg_count("/a", 1));
        assert!(seg_count("a/", 1));
        assert!(seg_count("/a/", 1));
        assert!(seg_count("/a/b", 2));
        assert!(seg_count("/a/b/", 2));
        assert!(seg_count("a/b/", 2));
        assert!(seg_count("ab/", 1));
    }

    #[test]
    fn segment_count() {
        assert!(seg_count("////", 0));
        assert!(seg_count("//a//", 1));
        assert!(seg_count("//abc//", 1));
        assert!(seg_count("//abc/def/", 2));
        assert!(seg_count("//////abc///def//////////", 2));
        assert!(seg_count("a/b/c/d/e/f/g", 7));
        assert!(seg_count("/a/b/c/d/e/f/g", 7));
        assert!(seg_count("/a/b/c/d/e/f/g/", 7));
        assert!(seg_count("/a/b/cdjflk/d/e/f/g", 7));
        assert!(seg_count("//aaflja/b/cdjflk/d/e/f/g", 7));
        assert!(seg_count("/a   /b", 2));
    }

    #[test]
    fn single_segments_match() {
        assert!(eq_segments("", &[]));
        assert!(eq_segments("a", &["a"]));
        assert!(eq_segments("/a", &["a"]));
        assert!(eq_segments("/a/", &["a"]));
        assert!(eq_segments("a/", &["a"]));
        assert!(eq_segments("///a/", &["a"]));
        assert!(eq_segments("///a///////", &["a"]));
        assert!(eq_segments("a///////", &["a"]));
        assert!(eq_segments("//a", &["a"]));
        assert!(eq_segments("", &[]));
        assert!(eq_segments("abc", &["abc"]));
        assert!(eq_segments("/a", &["a"]));
        assert!(eq_segments("/abc/", &["abc"]));
        assert!(eq_segments("abc/", &["abc"]));
        assert!(eq_segments("///abc/", &["abc"]));
        assert!(eq_segments("///abc///////", &["abc"]));
        assert!(eq_segments("abc///////", &["abc"]));
        assert!(eq_segments("//abc", &["abc"]));
    }

    #[test]
    fn multi_segments_match() {
        assert!(eq_segments("a/b/c", &["a", "b", "c"]));
        assert!(eq_segments("/a/b", &["a", "b"]));
        assert!(eq_segments("/a///b", &["a", "b"]));
        assert!(eq_segments("a/b/c/d", &["a", "b", "c", "d"]));
        assert!(eq_segments("///a///////d////c", &["a", "d", "c"]));
        assert!(eq_segments("abc/abc", &["abc", "abc"]));
        assert!(eq_segments("abc/abc/", &["abc", "abc"]));
        assert!(eq_segments("///abc///////a", &["abc", "a"]));
        assert!(eq_segments("/////abc/b", &["abc", "b"]));
        assert!(eq_segments("//abc//c////////d", &["abc", "c", "d"]));
    }

    #[test]
    fn multi_segments_match_funky_chars() {
        assert!(eq_segments("a/b/c!!!", &["a", "b", "c!!!"]));
        assert!(eq_segments("a  /b", &["a  ", "b"]));
        assert!(eq_segments("  a/b", &["  a", "b"]));
        assert!(eq_segments("  a/b  ", &["  a", "b  "]));
        assert!(eq_segments("  a///b  ", &["  a", "b  "]));
        assert!(eq_segments("  ab  ", &["  ab  "]));
    }

    #[test]
    fn segment_mismatch() {
        assert!(!eq_segments("", &["a"]));
        assert!(!eq_segments("a", &[]));
        assert!(!eq_segments("/a/a", &["a"]));
        assert!(!eq_segments("/a/b", &["b", "a"]));
        assert!(!eq_segments("/a/a/b", &["a", "b"]));
        assert!(!eq_segments("///a/", &[]));
    }

    fn test_query(uri: &str, query: Option<&str>) {
        let uri = URI::new(uri);
        assert_eq!(uri.query(), query);
    }

    fn test_fragment(uri: &str, fragment: Option<&str>) {
        let uri = URI::new(uri);
        assert_eq!(uri.fragment(), fragment);
    }

    #[test]
    fn query_does_not_exist() {
        test_query("/test", None);
        test_query("/a/b/c/d/e", None);
        test_query("/////", None);
        test_query("//a///", None);
    }

    #[test]
    fn query_exists() {
        test_query("/test?abc", Some("abc"));
        test_query("/a/b/c?abc", Some("abc"));
        test_query("/a/b/c/d/e/f/g/?abc#hijklmnop", Some("abc"));
        test_query("?123", Some("123"));
        test_query("?", Some(""));
        test_query("/?", Some(""));
        test_query("?#", Some(""));
        test_query("/?hi", Some("hi"));
    }

    #[test]
    fn fragment_exists() {
        test_fragment("/test#abc", Some("abc"));
        test_fragment("/#abc", Some("abc"));
        test_fragment("/a/b/c?123#a", Some("a"));
        test_fragment("/#a", Some("a"));
    }

    #[test]
    fn fragment_does_not_exist() {
        test_fragment("/testabc", None);
        test_fragment("/abc", None);
        test_fragment("/a/b/c?123", None);
        test_fragment("/a", None);
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::ir::*;
use crate::program::Program;
use common::WithLocation;
use std::sync::Arc;

pub trait Transformer {
    const NAME: &'static str;
    const VISIT_ARGUMENTS: bool;
    const VISIT_DIRECTIVES: bool;

    fn transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        self.default_transform_program(program)
    }

    fn default_transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        let mut next_program = Program::new(program.schema());
        let mut has_changes = false;
        for operation in program.operations() {
            match self.transform_operation(operation) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_operation(Arc::clone(operation)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_operation(Arc::new(replacement))
                }
            }
        }
        for fragment in program.fragments() {
            match self.transform_fragment(fragment) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_fragment(Arc::clone(fragment)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_fragment(Arc::new(replacement))
                }
            }
        }
        if has_changes {
            Some(next_program)
        } else {
            None
        }
    }

    // Fragment Definition
    fn transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        self.default_transform_fragment(fragment)
    }

    fn default_transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(FragmentDefinition {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        })
    }

    // Operation Definition
    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        self.default_transform_operation(operation)
    }

    fn default_transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        let selections = self.transform_selections(&operation.selections);
        // Special-case for empty selections
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&operation.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(OperationDefinition {
            directives: directives.unwrap_or_else(|| operation.directives.clone()),
            selections: selections.unwrap_or_else(|| operation.selections.clone()),
            ..operation.clone()
        })
    }

    // Selection
    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        self.transform_list(selections, Self::transform_selection)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        self.default_transform_selection(selection)
    }

    fn default_transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::FragmentSpread(selection) => self
                .transform_fragment_spread(selection)
                .map(Selection::FragmentSpread),
            Selection::InlineFragment(selection) => self
                .transform_inline_fragment(selection)
                .map(Selection::InlineFragment),
            Selection::LinkedField(selection) => self
                .transform_linked_field(selection)
                .map(Selection::LinkedField),
            Selection::ScalarField(selection) => self
                .transform_scalar_field(selection)
                .map(Selection::ScalarField),
            Selection::Condition(selection) => self
                .transform_condition(selection)
                .map(Selection::Condition),
        }
    }

    // Selection Kinds
    fn transform_scalar_field(&mut self, field: &ScalarField) -> Transformed<Arc<ScalarField>> {
        self.default_transform_scalar_field(field)
    }

    fn default_transform_scalar_field(
        &mut self,
        field: &ScalarField,
    ) -> Transformed<Arc<ScalarField>> {
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(ScalarField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            ..field.clone()
        }))
    }

    fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed<Arc<LinkedField>> {
        self.default_transform_linked_field(field)
    }

    fn default_transform_linked_field(
        &mut self,
        field: &LinkedField,
    ) -> Transformed<Arc<LinkedField>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&field.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if selections.is_none() && arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(LinkedField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            selections: selections.unwrap_or_else(|| field.selections.clone()),
            ..field.clone()
        }))
    }

    fn transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        self.default_transform_inline_fragment(fragment)
    }

    fn default_transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(InlineFragment {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        }))
    }

    fn transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        self.default_transform_fragment_spread(spread)
    }
    fn default_transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        let arguments = self.transform_arguments(&spread.arguments);
        let directives = self.transform_directives(&spread.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(FragmentSpread {
            arguments: arguments.unwrap_or_else(|| spread.arguments.clone()),
            directives: directives.unwrap_or_else(|| spread.directives.clone()),
            ..spread.clone()
        }))
    }

    // Conditions
    fn transform_condition(&mut self, condition: &Condition) -> Transformed<Arc<Condition>> {
        self.default_transform_condition(condition)
    }

    fn default_transform_condition(
        &mut self,
        condition: &Condition,
    ) -> Transformed<Arc<Condition>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&condition.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let condition_value = self.transform_condition_value(&condition.value);
        if selections.is_none() && condition_value.is_none() {
            Transformed::Keep
        } else {
            Transformed::Replace(Arc::new(Condition {
                value: condition_value.unwrap_or_else(|| condition.value.clone()),
                selections: selections.unwrap_or_else(|| condition.selections.clone()),
                ..condition.clone()
            }))
        }
    }

    fn transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Option<ConditionValue> {
        if Self::VISIT_ARGUMENTS {
            match self.default_transform_condition_value(condition_value) {
                Transformed::Delete => None,
                Transformed::Keep => Some(condition_value.clone()),
                Transformed::Replace(next_condition_value) => Some(next_condition_value),
            }
        } else {
            None
        }
    }

    fn default_transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Transformed<ConditionValue> {
        match condition_value {
            ConditionValue::Variable(variable) => self
                .transform_variable(variable)
                .map(ConditionValue::Variable),
            ConditionValue::Constant(_) => Transformed::Keep,
        }
    }

    // Directives
    fn transform_directives(&mut self, directives: &[Directive]) -> Option<Vec<Directive>> {
        if Self::VISIT_DIRECTIVES {
            self.transform_list(directives, Self::transform_directive)
        } else {
            None
        }
    }

    fn transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        self.default_transform_directive(directive)
    }

    fn default_transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        let arguments = self.transform_arguments(&directive.arguments);
        match arguments {
            None => Transformed::Keep,
            Some(replacement) => Transformed::Replace(Directive {
                arguments: replacement,
                ..directive.clone()
            }),
        }
    }

    // Arguments
    fn transform_arguments(&mut self, arguments: &[Argument]) -> Option<Vec<Argument>> {
        if Self::VISIT_ARGUMENTS {
            self.transform_list(arguments, Self::transform_argument)
        } else {
            None
        }
    }

    fn transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        self.default_transform_argument(argument)
    }

    fn default_transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        match self.transform_value(&argument.value.item) {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(Argument {
                value: WithLocation::new(argument.value.location, replacement),
                ..argument.clone()
            }),
        }
    }

    // Values
    fn transform_value(&mut self, value: &Value) -> Transformed<Value> {
        self.default_transform_value(value)
    }

    fn default_transform_value(&mut self, value: &Value) -> Transformed<Value> {
        match value {
            Value::Variable(variable) => self.transform_variable(variable).map(Value::Variable),
            Value::Constant(_) => Transformed::Keep,
            Value::List(items) => match self.transform_list(items, Self::transform_value) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::List(replacement)),
            },
            Value::Object(arguments) => match self.transform_arguments(arguments) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::Object(replacement)),
            },
        }
    }

    fn transform_variable(&mut self, value: &Variable) -> Transformed<Variable> {
        let _ = value;
        Transformed::Keep
    }

    // Helpers
    fn transform_list<F, T>(&mut self, list: &[T], f: F) -> Option<Vec<T>>
    where
        F: Fn(&mut Self, &T) -> Transformed<T>,
        T: Clone,
    {
        if list.is_empty() {
            return None;
        }
        let mut result = Vec::new();
        let mut has_changes = false;
        for (index, prev_item) in list.iter().enumerate() {
            let next_item = f(self, prev_item);
            match next_item {
                Transformed::Keep => {
                    if has_changes {
                        result.push(prev_item.clone());
                    }
                }
                Transformed::Delete => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    has_changes = true;
                }
                Transformed::Replace(next_item) => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    result.push(next_item);
                    has_changes = true;
                }
            }
        }
        if has_changes {
            // Note that result can be empty if the input was empty and all items were skipped
            Some(result)
        } else {
            None
        }
    }
}

#[derive(Clone)]
pub enum Transformed<T> {
    Delete,
    Keep,
    Replace(T),
}

impl<T> Transformed<T> {
    pub fn map<F, U>(self, f: F) -> Transformed<U>
    where
        F: FnOnce(T) -> U,
    {
        match self {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(f(replacement)),
        }
    }
}
<commit_msg>Make `default_transform_condition_value` return `Option<ConditionValue>`<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::ir::*;
use crate::program::Program;
use common::WithLocation;
use std::sync::Arc;

pub trait Transformer {
    const NAME: &'static str;
    const VISIT_ARGUMENTS: bool;
    const VISIT_DIRECTIVES: bool;

    fn transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        self.default_transform_program(program)
    }

    fn default_transform_program<'s>(&mut self, program: &Program<'s>) -> Option<Program<'s>> {
        let mut next_program = Program::new(program.schema());
        let mut has_changes = false;
        for operation in program.operations() {
            match self.transform_operation(operation) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_operation(Arc::clone(operation)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_operation(Arc::new(replacement))
                }
            }
        }
        for fragment in program.fragments() {
            match self.transform_fragment(fragment) {
                Transformed::Delete => has_changes = true,
                Transformed::Keep => next_program.insert_fragment(Arc::clone(fragment)),
                Transformed::Replace(replacement) => {
                    has_changes = true;
                    next_program.insert_fragment(Arc::new(replacement))
                }
            }
        }
        if has_changes {
            Some(next_program)
        } else {
            None
        }
    }

    // Fragment Definition
    fn transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        self.default_transform_fragment(fragment)
    }

    fn default_transform_fragment(
        &mut self,
        fragment: &FragmentDefinition,
    ) -> Transformed<FragmentDefinition> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(FragmentDefinition {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        })
    }

    // Operation Definition
    fn transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        self.default_transform_operation(operation)
    }

    fn default_transform_operation(
        &mut self,
        operation: &OperationDefinition,
    ) -> Transformed<OperationDefinition> {
        let selections = self.transform_selections(&operation.selections);
        // Special-case for empty selections
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&operation.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(OperationDefinition {
            directives: directives.unwrap_or_else(|| operation.directives.clone()),
            selections: selections.unwrap_or_else(|| operation.selections.clone()),
            ..operation.clone()
        })
    }

    // Selection
    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        self.transform_list(selections, Self::transform_selection)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        self.default_transform_selection(selection)
    }

    fn default_transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::FragmentSpread(selection) => self
                .transform_fragment_spread(selection)
                .map(Selection::FragmentSpread),
            Selection::InlineFragment(selection) => self
                .transform_inline_fragment(selection)
                .map(Selection::InlineFragment),
            Selection::LinkedField(selection) => self
                .transform_linked_field(selection)
                .map(Selection::LinkedField),
            Selection::ScalarField(selection) => self
                .transform_scalar_field(selection)
                .map(Selection::ScalarField),
            Selection::Condition(selection) => self
                .transform_condition(selection)
                .map(Selection::Condition),
        }
    }

    // Selection Kinds
    fn transform_scalar_field(&mut self, field: &ScalarField) -> Transformed<Arc<ScalarField>> {
        self.default_transform_scalar_field(field)
    }

    fn default_transform_scalar_field(
        &mut self,
        field: &ScalarField,
    ) -> Transformed<Arc<ScalarField>> {
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(ScalarField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            ..field.clone()
        }))
    }

    fn transform_linked_field(&mut self, field: &LinkedField) -> Transformed<Arc<LinkedField>> {
        self.default_transform_linked_field(field)
    }

    fn default_transform_linked_field(
        &mut self,
        field: &LinkedField,
    ) -> Transformed<Arc<LinkedField>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&field.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let arguments = self.transform_arguments(&field.arguments);
        let directives = self.transform_directives(&field.directives);
        if selections.is_none() && arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(LinkedField {
            arguments: arguments.unwrap_or_else(|| field.arguments.clone()),
            directives: directives.unwrap_or_else(|| field.directives.clone()),
            selections: selections.unwrap_or_else(|| field.selections.clone()),
            ..field.clone()
        }))
    }

    fn transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        self.default_transform_inline_fragment(fragment)
    }

    fn default_transform_inline_fragment(
        &mut self,
        fragment: &InlineFragment,
    ) -> Transformed<Arc<InlineFragment>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&fragment.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let directives = self.transform_directives(&fragment.directives);
        if selections.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(InlineFragment {
            directives: directives.unwrap_or_else(|| fragment.directives.clone()),
            selections: selections.unwrap_or_else(|| fragment.selections.clone()),
            ..fragment.clone()
        }))
    }

    fn transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        self.default_transform_fragment_spread(spread)
    }
    fn default_transform_fragment_spread(
        &mut self,
        spread: &FragmentSpread,
    ) -> Transformed<Arc<FragmentSpread>> {
        let arguments = self.transform_arguments(&spread.arguments);
        let directives = self.transform_directives(&spread.directives);
        if arguments.is_none() && directives.is_none() {
            return Transformed::Keep;
        }
        Transformed::Replace(Arc::new(FragmentSpread {
            arguments: arguments.unwrap_or_else(|| spread.arguments.clone()),
            directives: directives.unwrap_or_else(|| spread.directives.clone()),
            ..spread.clone()
        }))
    }

    // Conditions
    fn transform_condition(&mut self, condition: &Condition) -> Transformed<Arc<Condition>> {
        self.default_transform_condition(condition)
    }

    fn default_transform_condition(
        &mut self,
        condition: &Condition,
    ) -> Transformed<Arc<Condition>> {
        // Special-case for empty selections
        let selections = self.transform_selections(&condition.selections);
        if let Some(selections) = &selections {
            if selections.is_empty() {
                return Transformed::Delete;
            }
        }
        let condition_value = self.transform_condition_value(&condition.value);
        if selections.is_none() && condition_value.is_none() {
            Transformed::Keep
        } else {
            Transformed::Replace(Arc::new(Condition {
                value: condition_value.unwrap_or_else(|| condition.value.clone()),
                selections: selections.unwrap_or_else(|| condition.selections.clone()),
                ..condition.clone()
            }))
        }
    }

    fn transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Option<ConditionValue> {
        if Self::VISIT_ARGUMENTS {
            self.default_transform_condition_value(condition_value)
        } else {
            None
        }
    }

    fn default_transform_condition_value(
        &mut self,
        condition_value: &ConditionValue,
    ) -> Option<ConditionValue> {
        match condition_value {
            ConditionValue::Variable(variable) => match self.transform_variable(variable) {
                Transformed::Keep => None,
                Transformed::Delete => unreachable!(
                    "default_transform_condition_value: Unexpected `Delete` from transfrom_variable."
                ),
                Transformed::Replace(next_variable) => {
                    Some(ConditionValue::Variable(next_variable))
                }
            },
            ConditionValue::Constant(_) => None,
        }
    }

    // Directives
    fn transform_directives(&mut self, directives: &[Directive]) -> Option<Vec<Directive>> {
        if Self::VISIT_DIRECTIVES {
            self.transform_list(directives, Self::transform_directive)
        } else {
            None
        }
    }

    fn transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        self.default_transform_directive(directive)
    }

    fn default_transform_directive(&mut self, directive: &Directive) -> Transformed<Directive> {
        let arguments = self.transform_arguments(&directive.arguments);
        match arguments {
            None => Transformed::Keep,
            Some(replacement) => Transformed::Replace(Directive {
                arguments: replacement,
                ..directive.clone()
            }),
        }
    }

    // Arguments
    fn transform_arguments(&mut self, arguments: &[Argument]) -> Option<Vec<Argument>> {
        if Self::VISIT_ARGUMENTS {
            self.transform_list(arguments, Self::transform_argument)
        } else {
            None
        }
    }

    fn transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        self.default_transform_argument(argument)
    }

    fn default_transform_argument(&mut self, argument: &Argument) -> Transformed<Argument> {
        match self.transform_value(&argument.value.item) {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(Argument {
                value: WithLocation::new(argument.value.location, replacement),
                ..argument.clone()
            }),
        }
    }

    // Values
    fn transform_value(&mut self, value: &Value) -> Transformed<Value> {
        self.default_transform_value(value)
    }

    fn default_transform_value(&mut self, value: &Value) -> Transformed<Value> {
        match value {
            Value::Variable(variable) => self.transform_variable(variable).map(Value::Variable),
            Value::Constant(_) => Transformed::Keep,
            Value::List(items) => match self.transform_list(items, Self::transform_value) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::List(replacement)),
            },
            Value::Object(arguments) => match self.transform_arguments(arguments) {
                None => Transformed::Keep,
                Some(replacement) => Transformed::Replace(Value::Object(replacement)),
            },
        }
    }

    fn transform_variable(&mut self, value: &Variable) -> Transformed<Variable> {
        let _ = value;
        Transformed::Keep
    }

    // Helpers
    fn transform_list<F, T>(&mut self, list: &[T], f: F) -> Option<Vec<T>>
    where
        F: Fn(&mut Self, &T) -> Transformed<T>,
        T: Clone,
    {
        if list.is_empty() {
            return None;
        }
        let mut result = Vec::new();
        let mut has_changes = false;
        for (index, prev_item) in list.iter().enumerate() {
            let next_item = f(self, prev_item);
            match next_item {
                Transformed::Keep => {
                    if has_changes {
                        result.push(prev_item.clone());
                    }
                }
                Transformed::Delete => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    has_changes = true;
                }
                Transformed::Replace(next_item) => {
                    if !has_changes {
                        debug_assert!(result.capacity() == 0);
                        // assume most items won't be skipped and allocate space for all items
                        result.reserve(list.len());
                        result.extend(list.iter().take(index).cloned());
                    }
                    result.push(next_item);
                    has_changes = true;
                }
            }
        }
        if has_changes {
            // Note that result can be empty if the input was empty and all items were skipped
            Some(result)
        } else {
            None
        }
    }
}

#[derive(Clone)]
pub enum Transformed<T> {
    Delete,
    Keep,
    Replace(T),
}

impl<T> Transformed<T> {
    pub fn map<F, U>(self, f: F) -> Transformed<U>
    where
        F: FnOnce(T) -> U,
    {
        match self {
            Transformed::Delete => Transformed::Delete,
            Transformed::Keep => Transformed::Keep,
            Transformed::Replace(replacement) => Transformed::Replace(f(replacement)),
        }
    }
}
<|endoftext|>