<commit_before>
use BuildQuery;

// https://docs.gitlab.com/ce/api/projects.html

// Get a list of projects for which the authenticated user is a member.
// GET /projects
//
// Get a list of projects which the authenticated user can see.
// GET /projects/visible
//
// Get a list of projects which are owned by the authenticated user.
// GET /projects/owned
//
// Get a list of projects which are starred by the authenticated user.
// GET /projects/starred
//
// Get a list of all GitLab projects (admin only).
// GET /projects/all
//
// Get a specific project, identified by project `ID` or `NAMESPACE/PROJECT_NAME`, which is owned by the authenticated user. If using namespaced projects call make sure that the `NAMESPACE/PROJECT_NAME` is URL-encoded, eg. `/api/v3/projects/diaspora%2Fdiaspora` (where `/` is represented by `%2F`).
// GET /projects/:id
//
// Get the events for the specified project. Sorted from newest to oldest
// GET /projects/:id/events
//
// Creates a new project owned by the authenticated user.
// POST /projects
//
// Creates a new project owned by the specified user. Available only for admins.
// POST /projects/user/:user_id
//
// Updates an existing project
// PUT /projects/:id
//
// Forks a project into the user namespace of the authenticated user or the one provided.
// POST /projects/fork/:id
//
// Stars a given project. Returns status code `201` and the project on success and `304` if the project is already starred.
// POST /projects/:id/star
//
// Unstars a given project. Returns status code `200` and the project on success and `304` if the project is not starred.
// DELETE /projects/:id/star
//
// Archives the project if the user is either admin or the project owner of this project. This action is idempotent, thus archiving an already archived project will not change the project.
//
// Status code `201` with the project as body is given when successful, in case the user doesn't have the proper access rights, code `403` is returned. Status `404` is returned if the project doesn't exist, or is hidden to the user.
// POST /projects/:id/archive
//
//
// Unarchives the project if the user is either admin or the project owner of this project. This action is idempotent, thus unarchiving an non-archived project will not change the project.
//
// Status code `201` with the project as body is given when successful, in case the user doesn't have the proper access rights, code `403` is returned. Status `404` is returned if the project doesn't exist, or is hidden to the user.
// POST /projects/:id/unarchive
//
//
// Removes a project including all associated resources (issues, merge requests etc.)
// DELETE /projects/:id
//
//
// Uploads a file to the specified project to be used in an issue or merge request description, or a comment.
// POST /projects/:id/uploads
//
//
// Allow to share project with group.
// POST /projects/:id/share
//
//
// Get a list of project hooks.
// GET /projects/:id/hooks
//
//
// Get a specific hook for a project.
// GET /projects/:id/hooks/:hook_id
//
//
// Adds a hook to a specified project.
// POST /projects/:id/hooks
//
//
// Edits a hook for a specified project.
// PUT /projects/:id/hooks/:hook_id
//
//
// Removes a hook from a project. This is an idempotent method and can be called multiple times. Either the hook is available or not.
// DELETE /projects/:id/hooks/:hook_id
//
//
// Lists all branches of a project.
// GET /projects/:id/repository/branches
//
//
// A specific branch of a project.
// GET /projects/:id/repository/branches/:branch
//
//
// Protects a single branch of a project.
// PUT /projects/:id/repository/branches/:branch/protect
//
//
// Unprotects a single branch of a project.
// PUT /projects/:id/repository/branches/:branch/unprotect
//
//
// Create a forked from/to relation between existing projects.
// POST /projects/:id/fork/:forked_from_id
//
//
// Delete an existing forked from relationship
// DELETE /projects/:id/fork
//
//
// Search for projects by name which are accessible to the authenticated user.
// GET /projects/search/:query
<commit_msg>Added projets structs<commit_after>
use BuildQuery;

// https://docs.gitlab.com/ce/api/projects.html

// Get a list of projects for which the authenticated user is a member.
// GET /projects
//
// Get a list of projects which the authenticated user can see.
// GET /projects/visible
//
// Get a list of projects which are owned by the authenticated user.
// GET /projects/owned
//
// Get a list of projects which are starred by the authenticated user.
// GET /projects/starred
//
// Get a list of all GitLab projects (admin only).
// GET /projects/all
//
// Get a specific project, identified by project `ID` or `NAMESPACE/PROJECT_NAME`, which is owned by the authenticated user. If using namespaced projects call make sure that the `NAMESPACE/PROJECT_NAME` is URL-encoded, eg. `/api/v3/projects/diaspora%2Fdiaspora` (where `/` is represented by `%2F`).
// GET /projects/:id
//
// Get the events for the specified project. Sorted from newest to oldest
// GET /projects/:id/events
//
// Creates a new project owned by the authenticated user.
// POST /projects
//
// Creates a new project owned by the specified user. Available only for admins.
// POST /projects/user/:user_id
//
// Updates an existing project
// PUT /projects/:id
//
// Forks a project into the user namespace of the authenticated user or the one provided.
// POST /projects/fork/:id
//
// Stars a given project. Returns status code `201` and the project on success and `304` if the project is already starred.
// POST /projects/:id/star
//
// Unstars a given project. Returns status code `200` and the project on success and `304` if the project is not starred.
// DELETE /projects/:id/star
//
// Archives the project if the user is either admin or the project owner of this project. This action is idempotent, thus archiving an already archived project will not change the project.
//
// Status code `201` with the project as body is given when successful, in case the user doesn't have the proper access rights, code `403` is returned. Status `404` is returned if the project doesn't exist, or is hidden to the user.
// POST /projects/:id/archive
//
//
// Unarchives the project if the user is either admin or the project owner of this project. This action is idempotent, thus unarchiving an non-archived project will not change the project.
//
// Status code `201` with the project as body is given when successful, in case the user doesn't have the proper access rights, code `403` is returned. Status `404` is returned if the project doesn't exist, or is hidden to the user.
// POST /projects/:id/unarchive
//
//
// Removes a project including all associated resources (issues, merge requests etc.)
// DELETE /projects/:id
//
//
// Uploads a file to the specified project to be used in an issue or merge request description, or a comment.
// POST /projects/:id/uploads
//
//
// Allow to share project with group.
// POST /projects/:id/share
//
//
// Get a list of project hooks.
// GET /projects/:id/hooks
//
//
// Get a specific hook for a project.
// GET /projects/:id/hooks/:hook_id
//
//
// Adds a hook to a specified project.
// POST /projects/:id/hooks
//
//
// Edits a hook for a specified project.
// PUT /projects/:id/hooks/:hook_id
//
//
// Removes a hook from a project. This is an idempotent method and can be called multiple times. Either the hook is available or not.
// DELETE /projects/:id/hooks/:hook_id
//
//
// Lists all branches of a project.
// GET /projects/:id/repository/branches
//
//
// A specific branch of a project.
// GET /projects/:id/repository/branches/:branch
//
//
// Protects a single branch of a project.
// PUT /projects/:id/repository/branches/:branch/protect
//
//
// Unprotects a single branch of a project.
// PUT /projects/:id/repository/branches/:branch/unprotect
//
//
// Create a forked from/to relation between existing projects.
// POST /projects/:id/fork/:forked_from_id
//
//
// Delete an existing forked from relationship
// DELETE /projects/:id/fork
//
//
// Search for projects by name which are accessible to the authenticated user.
// GET /projects/search/:query

// https://docs.gitlab.com/ce/api/projects.html#list-projects

#[derive(Debug, Copy, Clone)]
pub enum ListingVisibility {
    Public,
    Internal,
    Private,
}

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

#[derive(Debug, Copy, Clone)]
pub enum ListingSort {
    Asc,
    Desc,
}

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


<commit_before>#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate semver;
extern crate clap;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagref;
extern crate libimagerror;
extern crate libimagentrylist;

mod ui;
use ui::build_ui;

fn main() {
    println!("Hello, world!");
}
<commit_msg>Add main()<commit_after>#[macro_use] extern crate log;
#[macro_use] extern crate version;
extern crate semver;
extern crate clap;

extern crate libimagstore;
extern crate libimagrt;
extern crate libimagref;
extern crate libimagerror;
extern crate libimagentrylist;

mod ui;
use ui::build_ui;

fn main() {
    let rt = generate_runtime_setup("imag-ref",
                                    &version!()[..],
                                    "Reference files outside of the store",
                                    build_ui);
    rt.cli()
        .subcommand_name()
        .map(|name| {
            debug!("Call: {}", name);
            match name {
                "add"    => add(&rt),
                "remove" => remove(&rt),
                "list"   => list(&rt),
                _        => {
                    debug!("Unknown command"); // More error handling
                },
            };
        });
}

fn add(rt: &Runtime) {
    unimplemented!()
}

fn remove(rt: &Runtime) {
    unimplemented!()
}

fn list(rt: &Runtime) {
    unimplemented!()
}

<|endoftext|>


<commit_before>//! way-cooler registry.

use std::ops::Deref;
use std::cmp::Eq;
use std::fmt::Display;
use std::hash::Hash;
use std::borrow::Borrow;
use std::collections::hash_map::{HashMap, Entry};
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};

use rustc_serialize::Decodable;
use rustc_serialize::json::{Json, ToJson, Decoder, DecoderError};

mod types;
mod commands;
pub use self::types::*; // Export constants too

#[cfg(test)]
mod tests;

pub type RegMap = HashMap<String, RegistryField>;

lazy_static! {
    /// Registry variable for the registry
    static ref REGISTRY: RwLock<RegMap> = RwLock::new(HashMap::new());
}

/// Error types that can happen
#[derive(Debug, PartialEq)]
pub enum RegistryError {
    /// The registry key was not found
    KeyNotFound,
    /// The registry key was of the wrong type
    WrongKeyType,
    /// Attempting to set a readonly value
    InvalidOperation,
    /// The object couldn't be converted to/from Json
    DecoderError(DecoderError)
}

/// Result type of gets/sets to the registry
pub type RegistryResult<T> = Result<T, RegistryError>;

/// Initialize the registry (register default commands)
pub fn init() {
    commands::register_defaults();
}

/// Acquires a read lock on the registry.
pub fn read_lock<'a>() -> RwLockReadGuard<'a, RegMap> {
    REGISTRY.read().expect("Unable to read from registry!")
}

/// Acquires a write lock on the registry.
pub fn write_lock<'a>() -> RwLockWriteGuard<'a, RegMap> {
    REGISTRY.write().expect("Unable to write to registry!")
}

/// Gets a RegistryField enum with the specified name
pub fn get_field<K>(name: &K) -> Option<RegistryField>
    where String: Borrow<K>, K: Hash + Eq + Display {
    // clone() will either clone an Arc or an Arc+AccessFlags
    let result = read_lock().get(name).map(|v| v.clone());
    trace!("get: {} => {:?}", name, &result);
    return result;
}

/// Attempts to get a command from the registry.
pub fn get_command<K>(name: &K) -> RegistryResult<CommandFn>
    where String: Borrow<K>, K: Hash + Eq + Display {
    get_field(name).ok_or(RegistryError::KeyNotFound)
        .and_then(|val| match val {
        RegistryField::Command(com) => Ok(com),
        _ => Err(RegistryError::WrongKeyType)
    })
}

/// Gets a data type from the registry, returning a reference to the property
/// method if the field is a property.
pub fn get_data<K>(name: &K) -> RegistryResult<RegistryGetData>
where String: Borrow<K>, K: Hash + Eq + Display {
    get_field(name).ok_or(RegistryError::KeyNotFound)
        .and_then(|val| match val {
            RegistryField::Object { flags, data } =>
                Ok(RegistryGetData::Object(flags, data)),
            RegistryField::Property { get: maybe_get, .. } =>
                match maybe_get {
                    Some(get) => Ok(RegistryGetData::Property(get)),
                    None => Err(RegistryError::InvalidOperation)
                },
            _ => Err(RegistryError::WrongKeyType)
        })
}

/// Get a Rust structure from the registry
#[allow(dead_code)]
pub fn get_struct<K, T>(name: &K) -> RegistryResult<(AccessFlags, T)>
where String: Borrow <K>, K: Hash + Eq + Display, T: Decodable {
    get_json(name).and_then(|(flags, json)|
        T::decode(&mut Decoder::new(json.deref().clone()))
                            .map_err(|e| RegistryError::DecoderError(e))
                            .map(|data| (flags, data)))
}

/// Get Json data from the registry, evaluating if a property was found.
pub fn get_json<K>(name: &K) -> RegistryResult<(AccessFlags, Arc<Json>)>
where String: Borrow<K>, K: Hash + Eq + Display {
    get_data(name).map(|val| val.resolve())
}

/// Set a value to the given registry field.
pub fn set_field(key: String, value: RegistryField)
                 -> RegistryResult<Option<RegistryField>> {
    let mut reg = write_lock();
    match reg.entry(key) {
        Entry::Occupied(mut entry) => {
            let first_type = entry.get().get_type();
            if first_type.can_set_from(value.get_type()) {
                Ok(Some(entry.insert(value)))
            }
            else {
                Err(RegistryError::WrongKeyType)
            }
        },
        Entry::Vacant(vacancy) => {
            vacancy.insert(value);
            Ok(None)
        }
    }
}

/// Set a command to the given Command
#[allow(dead_code)]
pub fn set_command(key: String, command: CommandFn)
                   -> RegistryResult<Option<CommandFn>> {
    set_field(key, RegistryField::Command(command))
        .map(|maybe_field|
              maybe_field.and_then(|field|
                                    field.get_command()))
}

/// Set a value to the given JSON value.
#[allow(dead_code)]
pub fn set_json(key: String, flags: AccessFlags, json: Json)
                -> RegistryResult<Option<(AccessFlags, Arc<Json>)>> {
    let mut reg = write_lock();
    match reg.entry(key) {
        Entry::Vacant(vacancy) => {
            vacancy.insert(RegistryField::Object {
                flags: flags, data: Arc::new(json)
            });
            return Ok(None);
        },
        Entry::Occupied(mut entry) => {
            let first_type = entry.get().get_type();
            if first_type == FieldType::Object {
                return Ok(entry.insert(RegistryField::Object {
                    flags: flags, data: Arc::new(json)
                }).as_object());
            }
            else if first_type == FieldType::Property {
                match entry.get().clone().as_property_set() {
                    Some(func) => {
                        func(json);
                        return Ok(None);
                    }
                    None => return Err(RegistryError::InvalidOperation)
                }
            }
            else {
                return Err(RegistryError::WrongKeyType);
            }
        }
    }
    return Ok(None);
}

/// Set an object/property in the registry to a value using a ToJson.
#[allow(dead_code)]
pub fn set_struct<T: ToJson>(key: String, flags: AccessFlags, value: T)
                             -> RegistryResult<Option<(AccessFlags, Arc<Json>)>> {
    set_json(key, flags, value.to_json())
}

/// Sets a value for a given Json value, optionally returning the associated property.
#[allow(dead_code)]
pub fn set_with_property(key: String, flags: AccessFlags, json: Json)
                         -> RegistryResult<Option<(Json, SetFn)>> {
    let mut reg = write_lock();
    match reg.entry(key) {
        Entry::Vacant(vacancy) => {
            vacancy.insert(RegistryField::Object {
                flags: flags, data: Arc::new(json)
            });
            Ok(None)
        },
        Entry::Occupied(mut entry) => {
            let first_type = entry.get().get_type();
            if first_type == FieldType::Object {
                entry.insert(RegistryField::Object {
                    flags: flags, data: Arc::new(json)
                });
                Ok(None)
            }
            else if first_type == FieldType::Property {
                Ok(Some((json, entry.get().clone().as_property_set()
                   .expect("set_json_property: checked value of field type"))))
            }
            else {
                Err(RegistryError::WrongKeyType)
            }
        }
    }
}

/// Binds properties to a field of the registry
#[allow(dead_code)]
pub fn set_property_field(key: String, get_fn: Option<GetFn>, set_fn: Option<SetFn>)
                          -> RegistryResult<Option<RegistryField>> {
    set_field(key, RegistryField::Property { get: get_fn, set: set_fn })
}

/// Whether this map contains a key of any type
#[allow(dead_code)]
pub fn contains_key<K>(key: &K) -> bool
where String: Borrow<K>, K: Hash + Eq + Display {
    trace!("contains_key: {}", *key);
    let read_reg = read_lock();
    read_reg.contains_key(key)
}

/// Whether the registry contains a key of some type:
/// None: no key
/// true: same type key
/// false: different type key
#[allow(dead_code)]
pub fn contains_key_of<K>(key: &K, field_type: FieldType) -> Option<bool>
where String: Borrow<K>, K: Hash + Eq + Display {
    let read_reg = read_lock();
    read_reg.get(key).map(|field| field.get_type() == field_type)
}
<commit_msg>Remove ParserError from RegistryError<commit_after>//! way-cooler registry.

use std::ops::Deref;
use std::cmp::Eq;
use std::fmt::Display;
use std::hash::Hash;
use std::borrow::Borrow;
use std::collections::hash_map::{HashMap, Entry};
use std::sync::{Arc, RwLock, RwLockReadGuard, RwLockWriteGuard};

use rustc_serialize::Decodable;
use rustc_serialize::json::{Json, ToJson, Decoder, DecoderError};

mod types;
mod commands;
pub use self::types::*; // Export constants too

#[cfg(test)]
mod tests;

pub type RegMap = HashMap<String, RegistryField>;

lazy_static! {
    /// Registry variable for the registry
    static ref REGISTRY: RwLock<RegMap> = RwLock::new(HashMap::new());
}

/// Error types that can happen
#[derive(Debug, PartialEq)]
pub enum RegistryError {
    /// The registry key was not found
    KeyNotFound,
    /// The registry key was of the wrong type
    WrongKeyType,
    /// Attempting to set a readonly value
    InvalidOperation,
}

/// Result type of gets/sets to the registry
pub type RegistryResult<T> = Result<T, RegistryError>;

/// Initialize the registry (register default commands)
pub fn init() {
    commands::register_defaults();
}

/// Acquires a read lock on the registry.
pub fn read_lock<'a>() -> RwLockReadGuard<'a, RegMap> {
    REGISTRY.read().expect("Unable to read from registry!")
}

/// Acquires a write lock on the registry.
pub fn write_lock<'a>() -> RwLockWriteGuard<'a, RegMap> {
    REGISTRY.write().expect("Unable to write to registry!")
}

/// Gets a RegistryField enum with the specified name
pub fn get_field<K>(name: &K) -> Option<RegistryField>
    where String: Borrow<K>, K: Hash + Eq + Display {
    // clone() will either clone an Arc or an Arc+AccessFlags
    let result = read_lock().get(name).map(|v| v.clone());
    trace!("get: {} => {:?}", name, &result);
    return result;
}

/// Attempts to get a command from the registry.
pub fn get_command<K>(name: &K) -> RegistryResult<CommandFn>
    where String: Borrow<K>, K: Hash + Eq + Display {
    get_field(name).ok_or(RegistryError::KeyNotFound)
        .and_then(|val| match val {
        RegistryField::Command(com) => Ok(com),
        _ => Err(RegistryError::WrongKeyType)
    })
}

/// Gets a data type from the registry, returning a reference to the property
/// method if the field is a property.
pub fn get_data<K>(name: &K) -> RegistryResult<RegistryGetData>
where String: Borrow<K>, K: Hash + Eq + Display {
    get_field(name).ok_or(RegistryError::KeyNotFound)
        .and_then(|val| match val {
            RegistryField::Object { flags, data } =>
                Ok(RegistryGetData::Object(flags, data)),
            RegistryField::Property { get: maybe_get, .. } =>
                match maybe_get {
                    Some(get) => Ok(RegistryGetData::Property(get)),
                    None => Err(RegistryError::InvalidOperation)
                },
            _ => Err(RegistryError::WrongKeyType)
        })
}

/// Get a Rust structure from the registry
#[allow(dead_code)]
    pub fn get_struct<K, T>(name: &K)
                        -> RegistryResult<(AccessFlags, Result<T, DecoderError>)>
where String: Borrow <K>, K: Hash + Eq + Display, T: Decodable {
    get_json(name).map(|(flags, json)|
        (flags, T::decode(&mut Decoder::new(json.deref().clone()))))
}

/// Get Json data from the registry, evaluating if a property was found.
pub fn get_json<K>(name: &K) -> RegistryResult<(AccessFlags, Arc<Json>)>
where String: Borrow<K>, K: Hash + Eq + Display {
    get_data(name).map(|val| val.resolve())
}

/// Set a value to the given registry field.
pub fn set_field(key: String, value: RegistryField)
                 -> RegistryResult<Option<RegistryField>> {
    let mut reg = write_lock();
    match reg.entry(key) {
        Entry::Occupied(mut entry) => {
            let first_type = entry.get().get_type();
            if first_type.can_set_from(value.get_type()) {
                Ok(Some(entry.insert(value)))
            }
            else {
                Err(RegistryError::WrongKeyType)
            }
        },
        Entry::Vacant(vacancy) => {
            vacancy.insert(value);
            Ok(None)
        }
    }
}

/// Set a command to the given Command
#[allow(dead_code)]
pub fn set_command(key: String, command: CommandFn)
                   -> RegistryResult<Option<CommandFn>> {
    set_field(key, RegistryField::Command(command))
        .map(|maybe_field|
              maybe_field.and_then(|field|
                                    field.get_command()))
}

/// Set a value to the given JSON value.
#[allow(dead_code)]
pub fn set_json(key: String, flags: AccessFlags, json: Json)
                -> RegistryResult<Option<(AccessFlags, Arc<Json>)>> {
    let mut reg = write_lock();
    match reg.entry(key) {
        Entry::Vacant(vacancy) => {
            vacancy.insert(RegistryField::Object {
                flags: flags, data: Arc::new(json)
            });
            return Ok(None);
        },
        Entry::Occupied(mut entry) => {
            let first_type = entry.get().get_type();
            if first_type == FieldType::Object {
                return Ok(entry.insert(RegistryField::Object {
                    flags: flags, data: Arc::new(json)
                }).as_object());
            }
            else if first_type == FieldType::Property {
                match entry.get().clone().as_property_set() {
                    Some(func) => {
                        func(json);
                        return Ok(None);
                    }
                    None => return Err(RegistryError::InvalidOperation)
                }
            }
            else {
                return Err(RegistryError::WrongKeyType);
            }
        }
    }
    return Ok(None);
}

/// Set an object/property in the registry to a value using a ToJson.
#[allow(dead_code)]
pub fn set_struct<T: ToJson>(key: String, flags: AccessFlags, value: T)
                             -> RegistryResult<Option<(AccessFlags, Arc<Json>)>> {
    set_json(key, flags, value.to_json())
}

/// Sets a value for a given Json value, optionally returning the associated property.
#[allow(dead_code)]
pub fn set_with_property(key: String, flags: AccessFlags, json: Json)
                         -> RegistryResult<Option<(Json, SetFn)>> {
    let mut reg = write_lock();
    match reg.entry(key) {
        Entry::Vacant(vacancy) => {
            vacancy.insert(RegistryField::Object {
                flags: flags, data: Arc::new(json)
            });
            Ok(None)
        },
        Entry::Occupied(mut entry) => {
            let first_type = entry.get().get_type();
            if first_type == FieldType::Object {
                entry.insert(RegistryField::Object {
                    flags: flags, data: Arc::new(json)
                });
                Ok(None)
            }
            else if first_type == FieldType::Property {
                Ok(Some((json, entry.get().clone().as_property_set()
                   .expect("set_json_property: checked value of field type"))))
            }
            else {
                Err(RegistryError::WrongKeyType)
            }
        }
    }
}

/// Binds properties to a field of the registry
#[allow(dead_code)]
pub fn set_property_field(key: String, get_fn: Option<GetFn>, set_fn: Option<SetFn>)
                          -> RegistryResult<Option<RegistryField>> {
    set_field(key, RegistryField::Property { get: get_fn, set: set_fn })
}

/// Whether this map contains a key of any type
#[allow(dead_code)]
pub fn contains_key<K>(key: &K) -> bool
where String: Borrow<K>, K: Hash + Eq + Display {
    trace!("contains_key: {}", *key);
    let read_reg = read_lock();
    read_reg.contains_key(key)
}

/// Whether the registry contains a key of some type:
/// None: no key
/// true: same type key
/// false: different type key
#[allow(dead_code)]
pub fn contains_key_of<K>(key: &K, field_type: FieldType) -> Option<bool>
where String: Borrow<K>, K: Hash + Eq + Display {
    let read_reg = read_lock();
    read_reg.get(key).map(|field| field.get_type() == field_type)
}
<|endoftext|>


<commit_before>/*
 * Copyright 2015-2016 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Implementation of the ElasticSearch Query DSL.
//!
//! ElasticSearch offers a
//! [rich DSL for searches](https://www.elastic.co/guide/en/elasticsearch/reference/1.x/query-dsl.html).
//! It is JSON based, and therefore very easy to use and composable if using from a
//! dynamic language (e.g.
//! [Ruby](https://github.com/elastic/elasticsearch-ruby/tree/master/elasticsearch-dsl#features-overview));
//! but Rust, being a staticly-typed language, things are different.  The `rs_es::query`
//! module defines a set of builder objects which can be similarly composed to the same
//! ends.
//!
//! For example:
//!
//! ```rust
//! use rs_es::query::Query;
//!
//! let query = Query::build_bool()
//!     .with_must(vec![Query::build_term("field_a",
//!                                       "value").build(),
//!                     Query::build_range("field_b")
//!                           .with_gte(5)
//!                           .with_lt(10)
//!                           .build()])
//!     .build();
//! ```

use std::collections::BTreeMap;

use rustc_serialize::json::{Json, ToJson};

use serde::{Serialize, Serializer};

use ::json::ShouldSkip;
use ::util::StrJoin;

#[macro_use]
mod common;

pub mod compound;
pub mod full_text;
pub mod functions;
pub mod geo;
pub mod joining;
pub mod specialized;
pub mod term;

// Miscellaneous types required by queries go here

// Enums

/// Minimum should match - used in numerous queries
/// TODO: should go somewhere specific
#[derive(Debug)]
pub struct CombinationMinimumShouldMatch {
    first: MinimumShouldMatch,
    second: MinimumShouldMatch
}

impl CombinationMinimumShouldMatch {
    pub fn new(first: A, second: B) -> CombinationMinimumShouldMatch
        where A: Into<MinimumShouldMatch>,
              B: Into<MinimumShouldMatch>
    {
        CombinationMinimumShouldMatch {
            first:  first.into(),
            second: second.into()
        }
    }
}

impl ToString for CombinationMinimumShouldMatch {
    fn to_string(&self) -> String {
        format!("{}<{}", self.first.to_string(), self.second.to_string())
    }
}

impl ToJson for CombinationMinimumShouldMatch {
    fn to_json(&self) -> Json {
        Json::String(self.to_string())
    }
}

impl Serialize for CombinationMinimumShouldMatch {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {
        self.to_string().serialize(serializer)
    }
}

#[derive(Debug)]
pub enum MinimumShouldMatch {
    Integer(i64),
    Percentage(f64),
    Combination(Box<CombinationMinimumShouldMatch>),
    MultipleCombination(Vec<CombinationMinimumShouldMatch>),
    LowHigh(i64, i64)
}

from!(i64, MinimumShouldMatch, Integer);
from!(f64, MinimumShouldMatch, Percentage);
from_exp!(CombinationMinimumShouldMatch,
          MinimumShouldMatch,
          from,
          MinimumShouldMatch::Combination(Box::new(from)));
from!(Vec<CombinationMinimumShouldMatch>, MinimumShouldMatch, MultipleCombination);
from_exp!((i64, i64),
          MinimumShouldMatch,
          from,
          MinimumShouldMatch::LowHigh(from.0, from.1));

impl ToString for MinimumShouldMatch {
    fn to_string(&self) -> String {
        match self {
            &MinimumShouldMatch::Integer(val) => val.to_string(),
            &MinimumShouldMatch::Percentage(val) => {
                format!("{}%", val)
            },
            _ => panic!("Can't convert {:?} to String", self)
        }
    }
}

impl Serialize for MinimumShouldMatch {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {
        match self {
            &MinimumShouldMatch::Integer(val) => val.serialize(serializer),
            &MinimumShouldMatch::Percentage(_) => self.to_string().serialize(serializer),
            &MinimumShouldMatch::Combination(ref comb) => comb.serialize(serializer),
            &MinimumShouldMatch::MultipleCombination(ref combs) => {
                combs.iter().map(|c| c.to_string()).join(" ").serialize(serializer)
            },
            &MinimumShouldMatch::LowHigh(low, high) => {
                let mut d = BTreeMap::new();
                d.insert("low_freq", low);
                d.insert("high_freq", high);
                d.serialize(serializer)
            }
        }
    }
}

/// Fuzziness
#[derive(Debug)]
pub enum Fuzziness {
    Auto,
    LevenshteinDistance(i64),
    Proportionate(f64)
}

from!(i64, Fuzziness, LevenshteinDistance);
from!(f64, Fuzziness, Proportionate);

impl Serialize for Fuzziness {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {

        use self::Fuzziness::*;
        match self {
            &Auto => "auto".serialize(serializer),
            &LevenshteinDistance(dist) => dist.serialize(serializer),
            &Proportionate(p) => p.serialize(serializer)
        }
    }
}

// Flags

/// Flags - multiple operations can take a set of flags, each set is dependent
/// on the operation in question, but they're all formatted to a similar looking
/// String
#[derive(Debug)]
pub struct Flags(Vec)
    where A: AsRef<str>;

impl Serialize for Flags
    where A: AsRef<str> {

    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {

        self.0.iter().join("|").serialize(serializer)
    }
}

impl From<Vec> for Flags
    where A: AsRef<str> {

    fn from(from: Vec) -> Self {
        Flags(from)
    }
}

/// ScoreMode
#[derive(Debug)]
pub enum ScoreMode {
    Multiply,
    Sum,
    Avg,
    First,
    Max,
    Min
}

impl Serialize for ScoreMode {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {
        match self {
            &ScoreMode::Multiply => "multiply".serialize(serializer),
            &ScoreMode::Sum => "sum".serialize(serializer),
            &ScoreMode::Avg => "avg".serialize(serializer),
            &ScoreMode::First => "first".serialize(serializer),
            &ScoreMode::Max => "max".serialize(serializer),
            &ScoreMode::Min => "min".serialize(serializer)
        }
    }
}

/// Query represents all available queries
///
/// Each value is boxed as Queries can be recursive, they also vary
/// significantly in size

// TODO: Filters and Queries are merged, ensure all filters are included in this enum
#[derive(Debug, Serialize)]
pub enum Query {
    // TODO - uncomment one-level to re-enable after Serdeification
    #[serde(rename="match_all")]
    MatchAll(Box<MatchAllQuery>),

    // Full-text queries
    #[serde(rename="match")]
    Match(Box<full_text::MatchQuery>),
    #[serde(rename="multi_match")]
    MultiMatch(Box<full_text::MultiMatchQuery>),
    #[serde(rename="common")]
    Common(Box<full_text::CommonQuery>),
    #[serde(rename="query_string")]
    QueryString(Box<full_text::QueryStringQuery>),
    #[serde(rename="simple_query_string")]
    SimpleQueryString(Box<full_text::SimpleQueryStringQuery>),

    // // Term level queries
    #[serde(rename="term")]
    Term(Box<term::TermQuery>),
    #[serde(rename="terms")]
    Terms(Box<term::TermsQuery>),
    #[serde(rename="range")]
    Range(Box<term::RangeQuery>),
    #[serde(rename="exists")]
    Exists(Box<term::ExistsQuery>),
    // // Not implementing the Missing query, as it's deprecated, use `must_not` and `Exists`
    // // instead
    #[serde(rename="prefix")]
    Prefix(Box<term::PrefixQuery>),
    #[serde(rename="wildcard")]
    Wildcard(Box<term::WildcardQuery>),
    #[serde(rename="regexp")]
    Regexp(Box<term::RegexpQuery>),
    #[serde(rename="fuzzy")]
    Fuzzy(Box<term::FuzzyQuery>),
    #[serde(rename="type")]
    Type(Box<term::TypeQuery>),
    #[serde(rename="ids")]
    Ids(Box<term::IdsQuery>),

    // Compound queries
    #[serde(rename="constant_score")]
    ConstantScore(Box<compound::ConstantScoreQuery>),
    #[serde(rename="bool")]
    Bool(Box<compound::BoolQuery>),
    #[serde(rename="dis_max")]
    DisMax(Box<compound::DisMaxQuery>),
    #[serde(rename="function_score")]
    FunctionScore(Box<compound::FunctionScoreQuery>),
    #[serde(rename="boosting")]
    Boosting(Box<compound::BoostingQuery>),
    #[serde(rename="indices")]
    Indices(Box<compound::IndicesQuery>),
    // // Not implementing the And query, as it's deprecated, use `bool` instead.
    // // Not implementing the Not query, as it's deprecated
    // // Not implementing the Or query, as it's deprecated, use `bool` instead.
    // // Not implementing the Filtered query, as it's deprecated.
    // // Not implementing the Limit query, as it's deprecated.

    // Joining queries
    Nested(Box<joining::NestedQuery>),
    HasChild(Box<joining::HasChildQuery>),
    HasParent(Box<joining::HasParentQuery>),

    // Geo queries
    GeoShape(Box<geo::GeoShapeQuery>),
    GeoBoundingBox(Box<geo::GeoBoundingBoxQuery>),
    GeoDistance(Box<geo::GeoDistanceQuery>),
    // TODO: implement me - pending changes to range query
    //GeoDistanceRange(Box<geo::GeoDistanceRangeQuery>)
    GeoPolygon(Box<geo::GeoPolygonQuery>),
    GeohashCell(Box<geo::GeohashCellQuery>),

    // // Specialized queries
    // MoreLikeThis(Box<specialized::MoreLikeThisQuery>),
    // // TODO: template queries
    // // TODO: Search by script

    // // Span queries
    // // TODO: SpanTerm(Box<term::TermQuery>),
    // // TODO: Span multi term query
    // // TODO: Span first query
    // // TODO: Span near query
    // // TODO: Span or query
    // // TODO: Span not query
    // // TODO: Span containing query
    // // TODO: Span within query
}

impl Default for Query {
    fn default() -> Query {
        // TODO - put this back
        //Query::MatchAll(Default::default())
        Query::build_terms("test_idx").build()
    }
}

// Specific query types go here

/// Match all query

#[derive(Debug, Default, Serialize)]
pub struct MatchAllQuery {
    #[serde(skip_serializing_if="ShouldSkip::should_skip")]
    boost: Option<f64>,
}

impl Query {
    pub fn build_match_all() -> MatchAllQuery {
        MatchAllQuery::default()
    }
}

impl MatchAllQuery {
    add_option!(with_boost, boost, f64);

    build!(MatchAll);
}

#[cfg(test)]
mod tests {
    // TODO: replace with Serde
    extern crate rustc_serialize;

    extern crate serde_json;

    use rustc_serialize::json::ToJson;

    use super::{Flags, Query};
    use super::full_text::SimpleQueryStringFlags;
    use super::functions::Function;
    use super::term::TermsQueryLookup;

    #[test]
    fn test_simple_query_string_flags() {
        let opts = vec![SimpleQueryStringFlags::And, SimpleQueryStringFlags::Not];
        let flags:Flags<SimpleQueryStringFlags> = opts.into();
        let json = serde_json::to_string(&flags);
        assert_eq!("\"AND|NOT\"", json.unwrap());
    }

    #[test]
    fn test_terms_query() {
        let terms_query = Query::build_terms("field_name")
            .with_values(vec!["a", "b", "c"])
            .build();
        assert_eq!("{\"terms\":{\"field_name\":[\"a\",\"b\",\"c\"]}}",
                   serde_json::to_string(&terms_query).unwrap());

        let terms_query_2 = Query::build_terms("field_name")
            .with_values(["a", "b", "c"].as_ref())
            .build();
        assert_eq!("{\"terms\":{\"field_name\":[\"a\",\"b\",\"c\"]}}",
                   serde_json::to_string(&terms_query_2).unwrap());

        let terms_query_3 = Query::build_terms("field_name")
            .with_values(TermsQueryLookup::new(123, "blah.de.blah").with_index("other"))
            .build();
        assert_eq!("{\"terms\":{\"field_name\":{\"id\":123,\"index\":\"other\",\"path\":\"blah.de.blah\"}}}",
                   serde_json::to_string(&terms_query_3).unwrap());
    }

    // TODO - re-enable
    // #[test]
    // fn test_function_score_query() {
    //     let function_score_query = Query::build_function_score()
    //         .with_function(Function::build_script_score("this_is_a_script")
    //                        .with_lang("made_up")
    //                        .add_param("A", 12)
    //                        .build())
    //         .build();
    //     assert_eq!("{\"function_score\":{\"functions\":[{\"script_score\":{\"inline\":\"this_is_a_script\",\"lang\":\"made_up\",\"params\":{\"A\":12}}}]}}",
    //                function_score_query.to_json().to_string());
    // }
}
<commit_msg>Re-selecting default query<commit_after>/*
 * Copyright 2015-2016 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//! Implementation of the ElasticSearch Query DSL.
//!
//! ElasticSearch offers a
//! [rich DSL for searches](https://www.elastic.co/guide/en/elasticsearch/reference/1.x/query-dsl.html).
//! It is JSON based, and therefore very easy to use and composable if using from a
//! dynamic language (e.g.
//! [Ruby](https://github.com/elastic/elasticsearch-ruby/tree/master/elasticsearch-dsl#features-overview));
//! but Rust, being a staticly-typed language, things are different.  The `rs_es::query`
//! module defines a set of builder objects which can be similarly composed to the same
//! ends.
//!
//! For example:
//!
//! ```rust
//! use rs_es::query::Query;
//!
//! let query = Query::build_bool()
//!     .with_must(vec![Query::build_term("field_a",
//!                                       "value").build(),
//!                     Query::build_range("field_b")
//!                           .with_gte(5)
//!                           .with_lt(10)
//!                           .build()])
//!     .build();
//! ```

use std::collections::BTreeMap;

use rustc_serialize::json::{Json, ToJson};

use serde::{Serialize, Serializer};

use ::json::ShouldSkip;
use ::util::StrJoin;

#[macro_use]
mod common;

pub mod compound;
pub mod full_text;
pub mod functions;
pub mod geo;
pub mod joining;
pub mod specialized;
pub mod term;

// Miscellaneous types required by queries go here

// Enums

/// Minimum should match - used in numerous queries
/// TODO: should go somewhere specific
#[derive(Debug)]
pub struct CombinationMinimumShouldMatch {
    first: MinimumShouldMatch,
    second: MinimumShouldMatch
}

impl CombinationMinimumShouldMatch {
    pub fn new(first: A, second: B) -> CombinationMinimumShouldMatch
        where A: Into<MinimumShouldMatch>,
              B: Into<MinimumShouldMatch>
    {
        CombinationMinimumShouldMatch {
            first:  first.into(),
            second: second.into()
        }
    }
}

impl ToString for CombinationMinimumShouldMatch {
    fn to_string(&self) -> String {
        format!("{}<{}", self.first.to_string(), self.second.to_string())
    }
}

impl ToJson for CombinationMinimumShouldMatch {
    fn to_json(&self) -> Json {
        Json::String(self.to_string())
    }
}

impl Serialize for CombinationMinimumShouldMatch {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {
        self.to_string().serialize(serializer)
    }
}

#[derive(Debug)]
pub enum MinimumShouldMatch {
    Integer(i64),
    Percentage(f64),
    Combination(Box<CombinationMinimumShouldMatch>),
    MultipleCombination(Vec<CombinationMinimumShouldMatch>),
    LowHigh(i64, i64)
}

from!(i64, MinimumShouldMatch, Integer);
from!(f64, MinimumShouldMatch, Percentage);
from_exp!(CombinationMinimumShouldMatch,
          MinimumShouldMatch,
          from,
          MinimumShouldMatch::Combination(Box::new(from)));
from!(Vec<CombinationMinimumShouldMatch>, MinimumShouldMatch, MultipleCombination);
from_exp!((i64, i64),
          MinimumShouldMatch,
          from,
          MinimumShouldMatch::LowHigh(from.0, from.1));

impl ToString for MinimumShouldMatch {
    fn to_string(&self) -> String {
        match self {
            &MinimumShouldMatch::Integer(val) => val.to_string(),
            &MinimumShouldMatch::Percentage(val) => {
                format!("{}%", val)
            },
            _ => panic!("Can't convert {:?} to String", self)
        }
    }
}

impl Serialize for MinimumShouldMatch {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {
        match self {
            &MinimumShouldMatch::Integer(val) => val.serialize(serializer),
            &MinimumShouldMatch::Percentage(_) => self.to_string().serialize(serializer),
            &MinimumShouldMatch::Combination(ref comb) => comb.serialize(serializer),
            &MinimumShouldMatch::MultipleCombination(ref combs) => {
                combs.iter().map(|c| c.to_string()).join(" ").serialize(serializer)
            },
            &MinimumShouldMatch::LowHigh(low, high) => {
                let mut d = BTreeMap::new();
                d.insert("low_freq", low);
                d.insert("high_freq", high);
                d.serialize(serializer)
            }
        }
    }
}

/// Fuzziness
#[derive(Debug)]
pub enum Fuzziness {
    Auto,
    LevenshteinDistance(i64),
    Proportionate(f64)
}

from!(i64, Fuzziness, LevenshteinDistance);
from!(f64, Fuzziness, Proportionate);

impl Serialize for Fuzziness {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {

        use self::Fuzziness::*;
        match self {
            &Auto => "auto".serialize(serializer),
            &LevenshteinDistance(dist) => dist.serialize(serializer),
            &Proportionate(p) => p.serialize(serializer)
        }
    }
}

// Flags

/// Flags - multiple operations can take a set of flags, each set is dependent
/// on the operation in question, but they're all formatted to a similar looking
/// String
#[derive(Debug)]
pub struct Flags(Vec)
    where A: AsRef<str>;

impl Serialize for Flags
    where A: AsRef<str> {

    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {

        self.0.iter().join("|").serialize(serializer)
    }
}

impl From<Vec> for Flags
    where A: AsRef<str> {

    fn from(from: Vec) -> Self {
        Flags(from)
    }
}

/// ScoreMode
#[derive(Debug)]
pub enum ScoreMode {
    Multiply,
    Sum,
    Avg,
    First,
    Max,
    Min
}

impl Serialize for ScoreMode {
    fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
        where S: Serializer {
        match self {
            &ScoreMode::Multiply => "multiply".serialize(serializer),
            &ScoreMode::Sum => "sum".serialize(serializer),
            &ScoreMode::Avg => "avg".serialize(serializer),
            &ScoreMode::First => "first".serialize(serializer),
            &ScoreMode::Max => "max".serialize(serializer),
            &ScoreMode::Min => "min".serialize(serializer)
        }
    }
}

/// Query represents all available queries
///
/// Each value is boxed as Queries can be recursive, they also vary
/// significantly in size

// TODO: Filters and Queries are merged, ensure all filters are included in this enum
#[derive(Debug, Serialize)]
pub enum Query {
    // TODO - uncomment one-level to re-enable after Serdeification
    #[serde(rename="match_all")]
    MatchAll(Box<MatchAllQuery>),

    // Full-text queries
    #[serde(rename="match")]
    Match(Box<full_text::MatchQuery>),
    #[serde(rename="multi_match")]
    MultiMatch(Box<full_text::MultiMatchQuery>),
    #[serde(rename="common")]
    Common(Box<full_text::CommonQuery>),
    #[serde(rename="query_string")]
    QueryString(Box<full_text::QueryStringQuery>),
    #[serde(rename="simple_query_string")]
    SimpleQueryString(Box<full_text::SimpleQueryStringQuery>),

    // // Term level queries
    #[serde(rename="term")]
    Term(Box<term::TermQuery>),
    #[serde(rename="terms")]
    Terms(Box<term::TermsQuery>),
    #[serde(rename="range")]
    Range(Box<term::RangeQuery>),
    #[serde(rename="exists")]
    Exists(Box<term::ExistsQuery>),
    // // Not implementing the Missing query, as it's deprecated, use `must_not` and `Exists`
    // // instead
    #[serde(rename="prefix")]
    Prefix(Box<term::PrefixQuery>),
    #[serde(rename="wildcard")]
    Wildcard(Box<term::WildcardQuery>),
    #[serde(rename="regexp")]
    Regexp(Box<term::RegexpQuery>),
    #[serde(rename="fuzzy")]
    Fuzzy(Box<term::FuzzyQuery>),
    #[serde(rename="type")]
    Type(Box<term::TypeQuery>),
    #[serde(rename="ids")]
    Ids(Box<term::IdsQuery>),

    // Compound queries
    #[serde(rename="constant_score")]
    ConstantScore(Box<compound::ConstantScoreQuery>),
    #[serde(rename="bool")]
    Bool(Box<compound::BoolQuery>),
    #[serde(rename="dis_max")]
    DisMax(Box<compound::DisMaxQuery>),
    #[serde(rename="function_score")]
    FunctionScore(Box<compound::FunctionScoreQuery>),
    #[serde(rename="boosting")]
    Boosting(Box<compound::BoostingQuery>),
    #[serde(rename="indices")]
    Indices(Box<compound::IndicesQuery>),
    // // Not implementing the And query, as it's deprecated, use `bool` instead.
    // // Not implementing the Not query, as it's deprecated
    // // Not implementing the Or query, as it's deprecated, use `bool` instead.
    // // Not implementing the Filtered query, as it's deprecated.
    // // Not implementing the Limit query, as it's deprecated.

    // Joining queries
    #[serde(rename="nested")]
    Nested(Box<joining::NestedQuery>),
    #[serde(rename="has_child")]
    HasChild(Box<joining::HasChildQuery>),
    #[serde(rename="has_parent")]
    HasParent(Box<joining::HasParentQuery>),

    // Geo queries
    #[serde(rename="geo_shape")]
    GeoShape(Box<geo::GeoShapeQuery>),
    #[serde(rename="geo_bounding_box")]
    GeoBoundingBox(Box<geo::GeoBoundingBoxQuery>),
    #[serde(rename="geo_distance")]
    GeoDistance(Box<geo::GeoDistanceQuery>),
    // TODO: implement me - pending changes to range query
    //GeoDistanceRange(Box<geo::GeoDistanceRangeQuery>)
    #[serde(rename="geo_polygon")]
    GeoPolygon(Box<geo::GeoPolygonQuery>),
    #[serde(rename="geohash_cell")]
    GeohashCell(Box<geo::GeohashCellQuery>),

    // // Specialized queries
    // MoreLikeThis(Box<specialized::MoreLikeThisQuery>),
    // // TODO: template queries
    // // TODO: Search by script

    // // Span queries
    // // TODO: SpanTerm(Box<term::TermQuery>),
    // // TODO: Span multi term query
    // // TODO: Span first query
    // // TODO: Span near query
    // // TODO: Span or query
    // // TODO: Span not query
    // // TODO: Span containing query
    // // TODO: Span within query
}

impl Default for Query {
    fn default() -> Query {
        Query::MatchAll(Default::default())
    }
}

// Specific query types go here

/// Match all query

#[derive(Debug, Default, Serialize)]
pub struct MatchAllQuery {
    #[serde(skip_serializing_if="ShouldSkip::should_skip")]
    boost: Option<f64>,
}

impl Query {
    pub fn build_match_all() -> MatchAllQuery {
        MatchAllQuery::default()
    }
}

impl MatchAllQuery {
    add_option!(with_boost, boost, f64);

    build!(MatchAll);
}

#[cfg(test)]
mod tests {
    // TODO: replace with Serde
    extern crate rustc_serialize;

    extern crate serde_json;

    use rustc_serialize::json::ToJson;

    use super::{Flags, Query};
    use super::full_text::SimpleQueryStringFlags;
    use super::functions::Function;
    use super::term::TermsQueryLookup;

    #[test]
    fn test_simple_query_string_flags() {
        let opts = vec![SimpleQueryStringFlags::And, SimpleQueryStringFlags::Not];
        let flags:Flags<SimpleQueryStringFlags> = opts.into();
        let json = serde_json::to_string(&flags);
        assert_eq!("\"AND|NOT\"", json.unwrap());
    }

    #[test]
    fn test_terms_query() {
        let terms_query = Query::build_terms("field_name")
            .with_values(vec!["a", "b", "c"])
            .build();
        assert_eq!("{\"terms\":{\"field_name\":[\"a\",\"b\",\"c\"]}}",
                   serde_json::to_string(&terms_query).unwrap());

        let terms_query_2 = Query::build_terms("field_name")
            .with_values(["a", "b", "c"].as_ref())
            .build();
        assert_eq!("{\"terms\":{\"field_name\":[\"a\",\"b\",\"c\"]}}",
                   serde_json::to_string(&terms_query_2).unwrap());

        let terms_query_3 = Query::build_terms("field_name")
            .with_values(TermsQueryLookup::new(123, "blah.de.blah").with_index("other"))
            .build();
        assert_eq!("{\"terms\":{\"field_name\":{\"id\":123,\"index\":\"other\",\"path\":\"blah.de.blah\"}}}",
                   serde_json::to_string(&terms_query_3).unwrap());
    }

    // TODO - re-enable
    // #[test]
    // fn test_function_score_query() {
    //     let function_score_query = Query::build_function_score()
    //         .with_function(Function::build_script_score("this_is_a_script")
    //                        .with_lang("made_up")
    //                        .add_param("A", 12)
    //                        .build())
    //         .build();
    //     assert_eq!("{\"function_score\":{\"functions\":[{\"script_score\":{\"inline\":\"this_is_a_script\",\"lang\":\"made_up\",\"params\":{\"A\":12}}}]}}",
    //                function_score_query.to_json().to_string());
    // }
}
<|endoftext|>


<commit_before>#[cfg(feature = "read")]
use alloc::boxed::Box;
#[cfg(feature = "read")]
use alloc::vec::Vec;
use core::fmt;
use core::mem::MaybeUninit;
use core::ops;
use core::ptr;
use core::slice;

mod sealed {
    // SAFETY: Implementer must not modify the content in storage.
    pub unsafe trait Sealed {
        type Storage;

        fn new_storage() -> Self::Storage;

        fn grow(_storage: &mut Self::Storage, _additional: usize) -> Result<(), CapacityFull> {
            Err(CapacityFull)
        }
    }

    #[derive(Clone, Copy, Debug)]
    pub struct CapacityFull;
}

use sealed::*;

/// Marker trait for types that can be used as backing storage when a growable array type is needed.
///
/// This trait is sealed and cannot be implemented for types outside this crate.
pub trait ArrayLike: Sealed {
    /// Type of the elements being stored.
    type Item;

    #[doc(hidden)]
    fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<Self::Item>];

    #[doc(hidden)]
    fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<Self::Item>];
}

// Use macro since const generics can't be used due to MSRV.
macro_rules! impl_array {
    () => {};
    ($n:literal $($rest:tt)*) => {
        // SAFETY: does not modify the content in storage.
        unsafe impl<T> Sealed for [T; $n] {
            type Storage = [MaybeUninit<T>; $n];

            fn new_storage() -> Self::Storage {
                // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid.
                unsafe { MaybeUninit::uninit().assume_init() }
            }
        }

        impl<T> ArrayLike for [T; $n] {
            type Item = T;

            fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] {
                storage
            }

            fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] {
                storage
            }
        }

        impl_array!($($rest)*);
    }
}

impl_array!(0 1 2 3 4 8 16 32 64 128 192);

#[cfg(feature = "read")]
unsafe impl<T> Sealed for Vec<T> {
    type Storage = Box<[MaybeUninit<T>]>;

    fn new_storage() -> Self::Storage {
        Box::new([])
    }

    fn grow(storage: &mut Self::Storage, additional: usize) -> Result<(), CapacityFull> {
        let mut vec: Vec<_> = core::mem::replace(storage, Box::new([])).into();
        vec.reserve(additional);
        // SAFETY: This is a `Vec` of `MaybeUninit`.
        unsafe { vec.set_len(vec.capacity()) };
        *storage = vec.into_boxed_slice();
        Ok(())
    }
}

#[cfg(feature = "read")]
impl<T> ArrayLike for Vec<T> {
    type Item = T;

    fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] {
        storage
    }

    fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] {
        storage
    }
}

pub(crate) struct ArrayVec {
    storage: A::Storage,
    len: usize,
}

impl ArrayVec {
    pub fn new() -> Self {
        Self {
            storage: A::new_storage(),
            len: 0,
        }
    }

    pub fn clear(&mut self) {
        let ptr: *mut [A::Item] = &mut **self;
        // Set length first so the type invariant is upheld even if `drop_in_place` panicks.
        self.len = 0;
        // SAFETY: `ptr` contains valid elements only and we "forget" them by setting the length.
        unsafe { ptr::drop_in_place(ptr) };
    }

    pub fn try_push(&mut self, value: A::Item) -> Result<(), CapacityFull> {
        let mut storage = A::as_mut_slice(&mut self.storage);
        if self.len >= storage.len() {
            A::grow(&mut self.storage, 1)?;
            storage = A::as_mut_slice(&mut self.storage);
        }

        storage[self.len] = MaybeUninit::new(value);
        self.len += 1;
        Ok(())
    }

    pub fn try_insert(&mut self, index: usize, element: A::Item) -> Result<(), CapacityFull> {
        assert!(index <= self.len);

        let mut storage = A::as_mut_slice(&mut self.storage);
        if self.len >= storage.len() {
            A::grow(&mut self.storage, 1)?;
            storage = A::as_mut_slice(&mut self.storage);
        }

        // SAFETY: storage[index] is filled later.
        unsafe {
            let p = storage.as_mut_ptr().add(index);
            core::ptr::copy(p as *const _, p.add(1), self.len - index);
        }
        storage[index] = MaybeUninit::new(element);
        self.len += 1;
        Ok(())
    }

    pub fn pop(&mut self) -> Option {
        if self.len == 0 {
            None
        } else {
            self.len -= 1;
            // SAFETY: this element is valid and we "forget" it by setting the length.
            Some(unsafe { A::as_slice(&mut self.storage)[self.len].as_ptr().read() })
        }
    }

    pub fn swap_remove(&mut self, index: usize) -> A::Item {
        assert!(self.len > 0);
        A::as_mut_slice(&mut self.storage).swap(index, self.len - 1);
        self.pop().unwrap()
    }
}

#[cfg(feature = "read")]
impl<T> ArrayVec<Vec<T>> {
    pub fn into_vec(mut self) -> Vec<T> {
        let len = core::mem::replace(&mut self.len, 0);
        let storage = core::mem::replace(&mut self.storage, Box::new([]));
        let slice = Box::leak(storage);
        debug_assert!(len <= slice.len());
        // SAFETY: valid elements.
        unsafe { Vec::from_raw_parts(slice.as_ptr() as _, len, slice.len()) }
    }
}

impl Drop for ArrayVec {
    fn drop(&mut self) {
        self.clear();
    }
}

impl Default for ArrayVec {
    fn default() -> Self {
        Self::new()
    }
}

impl ops::Deref for ArrayVec {
    type Target = [A::Item];

    fn deref(&self) -> &[A::Item] {
        let slice = &A::as_slice(&self.storage);
        debug_assert!(self.len <= slice.len());
        // SAFETY: valid elements.
        unsafe { slice::from_raw_parts(slice.as_ptr() as _, self.len) }
    }
}

impl ops::DerefMut for ArrayVec {
    fn deref_mut(&mut self) -> &mut [A::Item] {
        let slice = &mut A::as_mut_slice(&mut self.storage);
        debug_assert!(self.len <= slice.len());
        // SAFETY: valid elements.
        unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as _, self.len) }
    }
}

impl Clone for ArrayVec
where
    A::Item: Clone,
{
    fn clone(&self) -> Self {
        let mut new = Self::default();
        for value in &**self {
            new.try_push(value.clone()).unwrap();
        }
        new
    }
}

impl PartialEq for ArrayVec
where
    A::Item: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        **self == **other
    }
}

impl Eq for ArrayVec where A::Item: Eq {}

impl fmt::Debug for ArrayVec
where
    A::Item: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}
<commit_msg>Make Vec from a pointer that is valid for mutation (#614)<commit_after>#[cfg(feature = "read")]
use alloc::boxed::Box;
#[cfg(feature = "read")]
use alloc::vec::Vec;
use core::fmt;
use core::mem::MaybeUninit;
use core::ops;
use core::ptr;
use core::slice;

mod sealed {
    // SAFETY: Implementer must not modify the content in storage.
    pub unsafe trait Sealed {
        type Storage;

        fn new_storage() -> Self::Storage;

        fn grow(_storage: &mut Self::Storage, _additional: usize) -> Result<(), CapacityFull> {
            Err(CapacityFull)
        }
    }

    #[derive(Clone, Copy, Debug)]
    pub struct CapacityFull;
}

use sealed::*;

/// Marker trait for types that can be used as backing storage when a growable array type is needed.
///
/// This trait is sealed and cannot be implemented for types outside this crate.
pub trait ArrayLike: Sealed {
    /// Type of the elements being stored.
    type Item;

    #[doc(hidden)]
    fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<Self::Item>];

    #[doc(hidden)]
    fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<Self::Item>];
}

// Use macro since const generics can't be used due to MSRV.
macro_rules! impl_array {
    () => {};
    ($n:literal $($rest:tt)*) => {
        // SAFETY: does not modify the content in storage.
        unsafe impl<T> Sealed for [T; $n] {
            type Storage = [MaybeUninit<T>; $n];

            fn new_storage() -> Self::Storage {
                // SAFETY: An uninitialized `[MaybeUninit<_>; _]` is valid.
                unsafe { MaybeUninit::uninit().assume_init() }
            }
        }

        impl<T> ArrayLike for [T; $n] {
            type Item = T;

            fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] {
                storage
            }

            fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] {
                storage
            }
        }

        impl_array!($($rest)*);
    }
}

impl_array!(0 1 2 3 4 8 16 32 64 128 192);

#[cfg(feature = "read")]
unsafe impl<T> Sealed for Vec<T> {
    type Storage = Box<[MaybeUninit<T>]>;

    fn new_storage() -> Self::Storage {
        Box::new([])
    }

    fn grow(storage: &mut Self::Storage, additional: usize) -> Result<(), CapacityFull> {
        let mut vec: Vec<_> = core::mem::replace(storage, Box::new([])).into();
        vec.reserve(additional);
        // SAFETY: This is a `Vec` of `MaybeUninit`.
        unsafe { vec.set_len(vec.capacity()) };
        *storage = vec.into_boxed_slice();
        Ok(())
    }
}

#[cfg(feature = "read")]
impl<T> ArrayLike for Vec<T> {
    type Item = T;

    fn as_slice(storage: &Self::Storage) -> &[MaybeUninit<T>] {
        storage
    }

    fn as_mut_slice(storage: &mut Self::Storage) -> &mut [MaybeUninit<T>] {
        storage
    }
}

pub(crate) struct ArrayVec {
    storage: A::Storage,
    len: usize,
}

impl ArrayVec {
    pub fn new() -> Self {
        Self {
            storage: A::new_storage(),
            len: 0,
        }
    }

    pub fn clear(&mut self) {
        let ptr: *mut [A::Item] = &mut **self;
        // Set length first so the type invariant is upheld even if `drop_in_place` panicks.
        self.len = 0;
        // SAFETY: `ptr` contains valid elements only and we "forget" them by setting the length.
        unsafe { ptr::drop_in_place(ptr) };
    }

    pub fn try_push(&mut self, value: A::Item) -> Result<(), CapacityFull> {
        let mut storage = A::as_mut_slice(&mut self.storage);
        if self.len >= storage.len() {
            A::grow(&mut self.storage, 1)?;
            storage = A::as_mut_slice(&mut self.storage);
        }

        storage[self.len] = MaybeUninit::new(value);
        self.len += 1;
        Ok(())
    }

    pub fn try_insert(&mut self, index: usize, element: A::Item) -> Result<(), CapacityFull> {
        assert!(index <= self.len);

        let mut storage = A::as_mut_slice(&mut self.storage);
        if self.len >= storage.len() {
            A::grow(&mut self.storage, 1)?;
            storage = A::as_mut_slice(&mut self.storage);
        }

        // SAFETY: storage[index] is filled later.
        unsafe {
            let p = storage.as_mut_ptr().add(index);
            core::ptr::copy(p as *const _, p.add(1), self.len - index);
        }
        storage[index] = MaybeUninit::new(element);
        self.len += 1;
        Ok(())
    }

    pub fn pop(&mut self) -> Option {
        if self.len == 0 {
            None
        } else {
            self.len -= 1;
            // SAFETY: this element is valid and we "forget" it by setting the length.
            Some(unsafe { A::as_slice(&mut self.storage)[self.len].as_ptr().read() })
        }
    }

    pub fn swap_remove(&mut self, index: usize) -> A::Item {
        assert!(self.len > 0);
        A::as_mut_slice(&mut self.storage).swap(index, self.len - 1);
        self.pop().unwrap()
    }
}

#[cfg(feature = "read")]
impl<T> ArrayVec<Vec<T>> {
    pub fn into_vec(mut self) -> Vec<T> {
        let len = core::mem::replace(&mut self.len, 0);
        let storage = core::mem::replace(&mut self.storage, Box::new([]));
        let slice = Box::leak(storage);
        debug_assert!(len <= slice.len());
        // SAFETY: valid elements.
        unsafe { Vec::from_raw_parts(slice.as_mut_ptr() as *mut T, len, slice.len()) }
    }
}

impl Drop for ArrayVec {
    fn drop(&mut self) {
        self.clear();
    }
}

impl Default for ArrayVec {
    fn default() -> Self {
        Self::new()
    }
}

impl ops::Deref for ArrayVec {
    type Target = [A::Item];

    fn deref(&self) -> &[A::Item] {
        let slice = &A::as_slice(&self.storage);
        debug_assert!(self.len <= slice.len());
        // SAFETY: valid elements.
        unsafe { slice::from_raw_parts(slice.as_ptr() as _, self.len) }
    }
}

impl ops::DerefMut for ArrayVec {
    fn deref_mut(&mut self) -> &mut [A::Item] {
        let slice = &mut A::as_mut_slice(&mut self.storage);
        debug_assert!(self.len <= slice.len());
        // SAFETY: valid elements.
        unsafe { slice::from_raw_parts_mut(slice.as_mut_ptr() as _, self.len) }
    }
}

impl Clone for ArrayVec
where
    A::Item: Clone,
{
    fn clone(&self) -> Self {
        let mut new = Self::default();
        for value in &**self {
            new.try_push(value.clone()).unwrap();
        }
        new
    }
}

impl PartialEq for ArrayVec
where
    A::Item: PartialEq,
{
    fn eq(&self, other: &Self) -> bool {
        **self == **other
    }
}

impl Eq for ArrayVec where A::Item: Eq {}

impl fmt::Debug for ArrayVec
where
    A::Item: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        fmt::Debug::fmt(&**self, f)
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright 2018-2019 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::{
    fmt,
    sync::{Arc, Mutex, RwLock},
    time::Duration,
};

use futures::{
    future::{self, Either},
    Future,
};

use tokio_executor::{DefaultExecutor, Executor};
use tokio_timer::Timeout;

use crate::error::{self, ConnectionReason};

type WorkFn<T, A> =
    dyn Fn(&T, A) -> Box<dyn Future<Item = (), Error = error::Error> + Send> + Send + Sync;
type ConnFn<T> = dyn Fn() -> Box<dyn Future<Item = T, Error = error::Error> + Send> + Send + Sync;

pub(crate) struct Reconnect<A, T> {
    state: Arc<RwLock<ReconnectState<T>>>,

    work_fn: Arc<WorkFn<T, A>>,
    conn_fn: Arc<ConnFn<T>>,
}

impl<A, T> fmt::Debug for Reconnect<A, T>
where
    T: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Reconnect")
            .field("state", &self.state)
            .field("work_fn", &String::from("REDACTED"))
            .field("conn_fn", &String::from("REDACTED"))
            .finish()
    }
}

pub(crate) fn reconnect<A, T, W, C>(
    w: W,
    c: C,
) -> impl Future<Item = Reconnect<A, T>, Error = error::Error>
where
    A: Send + 'static,
    W: Fn(&T, A) -> Box<dyn Future<Item = (), Error = error::Error> + Send> + Send + Sync + 'static,
    C: Fn() -> Box<dyn Future<Item = T, Error = error::Error> + Send> + Send + Sync + 'static,
    T: Clone + Send + Sync + 'static,
{
    let r = Reconnect {
        state: Arc::new(RwLock::new(ReconnectState::NotConnected)),

        work_fn: Arc::new(w),
        conn_fn: Arc::new(c),
    };
    r.reconnect().map(|()| r)
}

enum ReconnectState<T> {
    NotConnected,
    Connected(T),
    ConnectionFailed(Mutex<Option<error::Error>>),
    Connecting,
}

impl<T> fmt::Debug for ReconnectState<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ReconnectState::")?;
        match self {
            NotConnected => write!(f, "NotConnected"),
            Connected(_) => write!(f, "Connected"),
            ConnectionFailed(_) => write!(f, "ConnectionFailed"),
            Connecting => write!(f, "Connecting"),
        }
    }
}

use self::ReconnectState::*;

const CONNECTION_TIMEOUT_SECONDS: u64 = 10;
const CONNECTION_TIMEOUT: Duration = Duration::from_secs(CONNECTION_TIMEOUT_SECONDS);

impl<A, T> Reconnect<A, T>
where
    A: Send + 'static,
    T: Clone + Send + Sync + 'static,
{
    fn call_work(&self, t: &T, a: A) -> impl Future<Item = (), Error = error::Error> {
        let reconnect = self.clone();
        (self.work_fn)(t, a).map_err(move |e| {
            match e {
                error::Error::IO(_) | error::Error::Unexpected(_) => {
                    log::error!("Error in work_fn will force connection closed, next command will attempt to re-establish it: {}", e);
                    reconnect.disconnect();
                    reconnect.reconnect_spawn();
                }
                _ => ()
            }
            e
        })
    }

    // Called when a bad situation has been discovered, force the connections to re-connect.
    fn disconnect(&self) {
        let mut state = self.state.write().expect("Cannot obtain a write lock");
        *state = NotConnected;
    }

    pub(crate) fn do_work(&self, a: A) -> impl Future<Item = (), Error = error::Error> {
        let rv = {
            let state = self.state.read().expect("Cannot obtain read lock");
            match *state {
                NotConnected => Either::B(future::err(error::Error::Connection(
                    ConnectionReason::NotConnected,
                ))),
                Connected(ref t) => return Either::A(self.call_work(t, a)),
                ConnectionFailed(ref e) => {
                    let mut lock = e.lock().expect("Poisioned lock");
                    let e = match lock.take() {
                        Some(e) => e,
                        None => error::Error::Connection(ConnectionReason::NotConnected),
                    };
                    Either::B(future::err(e))
                }
                Connecting => {
                    return Either::B(future::err(error::Error::Connection(
                        ConnectionReason::Connecting,
                    )));
                }
            }
        };
        self.reconnect_spawn();
        rv
    }

    /// Returns a future that completes when the connection is established or failed to establish
    /// used only for timing.
    fn reconnect(&self) -> impl Future<Item = (), Error = error::Error> {
        let mut state = self.state.write().expect("Cannot obtain write lock");

        log::info!("Attempting to reconnect, current state: {:?}", *state);

        match *state {
            Connected(_) => {
                return Either::B(future::err(error::Error::Connection(
                    ConnectionReason::Connected,
                )));
            }
            Connecting => {
                return Either::B(future::err(error::Error::Connection(
                    ConnectionReason::Connecting,
                )));
            }
            NotConnected | ConnectionFailed(_) => (),
        }
        *state = ReconnectState::Connecting;

        let reconnect = self.clone();
        let connect_f = Timeout::new((self.conn_fn)(), CONNECTION_TIMEOUT).map_err(|e| {
            if e.is_inner() {
                e.into_inner().unwrap()
            } else if e.is_elapsed() {
                error::internal(format!(
                    "Connection timed-out after {} seconds",
                    CONNECTION_TIMEOUT_SECONDS
                ))
            } else if e.is_inner() {
                error::internal(format!("Error timing-out connection: {}", e))
            } else {
                unreachable!("A surprise fourth type of timer error has occurred")
            }
        });
        let connect_f = connect_f.then(move |t| {
            let mut state = reconnect.state.write().expect("Cannot obtain write lock");
            match *state {
                NotConnected | Connecting => match t {
                    Ok(t) => {
                        log::info!("Connection established");
                        *state = Connected(t);
                        Ok(())
                    }
                    Err(e) => {
                        log::error!("Connection cannot be established: {}", e);
                        *state = ConnectionFailed(Mutex::new(Some(e)));
                        Err(error::Error::Connection(ConnectionReason::ConnectionFailed))
                    }
                },
                ConnectionFailed(_) => {
                    panic!("The connection state wasn't reset before connecting")
                }
                Connected(_) => panic!("A connected state shouldn't be attempting to reconnect"),
            }
        });

        Either::A(connect_f)
    }

    fn reconnect_spawn(&self) {
        let reconnect_f = self
            .reconnect()
            .map_err(|e| log::error!("Error asynchronously reconnecting: {}", e));

        let mut executor = DefaultExecutor::current();
        executor
            .spawn(Box::new(reconnect_f))
            .expect("Cannot spawn asynchronous reconnection");
    }
}

impl<A, T> Clone for Reconnect<A, T> {
    fn clone(&self) -> Self {
        Reconnect {
            state: self.state.clone(),
            work_fn: self.work_fn.clone(),
            conn_fn: self.conn_fn.clone(),
        }
    }
}
<commit_msg>Fix typo in checking for timeout errors in connection attempts.<commit_after>/*
 * Copyright 2018-2019 Ben Ashford
 *
 * Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
 * http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
 * <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
 * option. This file may not be copied, modified, or distributed
 * except according to those terms.
 */

use std::{
    fmt,
    sync::{Arc, Mutex, RwLock},
    time::Duration,
};

use futures::{
    future::{self, Either},
    Future,
};

use tokio_executor::{DefaultExecutor, Executor};
use tokio_timer::Timeout;

use crate::error::{self, ConnectionReason};

type WorkFn<T, A> =
    dyn Fn(&T, A) -> Box<dyn Future<Item = (), Error = error::Error> + Send> + Send + Sync;
type ConnFn<T> = dyn Fn() -> Box<dyn Future<Item = T, Error = error::Error> + Send> + Send + Sync;

pub(crate) struct Reconnect<A, T> {
    state: Arc<RwLock<ReconnectState<T>>>,

    work_fn: Arc<WorkFn<T, A>>,
    conn_fn: Arc<ConnFn<T>>,
}

impl<A, T> fmt::Debug for Reconnect<A, T>
where
    T: fmt::Debug,
{
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.debug_struct("Reconnect")
            .field("state", &self.state)
            .field("work_fn", &String::from("REDACTED"))
            .field("conn_fn", &String::from("REDACTED"))
            .finish()
    }
}

pub(crate) fn reconnect<A, T, W, C>(
    w: W,
    c: C,
) -> impl Future<Item = Reconnect<A, T>, Error = error::Error>
where
    A: Send + 'static,
    W: Fn(&T, A) -> Box<dyn Future<Item = (), Error = error::Error> + Send> + Send + Sync + 'static,
    C: Fn() -> Box<dyn Future<Item = T, Error = error::Error> + Send> + Send + Sync + 'static,
    T: Clone + Send + Sync + 'static,
{
    let r = Reconnect {
        state: Arc::new(RwLock::new(ReconnectState::NotConnected)),

        work_fn: Arc::new(w),
        conn_fn: Arc::new(c),
    };
    r.reconnect().map(|()| r)
}

enum ReconnectState<T> {
    NotConnected,
    Connected(T),
    ConnectionFailed(Mutex<Option<error::Error>>),
    Connecting,
}

impl<T> fmt::Debug for ReconnectState<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "ReconnectState::")?;
        match self {
            NotConnected => write!(f, "NotConnected"),
            Connected(_) => write!(f, "Connected"),
            ConnectionFailed(_) => write!(f, "ConnectionFailed"),
            Connecting => write!(f, "Connecting"),
        }
    }
}

use self::ReconnectState::*;

const CONNECTION_TIMEOUT_SECONDS: u64 = 10;
const CONNECTION_TIMEOUT: Duration = Duration::from_secs(CONNECTION_TIMEOUT_SECONDS);

impl<A, T> Reconnect<A, T>
where
    A: Send + 'static,
    T: Clone + Send + Sync + 'static,
{
    fn call_work(&self, t: &T, a: A) -> impl Future<Item = (), Error = error::Error> {
        let reconnect = self.clone();
        (self.work_fn)(t, a).map_err(move |e| {
            match e {
                error::Error::IO(_) | error::Error::Unexpected(_) => {
                    log::error!("Error in work_fn will force connection closed, next command will attempt to re-establish it: {}", e);
                    reconnect.disconnect();
                    reconnect.reconnect_spawn();
                }
                _ => ()
            }
            e
        })
    }

    // Called when a bad situation has been discovered, force the connections to re-connect.
    fn disconnect(&self) {
        let mut state = self.state.write().expect("Cannot obtain a write lock");
        *state = NotConnected;
    }

    pub(crate) fn do_work(&self, a: A) -> impl Future<Item = (), Error = error::Error> {
        let rv = {
            let state = self.state.read().expect("Cannot obtain read lock");
            match *state {
                NotConnected => Either::B(future::err(error::Error::Connection(
                    ConnectionReason::NotConnected,
                ))),
                Connected(ref t) => return Either::A(self.call_work(t, a)),
                ConnectionFailed(ref e) => {
                    let mut lock = e.lock().expect("Poisioned lock");
                    let e = match lock.take() {
                        Some(e) => e,
                        None => error::Error::Connection(ConnectionReason::NotConnected),
                    };
                    Either::B(future::err(e))
                }
                Connecting => {
                    return Either::B(future::err(error::Error::Connection(
                        ConnectionReason::Connecting,
                    )));
                }
            }
        };
        self.reconnect_spawn();
        rv
    }

    /// Returns a future that completes when the connection is established or failed to establish
    /// used only for timing.
    fn reconnect(&self) -> impl Future<Item = (), Error = error::Error> {
        let mut state = self.state.write().expect("Cannot obtain write lock");

        log::info!("Attempting to reconnect, current state: {:?}", *state);

        match *state {
            Connected(_) => {
                return Either::B(future::err(error::Error::Connection(
                    ConnectionReason::Connected,
                )));
            }
            Connecting => {
                return Either::B(future::err(error::Error::Connection(
                    ConnectionReason::Connecting,
                )));
            }
            NotConnected | ConnectionFailed(_) => (),
        }
        *state = ReconnectState::Connecting;

        let reconnect = self.clone();
        let connect_f = Timeout::new((self.conn_fn)(), CONNECTION_TIMEOUT).map_err(|e| {
            if e.is_inner() {
                e.into_inner().unwrap()
            } else if e.is_elapsed() {
                error::internal(format!(
                    "Connection timed-out after {} seconds",
                    CONNECTION_TIMEOUT_SECONDS
                ))
            } else if e.is_timer() {
                error::internal(format!("Error timing-out connection: {}", e))
            } else {
                unreachable!("A surprise fourth type of timer error has occurred")
            }
        });
        let connect_f = connect_f.then(move |t| {
            let mut state = reconnect.state.write().expect("Cannot obtain write lock");
            match *state {
                NotConnected | Connecting => match t {
                    Ok(t) => {
                        log::info!("Connection established");
                        *state = Connected(t);
                        Ok(())
                    }
                    Err(e) => {
                        log::error!("Connection cannot be established: {}", e);
                        *state = ConnectionFailed(Mutex::new(Some(e)));
                        Err(error::Error::Connection(ConnectionReason::ConnectionFailed))
                    }
                },
                ConnectionFailed(_) => {
                    panic!("The connection state wasn't reset before connecting")
                }
                Connected(_) => panic!("A connected state shouldn't be attempting to reconnect"),
            }
        });

        Either::A(connect_f)
    }

    fn reconnect_spawn(&self) {
        let reconnect_f = self
            .reconnect()
            .map_err(|e| log::error!("Error asynchronously reconnecting: {}", e));

        let mut executor = DefaultExecutor::current();
        executor
            .spawn(Box::new(reconnect_f))
            .expect("Cannot spawn asynchronous reconnection");
    }
}

impl<A, T> Clone for Reconnect<A, T> {
    fn clone(&self) -> Self {
        Reconnect {
            state: self.state.clone(),
            work_fn: self.work_fn.clone(),
            conn_fn: self.conn_fn.clone(),
        }
    }
}
<|endoftext|>


<commit_before>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;
use spinlock::{Spinlock, SpinlockGuard};

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.spawn(f)
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {
    // switch stack so we can park current thread

    call_on_stack(inner, current_rsp, scheduler_stack_top());

    fn inner(current_rsp: uint) -> ! {
        // park current thread
        let current = Thread::from_rsp(current_rsp);
        global().scheduler.park(current);

        schedule_inner();
    }
}

pub unsafe fn schedule() -> ! {
    call_on_stack(inner, scheduler_stack_top());

    unsafe fn call_on_stack(function: fn() -> !, stack_top: uint) -> ! {
        asm!("call $0;" :: "r"(function), "{rsp}"(stack_top) :: "intel", "volatile");
        panic!("diverging fn returned");
    }

    fn inner() -> ! {
        // TODO FIXME kill current thread and deallocate stack
        schedule_inner()
    }
}

fn schedule_inner() -> ! {
    #[allow(improper_ctypes)]
    extern {
        fn pop_registers_and_iret(rsp: uint) -> !;
    }

    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        unsafe{::enable_interrupts()};
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }

    // schedule new thread
    let new_thread = unsafe{global().scheduler.schedule().unwrap_or_default()};

    match new_thread {
        Thread{state: ThreadState::Active{rsp}} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        Thread{state: ThreadState::New{function}} => {
            println!("new");
            let new_stack_top = new_stack();
            unsafe{call_on_stack(invoke, function, new_stack_top)}
        },
    }
}

fn scheduler_stack_top() -> uint {
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    &SCHEDULER_STACK as *const [u8; 4096] as uint + SCHEDULER_STACK_SIZE
}

unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
        : "intel", "volatile");
    panic!("diverging fn returned");
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

struct NonInterruptableSpinlock<T>(Spinlock<T>);

impl<T> NonInterruptableSpinlock<T> {
    pub fn lock(&self) -> SpinlockGuard<T> {
        unsafe{::disable_interrupts()};
        self.0.lock()
    }
}

pub struct GlobalScheduler {
    threads: NonInterruptableSpinlock<RingBuf<Thread>>,
}

impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        GlobalScheduler{
            threads: NonInterruptableSpinlock(Spinlock::new(RingBuf::new())),
        }
    }

    fn spawn<F, R>(&self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.lock().push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        Future/*::from_receiver(rx)*/
    }

    fn park(&self, thread: Thread) {
        self.threads.lock().push_back(thread)
    }

    unsafe fn schedule(&self) -> Option<Thread> {
        self.threads.lock().pop_front()
    }
}<commit_msg>enable interrupts after spawning again<commit_after>use core::prelude::*;
use core::default::Default;
use boxed::Box;
use fn_box::FnBox;
use collections::RingBuf;
use global::global;
use alloc::heap::allocate;
use spinlock::{Spinlock, SpinlockGuard};

pub fn spawn<F, R>(f:F) -> Future<R> where F: FnOnce()->R, F:Send {
    global().scheduler.spawn(f)
}

pub unsafe fn reschedule(current_rsp: uint) -> ! {
    // switch stack so we can park current thread

    call_on_stack(inner, current_rsp, scheduler_stack_top());

    fn inner(current_rsp: uint) -> ! {
        // park current thread
        let current = Thread::from_rsp(current_rsp);
        global().scheduler.park(current);

        schedule_inner();
    }
}

pub unsafe fn schedule() -> ! {
    call_on_stack(inner, scheduler_stack_top());

    unsafe fn call_on_stack(function: fn() -> !, stack_top: uint) -> ! {
        asm!("call $0;" :: "r"(function), "{rsp}"(stack_top) :: "intel", "volatile");
        panic!("diverging fn returned");
    }

    fn inner() -> ! {
        // TODO FIXME kill current thread and deallocate stack
        schedule_inner()
    }
}

fn schedule_inner() -> ! {
    #[allow(improper_ctypes)]
    extern {
        fn pop_registers_and_iret(rsp: uint) -> !;
    }

    fn new_stack() -> uint {
        const NEW_STACK_SIZE: uint = 4096*2;
        let stack_bottom = unsafe{allocate(NEW_STACK_SIZE, 4096)};
        let stack_top = stack_bottom as uint + NEW_STACK_SIZE;
        stack_top
    }

    fn invoke(function: Box<FnBox() + Send>) -> ! {
        unsafe{::enable_interrupts()};
        function.call_once(());
        unsafe{asm!("int $$66" :::: "volatile")};
        unreachable!();
    }

    // schedule new thread
    let new_thread = unsafe{global().scheduler.schedule().unwrap_or_default()};

    match new_thread {
        Thread{state: ThreadState::Active{rsp}} => {
            unsafe{pop_registers_and_iret(rsp)}
        },
        Thread{state: ThreadState::New{function}} => {
            println!("new");
            let new_stack_top = new_stack();
            unsafe{call_on_stack(invoke, function, new_stack_top)}
        },
    }
}

fn scheduler_stack_top() -> uint {
    const SCHEDULER_STACK_SIZE: uint = 4096;
    static SCHEDULER_STACK: [u8; SCHEDULER_STACK_SIZE] = [0; SCHEDULER_STACK_SIZE];

    &SCHEDULER_STACK as *const [u8; 4096] as uint + SCHEDULER_STACK_SIZE
}

unsafe fn call_on_stack<Arg>(function: fn(Arg) -> !, arg: Arg, stack_top: uint) -> ! {
    asm!("call $0;" :: "r"(function), "{rdi}"(arg), "{rsp}"(stack_top) :
        : "intel", "volatile");
    panic!("diverging fn returned");
}

pub struct Future<T>;

pub struct Thread {
    state: ThreadState,
}

impl Thread {
    fn new<F>(f: F) -> Thread where F : FnOnce(), F: Send {
        Thread {
            state: ThreadState::New {
                function: box f,
            }
        }
    }

    pub fn from_rsp(rsp: uint) -> Thread {
        Thread {
            state: ThreadState::Active {
                rsp: rsp,
            }
        }
    }
}

impl Default for Thread {
    fn default() -> Thread {
        Thread::new(|| print!("."))
    }
}

enum ThreadState {
    New {
        function: Box<FnBox() + Send>,
    },
    Active {
        rsp: uint,
    }
}

struct NonInterruptableSpinlock<T>(Spinlock<T>);

impl<T> NonInterruptableSpinlock<T> {
    pub fn lock(&self) -> SpinlockGuard<T> {
        unsafe{::disable_interrupts()};
        self.0.lock()
    }
}

pub struct GlobalScheduler {
    threads: NonInterruptableSpinlock<RingBuf<Thread>>,
}

impl GlobalScheduler {
    pub fn new() -> GlobalScheduler {
        GlobalScheduler{
            threads: NonInterruptableSpinlock(Spinlock::new(RingBuf::new())),
        }
    }

    fn spawn<F, R>(&self, f:F) -> Future<R> where F: FnOnce()->R, F: Send {

        //let (tx, rx) = channel();

        self.threads.lock().push_back(Thread::new(move |:| {
            /*tx.send_opt(*/ f();
        }));

        unsafe{::enable_interrupts()};

        Future/*::from_receiver(rx)*/
    }

    fn park(&self, thread: Thread) {
        self.threads.lock().push_back(thread)
    }

    unsafe fn schedule(&self) -> Option<Thread> {
        self.threads.lock().pop_front()
    }
}<|endoftext|>


<commit_before>//! This module contains all the OpenGEX structures as specified. The module tries to stay as close
//! to the OpenGEX structures as possible.
//!
//! Some of the documentation might be subjected to copyright by Eric Lengyel. For more detailed
//! documentation, please go to http://opengex.org.

use std::collections::HashMap;

/// The Material structure contains information about a material. Material structures are
/// referenced by geometry nodes through MaterialRef structures belonging to GeometryNode
/// structures.
pub struct Material {
    /// Whether the material is two-sided.
    pub two_sided: bool,
    /// An optional name.
    pub name: Option<Name>,
    /// Any number of colors.
    pub color: HashMap<String, Color>,
    /// Any number of parameters.
    pub param: ParamMap,
    /// Any number of textures.
    pub texture: HashMap<String, Texture>,
}

/// A Color structure must contain an RGB color, or RGBA color.
pub enum Color {
    /// An RGB color value.
    Rgb(f32, f32, f32),
    /// An RGBA color value.
    Rgba(f32, f32, f32, f32)
}

/// This is a map of different Param structures, for convenience.
pub type ParamMap = HashMap<String, f32>;

/// The Name structure holds the name of a node, morph target, material, or animation clip.
pub type Name = String;

/// The Texture structure holds information about a single texture map, and how it is accessed with
/// texture coordinates.
pub struct Texture {
    /// The index of the texture coordinate set associated with the texture.
    pub texcoord: u32,
    /// A substructure holding the file name of the texture.
    pub file_name: String,
    /// Any number of transformations that are applied to the texture coordinates of a mesh when
    /// they are used to fetch from the texture map.
    pub temp: () // TODO: Finish this structure.
}
<commit_msg>Made progress on implementing all OpenGEX structures.<commit_after>//! This module contains all the OpenGEX structures as specified. The module tries to stay as close
//! to the OpenGEX structures as possible.
//!
//! Some of the documentation might be subjected to copyright by Eric Lengyel. For more detailed
//! documentation, please go to http://opengex.org.

use std::collections::HashMap;

/// The Material structure contains information about a material. Material structures are
/// referenced by geometry nodes through MaterialRef structures belonging to GeometryNode
/// structures.
pub struct Material {
    /// Whether the material is two-sided.
    pub two_sided: bool,
    /// An optional name.
    pub name: Option<Name>,
    /// Any number of colors.
    pub color: HashMap<String, Color>,
    /// Any number of parameters.
    pub param: ParamMap,
    /// Any number of textures.
    pub texture: HashMap<String, Texture>,
}

/// A Color structure must contain an RGB or RGBA color value.
pub enum Color {
    /// An RGB color value.
    Rgb(f32, f32, f32),
    /// An RGBA color value.
    Rgba(f32, f32, f32, f32)
}

/// This is a map of different Param structures, for convenience.
pub type ParamMap = HashMap<String, f32>;

/// The Name structure holds the name of a node, morph target, material, or animation clip.
pub type Name = String;

/// The Texture structure holds information about a single texture map, and how it is accessed with
/// texture coordinates.
pub struct Texture {
    /// The index of the texture coordinate set associated with the texture.
    pub texcoord: u32,
    /// A substructure holding the file name of the texture.
    pub file_name: String,
    /// Any number of transformations that are applied to the texture coordinates of a mesh when
    /// they are used to fetch from the texture map.
    pub transformations: Vec<Transformation>,
    /// Any number of animation tracks that are applied to the texture coordinate transformations.
    pub animation: Vec<Animation>
}

/// Helper enum to contain all different kinds of Transformations.
pub enum Transformation {
    /// A Transform structure.
    pub Transform(Transform),
    /// A Translation structure.
    pub Translation(Translation),
    /// A Rotation structure.
    pub Rotation(Rotation),
    /// A Scale structure.
    pub Scale(Scale)
}

/// The Transform structure holds one or more 4 x 4 transformation matrices. In the cases that a
/// Transform structure is contained inside any type of node structure, a Texture structure, or a
/// Skin structure, it must contain a single matrix. In the case that a Transform structure is
/// contained inside a Skeleton structure, is must contain an array of matrices with one entry for
/// each bone referenced by the skeleton.
///
/// When contained inside a node structure, a Transform structure can be the target of a track
/// stored inside an Animation structure.
pub struct Transform([f32; 16]);

/// The Translation structure holds a translation transformation in one of several possible
/// variants.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Translation structure can be the target of a track
/// stored inside an Animation structure.
pub enum Translation {
    /// The translation occurs along only the X axis.
    pub X(f32),
    /// The translation occurs along only the Y axis.
    pub Y(f32),
    /// The translation occurs along only the Z axis.
    pub Z(f32),
    /// The translation occurs along all three coordinate axes.
    pub Xyz(f32, f32, f32)
}

/// The Rotation structure represents a rotation along one of several axes.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Rotation structure can be the target of a track
/// stored in an Animation structure.
pub enum Rotation {
    /// The rotation occurs about the X axis.
    pub X(f32),
    /// The rotation occurs about the Y axis.
    pub Y(f32),
    /// The rotation occurs about the Z axis.
    pub Z(f32),
    /// The rotation occurs about an arbitrary axis. The first entry of this structure is the angle
    /// of rotation. The remaining three entries are respectively the X, Y and Z components of the
    /// axis of rotation.
    pub Axis(f32, f32, f32, f32),
    /// The rotation is given by a quaternion. Please refer to the official OpenGEX documentation
    /// for more information.
    pub Quaternion(f32, f32, f32, f32)
}

/// The Scale structure represents a scale transformation in one of several possible variants.
///
/// There are different variants of this type of tranformation, one for each "kind".
///
/// When contained inside a node structure, a Scale structure can be the target of a strack stored
/// inside an Animation structure.
pub enum Scale {
    /// The scaling occurs along only the X axis.
    pub X(f32),
    /// The scaling occurs along only the Y axis.
    pub Y(f32),
    /// The scaling occurs along only the Z axis.
    pub Z(f32),
    /// The scaling occurs along all three coordinate axes.
    pub Xyz(f32, f32, f32)
}

/// The Animation structure contains animation data for a single node in a scene. Each animation
/// structure is directly contained inside a node structure or Texture structure. Each animation
/// structure contains the data needed to modify its sibling Transformation structures or sibling
/// MorphWeight structures over time.
///
/// More detailed information can be found in the official OpenGEX specification.
pub struct Animation {
    /// Specifies the animation clip index.
    pub clip: u32,
    /// Specifies when the animation begins. If the property is not specifies, the begin time for
    /// the animation is determined by the earliest time values present in the Track structures
    /// belonging to this Animation.
    pub begin: Opt<f32>,
    /// Specifies when the animation ends. Like with the begin property, if the property is not
    /// specified, the end time for the animation is determined by the latest time values present
    /// in the Track structures belonging to this Animation.
    pub end: Opt<f32>,
    /// One or more tracks that each hold animation keys for a single target.
    pub tracks: Vec<Track>,
}

/// Enum wrapping over all possible animation track targets.
pub enum TrackTarget {
    pub Transformation(Transformation),
    pub MorphWeight(MorphWeight)
}

/// The Track structure contains animation key data for a single Transformation or MorphWeight
/// structure.
pub struct Track {
    // TODO: Finish this structure
}
<|endoftext|>