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

//! # Iml Job Scheduler RPC client
//!
//! This module provides a client to make rpc calls to the `job_scheduler`.
//!
//! It should be able to call into the job scheduler and recieve results the same as `job_scheduler_client`.

mod request;
mod response;

use iml_rabbit::{
    basic_consume_one, basic_publish, bind_queue, create_channel, declare_transient_exchange,
    declare_transient_queue, delete_queue, BasicConsumeOptions, Client, ExchangeKind,
    ImlRabbitError,
};
use iml_wire_types::CompositeId;
use request::Request;
use response::Response;
use std::{collections::HashMap, fmt::Debug};
use uuid::Uuid;

static JOB_SCHEDULER_RPC: &str = "JobSchedulerRpc";
static RPC: &str = "rpc";

#[derive(Debug)]
pub enum ImlJobSchedulerRpcError {
    ImlRabbitError(ImlRabbitError),
    RpcError(String),
    SerdeJsonError(serde_json::error::Error),
}

impl std::fmt::Display for ImlJobSchedulerRpcError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            ImlJobSchedulerRpcError::ImlRabbitError(ref err) => write!(f, "{}", err),
            ImlJobSchedulerRpcError::RpcError(ref err) => write!(f, "{}", err),
            ImlJobSchedulerRpcError::SerdeJsonError(ref err) => write!(f, "{}", err),
        }
    }
}

impl std::error::Error for ImlJobSchedulerRpcError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match *self {
            ImlJobSchedulerRpcError::ImlRabbitError(ref err) => Some(err),
            ImlJobSchedulerRpcError::RpcError(_) => None,
            ImlJobSchedulerRpcError::SerdeJsonError(ref err) => Some(err),
        }
    }
}

impl From<ImlRabbitError> for ImlJobSchedulerRpcError {
    fn from(err: ImlRabbitError) -> Self {
        ImlJobSchedulerRpcError::ImlRabbitError(err)
    }
}

impl From<String> for ImlJobSchedulerRpcError {
    fn from(err: String) -> Self {
        ImlJobSchedulerRpcError::RpcError(err)
    }
}

impl From<serde_json::error::Error> for ImlJobSchedulerRpcError {
    fn from(err: serde_json::error::Error) -> Self {
        ImlJobSchedulerRpcError::SerdeJsonError(err)
    }
}

/// Performs an RPC call to the `job_scheduler`.
pub async fn call<I: Debug + serde::Serialize, T: serde::de::DeserializeOwned>(
    client: Client,
    method: impl Into<String>,
    args: impl Into<Option<Vec<I>>>,
    kwargs: impl Into<Option<HashMap<String, String>>>,
) -> Result<T, ImlJobSchedulerRpcError> {
    let response_key = format!(
        "{}.responses_{}",
        JOB_SCHEDULER_RPC,
        Uuid::new_v4().to_hyphenated().to_string()
    );

    let method = method.into();

    let req = Request::new(
        &method,
        &response_key,
        args.into().unwrap_or_default(),
        kwargs.into().unwrap_or_default(),
    );

    tracing::debug!("--> to job scheduler {} {:?}", method, req);

    let channel = create_channel(client).await?;
    let channel = declare_transient_exchange(channel, RPC, ExchangeKind::Topic).await?;
    let (channel, queue) = declare_transient_queue(channel, &response_key).await?;
    let channel = bind_queue(channel, RPC, &response_key, &response_key).await?;

    let channel =
        basic_publish(channel, RPC, format!("{}.requests", JOB_SCHEDULER_RPC), req).await?;

    let (delivery, channel) = basic_consume_one(
        channel,
        queue,
        &response_key,
        Some(BasicConsumeOptions {
            no_ack: true,
            ..BasicConsumeOptions::default()
        }),
    )
    .await?;

    tracing::debug!(
        "<- from job scheduler {} {:?}",
        method,
        std::str::from_utf8(&delivery.data)
    );

    delete_queue(channel, &response_key).await?;

    let resp: Response<T> = serde_json::from_slice(&delivery.data)?;

    if let Some(e) = resp.exception {
        return Err(e.into());
    }

    resp.result.ok_or_else(|| {
        ImlJobSchedulerRpcError::RpcError("RPC response was unexpectedly empty".into())
    })
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct Transition {
    pub display_group: u64,
    pub display_order: u64,
    pub long_description: String,
    pub state: String,
    pub verb: String,
}

pub async fn available_transitions(
    client: Client,
    ids: &[CompositeId],
) -> Result<HashMap<CompositeId, Vec<Transition>>, ImlJobSchedulerRpcError> {
    let ids: Vec<_> = ids.into_iter().map(|x| (x.0, x.1)).collect();

    call(client, "available_transitions", vec![ids], None).await
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct Job {
    pub args: Option<HashMap<String, Option<u64>>>,
    pub class_name: Option<String>,
    pub confirmation: Option<String>,
    pub display_group: u64,
    pub display_order: u64,
    pub long_description: String,
    pub verb: String,
}

pub async fn available_jobs(
    client: Client,
    ids: &[CompositeId],
) -> Result<HashMap<CompositeId, Vec<Job>>, ImlJobSchedulerRpcError> {
    let ids: Vec<_> = ids.into_iter().map(|x| (x.0, x.1)).collect();

    call(client, "available_jobs", vec![ids], None).await
}
<commit_msg>ensure we close channel after making rpc call (#1599)<commit_after>// Copyright (c) 2020 DDN. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

//! # Iml Job Scheduler RPC client
//!
//! This module provides a client to make rpc calls to the `job_scheduler`.
//!
//! It should be able to call into the job scheduler and recieve results the same as `job_scheduler_client`.

mod request;
mod response;

use iml_rabbit::{
    basic_consume_one, basic_publish, bind_queue, close_channel, create_channel,
    declare_transient_exchange, declare_transient_queue, delete_queue, BasicConsumeOptions, Client,
    ExchangeKind, ImlRabbitError,
};
use iml_wire_types::CompositeId;
use request::Request;
use response::Response;
use std::{collections::HashMap, fmt::Debug};
use uuid::Uuid;

static JOB_SCHEDULER_RPC: &str = "JobSchedulerRpc";
static RPC: &str = "rpc";

#[derive(Debug)]
pub enum ImlJobSchedulerRpcError {
    ImlRabbitError(ImlRabbitError),
    RpcError(String),
    SerdeJsonError(serde_json::error::Error),
}

impl std::fmt::Display for ImlJobSchedulerRpcError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
        match *self {
            ImlJobSchedulerRpcError::ImlRabbitError(ref err) => write!(f, "{}", err),
            ImlJobSchedulerRpcError::RpcError(ref err) => write!(f, "{}", err),
            ImlJobSchedulerRpcError::SerdeJsonError(ref err) => write!(f, "{}", err),
        }
    }
}

impl std::error::Error for ImlJobSchedulerRpcError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        match *self {
            ImlJobSchedulerRpcError::ImlRabbitError(ref err) => Some(err),
            ImlJobSchedulerRpcError::RpcError(_) => None,
            ImlJobSchedulerRpcError::SerdeJsonError(ref err) => Some(err),
        }
    }
}

impl From<ImlRabbitError> for ImlJobSchedulerRpcError {
    fn from(err: ImlRabbitError) -> Self {
        ImlJobSchedulerRpcError::ImlRabbitError(err)
    }
}

impl From<String> for ImlJobSchedulerRpcError {
    fn from(err: String) -> Self {
        ImlJobSchedulerRpcError::RpcError(err)
    }
}

impl From<serde_json::error::Error> for ImlJobSchedulerRpcError {
    fn from(err: serde_json::error::Error) -> Self {
        ImlJobSchedulerRpcError::SerdeJsonError(err)
    }
}

/// Performs an RPC call to the `job_scheduler`.
pub async fn call<I: Debug + serde::Serialize, T: serde::de::DeserializeOwned>(
    client: Client,
    method: impl Into<String>,
    args: impl Into<Option<Vec<I>>>,
    kwargs: impl Into<Option<HashMap<String, String>>>,
) -> Result<T, ImlJobSchedulerRpcError> {
    let response_key = format!(
        "{}.responses_{}",
        JOB_SCHEDULER_RPC,
        Uuid::new_v4().to_hyphenated().to_string()
    );

    let method = method.into();

    let req = Request::new(
        &method,
        &response_key,
        args.into().unwrap_or_default(),
        kwargs.into().unwrap_or_default(),
    );

    tracing::debug!("--> to job scheduler {} {:?}", method, req);

    let channel = create_channel(client).await?;
    let channel = declare_transient_exchange(channel, RPC, ExchangeKind::Topic).await?;
    let (channel, queue) = declare_transient_queue(channel, &response_key).await?;
    let channel = bind_queue(channel, RPC, &response_key, &response_key).await?;

    let channel =
        basic_publish(channel, RPC, format!("{}.requests", JOB_SCHEDULER_RPC), req).await?;

    let (delivery, channel) = basic_consume_one(
        channel,
        queue,
        &response_key,
        Some(BasicConsumeOptions {
            no_ack: true,
            ..BasicConsumeOptions::default()
        }),
    )
    .await?;

    tracing::debug!(
        "<- from job scheduler {} {:?}",
        method,
        std::str::from_utf8(&delivery.data)
    );

    let channel = delete_queue(channel, &response_key).await?;

    close_channel(channel).await?;

    let resp: Response<T> = serde_json::from_slice(&delivery.data)?;

    if let Some(e) = resp.exception {
        return Err(e.into());
    }

    resp.result.ok_or_else(|| {
        ImlJobSchedulerRpcError::RpcError("RPC response was unexpectedly empty".into())
    })
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct Transition {
    pub display_group: u64,
    pub display_order: u64,
    pub long_description: String,
    pub state: String,
    pub verb: String,
}

pub async fn available_transitions(
    client: Client,
    ids: &[CompositeId],
) -> Result<HashMap<CompositeId, Vec<Transition>>, ImlJobSchedulerRpcError> {
    let ids: Vec<_> = ids.into_iter().map(|x| (x.0, x.1)).collect();

    call(client, "available_transitions", vec![ids], None).await
}

#[derive(serde::Serialize, serde::Deserialize, Debug)]
pub struct Job {
    pub args: Option<HashMap<String, Option<u64>>>,
    pub class_name: Option<String>,
    pub confirmation: Option<String>,
    pub display_group: u64,
    pub display_order: u64,
    pub long_description: String,
    pub verb: String,
}

pub async fn available_jobs(
    client: Client,
    ids: &[CompositeId],
) -> Result<HashMap<CompositeId, Vec<Job>>, ImlJobSchedulerRpcError> {
    let ids: Vec<_> = ids.into_iter().map(|x| (x.0, x.1)).collect();

    call(client, "available_jobs", vec![ids], None).await
}
<|endoftext|>


<commit_before>#![feature(test)]
extern crate test;
extern crate postgres;

use postgres::{Connection, TlsMode};

#[bench]
fn bench_naiive_execute(b: &mut test::Bencher) {
    let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap();
    conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])
        .unwrap();

    b.iter(|| {
        let stmt = conn.prepare("UPDATE foo SET id = 1").unwrap();
        let out = stmt.execute(&[]).unwrap();
        stmt.finish().unwrap();
        out
    });
}

#[bench]
fn bench_execute(b: &mut test::Bencher) {
    let conn = Connection::connect("postgres://postgres@localhost:5433", TlsMode::None).unwrap();
    conn.execute("CREATE TEMPORARY TABLE foo (id INT)", &[])
        .unwrap();

    b.iter(|| conn.execute("UPDATE foo SET id = 1", &[]).unwrap());
}
<commit_msg>Remove old benchmarks<commit_after><|endoftext|>


<commit_before>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

#[macro_export]
macro_rules! cstr {
	($s:expr) => {{
		const STR: &'static str = $s;
		$crate::lua::LuaCStr(concat!($s, "\0").as_bytes())
	}};
}

#[macro_export]
macro_rules! lua_module {
	($(#[$attr:meta])* unsafe fn $modname:ident($lua:ident) $block:block) => {
		$(#[$attr])*
		#[no_mangle]
		unsafe extern "C" fn $modname(L: $crate::lua_api::lua_State) -> ::libc::c_int {
			const RET_VALS: ::libc::c_int = 1;
			let $lua = unsafe { $crate::lua::Lua::new(&L) };
			let $lua = &$lua;

			#[cfg(any(debug_assertions, feature = "extra_checks"))]
			let _orig_top = unsafe { $lua.get_top() };

			$block

			#[cfg(any(debug_assertions, feature = "extra_checks"))]
			unsafe {
				let _new_top = $lua.get_top();
				let _stack_usage = $lua.get_top().wrapping_sub(_orig_top);
				assert_eq!(RET_VALS, _stack_usage);
			}

			RET_VALS
		}
	};
}

#[macro_export]
macro_rules! lua_struct_module {
	($mod_name:ident : $class:ident) => {
		$crate::lua_module! {unsafe fn $mod_name(lua) {
			unsafe {
				$crate::lua_get_or_init_metatable!($class : lua);
				lua.push("__index");
				lua.raw_get(-2);
				lua.replace(-2); // replace metatable with metatable.__index
			}
		}}
	};
}

#[macro_export]
macro_rules! lua_get_or_init_metatable {
	($class:ty : $lua:ident) => {
		if $lua.new_registry_metatable(<$class>::METATABLE_KEY) {
			<$class>::init_metatable($lua);
		}
	};
}

#[macro_export]
macro_rules! lua_methods {
	($($(#[$attr:meta])* unsafe fn $method_name:ident($lua:ident $(, $arg:ident:$arg_ty:ty)*) -> $ret_vals:literal $block:block)*) => {
		$(
			$(#[$attr])*
			#[allow(non_snake_case)]
			unsafe extern "C" fn $method_name(L: $crate::lua_api::lua_State) -> libc::c_int {
				let $lua = unsafe { $crate::lua::Lua::new(&L) };
				let $lua = &$lua;

				#[cfg(any(debug_assertions, feature = "extra_checks"))]
				let _orig_top = unsafe { $lua.get_top() };

				// Unfortunately consts don't work, eg. const A: c_int = 1; const A: c_int = A + 1; etc.
				let _lua_index: ::libc::c_int = 1;
				$(
					let $arg: <$arg_ty as FromLua<'_>>::RetType = <$arg_ty as FromLua<'_>>::from_lua($lua, _lua_index);
					let _lua_index: ::libc::c_int = _lua_index + 1;
				)*

				const _: [(); 0 - !($ret_vals >= 0) as usize] = []; //TODO: Rust 1.57: use const _: () = assert!($ret_vals >= 0);
				if $ret_vals > $crate::lua_api::LUA_MINSTACK {
					if !$lua.check_stack($ret_vals as ::libc::c_int) {
						$lua.throw_error_msg("Couldn't grow the stack");
					}
				}
				$block

				#[cfg(any(debug_assertions, feature = "extra_checks"))]
				unsafe {
					let _new_top = $lua.get_top();
					let _stack_usage = $lua.get_top().wrapping_sub(_orig_top);
					assert_eq!($ret_vals, _stack_usage);
				}

				$ret_vals
			}
		)*
	};
}

#[macro_export]
macro_rules! lua_pub_methods {
	(static $export_name:ident => $($(#[$attr:meta])* unsafe fn $method_name:ident($lua:ident $(, $arg:ident:$arg_ty:ty)*) -> $ret_vals:literal $block:block)*) => {
		$crate::lua_methods! { $($(#[$attr])* unsafe fn $method_name($lua $(, $arg:$arg_ty)*) -> $ret_vals $block)* }
		static $export_name: &[(&str, $crate::lua_api::lua_CFunction)] = &[
			$(
				(stringify!($method_name), $method_name),
			)*
		];
	};
}
<commit_msg>Use full path to `FromLua` trait in exported macros<commit_after>// SPDX-License-Identifier: MIT
// Copyright (C) 2018-present iced project and contributors

#[macro_export]
macro_rules! cstr {
	($s:expr) => {{
		const STR: &'static str = $s;
		$crate::lua::LuaCStr(concat!($s, "\0").as_bytes())
	}};
}

#[macro_export]
macro_rules! lua_module {
	($(#[$attr:meta])* unsafe fn $modname:ident($lua:ident) $block:block) => {
		$(#[$attr])*
		#[no_mangle]
		unsafe extern "C" fn $modname(L: $crate::lua_api::lua_State) -> ::libc::c_int {
			const RET_VALS: ::libc::c_int = 1;
			let $lua = unsafe { $crate::lua::Lua::new(&L) };
			let $lua = &$lua;

			#[cfg(any(debug_assertions, feature = "extra_checks"))]
			let _orig_top = unsafe { $lua.get_top() };

			$block

			#[cfg(any(debug_assertions, feature = "extra_checks"))]
			unsafe {
				let _new_top = $lua.get_top();
				let _stack_usage = $lua.get_top().wrapping_sub(_orig_top);
				assert_eq!(RET_VALS, _stack_usage);
			}

			RET_VALS
		}
	};
}

#[macro_export]
macro_rules! lua_struct_module {
	($mod_name:ident : $class:ident) => {
		$crate::lua_module! {unsafe fn $mod_name(lua) {
			unsafe {
				$crate::lua_get_or_init_metatable!($class : lua);
				lua.push("__index");
				lua.raw_get(-2);
				lua.replace(-2); // replace metatable with metatable.__index
			}
		}}
	};
}

#[macro_export]
macro_rules! lua_get_or_init_metatable {
	($class:ty : $lua:ident) => {
		if $lua.new_registry_metatable(<$class>::METATABLE_KEY) {
			<$class>::init_metatable($lua);
		}
	};
}

#[macro_export]
macro_rules! lua_methods {
	($($(#[$attr:meta])* unsafe fn $method_name:ident($lua:ident $(, $arg:ident:$arg_ty:ty)*) -> $ret_vals:literal $block:block)*) => {
		$(
			$(#[$attr])*
			#[allow(non_snake_case)]
			unsafe extern "C" fn $method_name(L: $crate::lua_api::lua_State) -> libc::c_int {
				let $lua = unsafe { $crate::lua::Lua::new(&L) };
				let $lua = &$lua;

				#[cfg(any(debug_assertions, feature = "extra_checks"))]
				let _orig_top = unsafe { $lua.get_top() };

				// Unfortunately consts don't work, eg. const A: c_int = 1; const A: c_int = A + 1; etc.
				let _lua_index: ::libc::c_int = 1;
				$(
					let $arg: <$arg_ty as $crate::tofrom::FromLua<'_>>::RetType = <$arg_ty as $crate::tofrom::FromLua<'_>>::from_lua($lua, _lua_index);
					let _lua_index: ::libc::c_int = _lua_index + 1;
				)*

				const _: [(); 0 - !($ret_vals >= 0) as usize] = []; //TODO: Rust 1.57: use const _: () = assert!($ret_vals >= 0);
				if $ret_vals > $crate::lua_api::LUA_MINSTACK {
					if !$lua.check_stack($ret_vals as ::libc::c_int) {
						$lua.throw_error_msg("Couldn't grow the stack");
					}
				}
				$block

				#[cfg(any(debug_assertions, feature = "extra_checks"))]
				unsafe {
					let _new_top = $lua.get_top();
					let _stack_usage = $lua.get_top().wrapping_sub(_orig_top);
					assert_eq!($ret_vals, _stack_usage);
				}

				$ret_vals
			}
		)*
	};
}

#[macro_export]
macro_rules! lua_pub_methods {
	(static $export_name:ident => $($(#[$attr:meta])* unsafe fn $method_name:ident($lua:ident $(, $arg:ident:$arg_ty:ty)*) -> $ret_vals:literal $block:block)*) => {
		$crate::lua_methods! { $($(#[$attr])* unsafe fn $method_name($lua $(, $arg:$arg_ty)*) -> $ret_vals $block)* }
		static $export_name: &[(&str, $crate::lua_api::lua_CFunction)] = &[
			$(
				(stringify!($method_name), $method_name),
			)*
		];
	};
}
<|endoftext|>


<commit_before>//! Futures task based helpers

#![allow(clippy::mutex_atomic)]

use std::future::Future;
use std::mem;
use std::ops;
use std::pin::Pin;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

use tokio::stream::Stream;

/// TOOD: dox
pub fn spawn<T>(task: T) -> Spawn<T> {
    Spawn {
        task: MockTask::new(),
        future: Box::pin(task),
    }
}

/// Future spawned on a mock task
#[derive(Debug)]
pub struct Spawn<T> {
    task: MockTask,
    future: Pin<Box<T>>,
}

/// Mock task
///
/// A mock task is able to intercept and track wake notifications.
#[derive(Debug, Clone)]
struct MockTask {
    waker: Arc<ThreadWaker>,
}

#[derive(Debug)]
struct ThreadWaker {
    state: Mutex<usize>,
    condvar: Condvar,
}

const IDLE: usize = 0;
const WAKE: usize = 1;
const SLEEP: usize = 2;

impl<T> Spawn<T> {
    /// Consumes `self` returning the inner value
    pub fn into_inner(self) -> T
    where
        T: Unpin,
    {
        *Pin::into_inner(self.future)
    }

    /// Returns `true` if the inner future has received a wake notification
    /// since the last call to `enter`.
    pub fn is_woken(&self) -> bool {
        self.task.is_woken()
    }

    /// Returns the number of references to the task waker
    ///
    /// The task itself holds a reference. The return value will never be zero.
    pub fn waker_ref_count(&self) -> usize {
        self.task.waker_ref_count()
    }

    /// Enter the task context
    pub fn enter<F, R>(&mut self, f: F) -> R
    where
        F: FnOnce(&mut Context<'_>, Pin<&mut T>) -> R,
    {
        let fut = self.future.as_mut();
        self.task.enter(|cx| f(cx, fut))
    }
}

impl<T: Unpin> ops::Deref for Spawn<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.future
    }
}

impl<T: Unpin> ops::DerefMut for Spawn<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.future
    }
}

impl<T: Future> Spawn<T> {
    /// Polls a future
    pub fn poll(&mut self) -> Poll<T::Output> {
        let fut = self.future.as_mut();
        self.task.enter(|cx| fut.poll(cx))
    }
}

impl<T: Stream> Spawn<T> {
    /// Polls a stream
    pub fn poll_next(&mut self) -> Poll<Option<T::Item>> {
        let stream = self.future.as_mut();
        self.task.enter(|cx| stream.poll_next(cx))
    }
}

impl<T: Future> Future for Spawn<T> {
    type Output = T::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        self.future.as_mut().poll(cx)
    }
}

impl<T: Stream> Stream for Spawn<T> {
    type Item = T::Item;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        self.future.as_mut().poll_next(cx)
    }
}

impl MockTask {
    /// Creates new mock task
    fn new() -> Self {
        MockTask {
            waker: Arc::new(ThreadWaker::new()),
        }
    }

    /// Runs a closure from the context of the task.
    ///
    /// Any wake notifications resulting from the execution of the closure are
    /// tracked.
    fn enter<F, R>(&mut self, f: F) -> R
    where
        F: FnOnce(&mut Context<'_>) -> R,
    {
        self.waker.clear();
        let waker = self.waker();
        let mut cx = Context::from_waker(&waker);

        f(&mut cx)
    }

    /// Returns `true` if the inner future has received a wake notification
    /// since the last call to `enter`.
    fn is_woken(&self) -> bool {
        self.waker.is_woken()
    }

    /// Returns the number of references to the task waker
    ///
    /// The task itself holds a reference. The return value will never be zero.
    fn waker_ref_count(&self) -> usize {
        Arc::strong_count(&self.waker)
    }

    fn waker(&self) -> Waker {
        unsafe {
            let raw = to_raw(self.waker.clone());
            Waker::from_raw(raw)
        }
    }
}

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

impl ThreadWaker {
    fn new() -> Self {
        ThreadWaker {
            state: Mutex::new(IDLE),
            condvar: Condvar::new(),
        }
    }

    /// Clears any previously received wakes, avoiding potential spurrious
    /// wake notifications. This should only be called immediately before running the
    /// task.
    fn clear(&self) {
        *self.state.lock().unwrap() = IDLE;
    }

    fn is_woken(&self) -> bool {
        match *self.state.lock().unwrap() {
            IDLE => false,
            WAKE => true,
            _ => unreachable!(),
        }
    }

    fn wake(&self) {
        // First, try transitioning from IDLE -> NOTIFY, this does not require a lock.
        let mut state = self.state.lock().unwrap();
        let prev = *state;

        if prev == WAKE {
            return;
        }

        *state = WAKE;

        if prev == IDLE {
            return;
        }

        // The other half is sleeping, so we wake it up.
        assert_eq!(prev, SLEEP);
        self.condvar.notify_one();
    }
}

static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop_waker);

unsafe fn to_raw(waker: Arc<ThreadWaker>) -> RawWaker {
    RawWaker::new(Arc::into_raw(waker) as *const (), &VTABLE)
}

unsafe fn from_raw(raw: *const ()) -> Arc<ThreadWaker> {
    Arc::from_raw(raw as *const ThreadWaker)
}

unsafe fn clone(raw: *const ()) -> RawWaker {
    let waker = from_raw(raw);

    // Increment the ref count
    mem::forget(waker.clone());

    to_raw(waker)
}

unsafe fn wake(raw: *const ()) {
    let waker = from_raw(raw);
    waker.wake();
}

unsafe fn wake_by_ref(raw: *const ()) {
    let waker = from_raw(raw);
    waker.wake();

    // We don't actually own a reference to the unparker
    mem::forget(waker);
}

unsafe fn drop_waker(raw: *const ()) {
    let _ = from_raw(raw);
}
<commit_msg>test: fix spelling error in documentation (#2895)<commit_after>//! Futures task based helpers

#![allow(clippy::mutex_atomic)]

use std::future::Future;
use std::mem;
use std::ops;
use std::pin::Pin;
use std::sync::{Arc, Condvar, Mutex};
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};

use tokio::stream::Stream;

/// TODO: dox
pub fn spawn<T>(task: T) -> Spawn<T> {
    Spawn {
        task: MockTask::new(),
        future: Box::pin(task),
    }
}

/// Future spawned on a mock task
#[derive(Debug)]
pub struct Spawn<T> {
    task: MockTask,
    future: Pin<Box<T>>,
}

/// Mock task
///
/// A mock task is able to intercept and track wake notifications.
#[derive(Debug, Clone)]
struct MockTask {
    waker: Arc<ThreadWaker>,
}

#[derive(Debug)]
struct ThreadWaker {
    state: Mutex<usize>,
    condvar: Condvar,
}

const IDLE: usize = 0;
const WAKE: usize = 1;
const SLEEP: usize = 2;

impl<T> Spawn<T> {
    /// Consumes `self` returning the inner value
    pub fn into_inner(self) -> T
    where
        T: Unpin,
    {
        *Pin::into_inner(self.future)
    }

    /// Returns `true` if the inner future has received a wake notification
    /// since the last call to `enter`.
    pub fn is_woken(&self) -> bool {
        self.task.is_woken()
    }

    /// Returns the number of references to the task waker
    ///
    /// The task itself holds a reference. The return value will never be zero.
    pub fn waker_ref_count(&self) -> usize {
        self.task.waker_ref_count()
    }

    /// Enter the task context
    pub fn enter<F, R>(&mut self, f: F) -> R
    where
        F: FnOnce(&mut Context<'_>, Pin<&mut T>) -> R,
    {
        let fut = self.future.as_mut();
        self.task.enter(|cx| f(cx, fut))
    }
}

impl<T: Unpin> ops::Deref for Spawn<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.future
    }
}

impl<T: Unpin> ops::DerefMut for Spawn<T> {
    fn deref_mut(&mut self) -> &mut T {
        &mut self.future
    }
}

impl<T: Future> Spawn<T> {
    /// Polls a future
    pub fn poll(&mut self) -> Poll<T::Output> {
        let fut = self.future.as_mut();
        self.task.enter(|cx| fut.poll(cx))
    }
}

impl<T: Stream> Spawn<T> {
    /// Polls a stream
    pub fn poll_next(&mut self) -> Poll<Option<T::Item>> {
        let stream = self.future.as_mut();
        self.task.enter(|cx| stream.poll_next(cx))
    }
}

impl<T: Future> Future for Spawn<T> {
    type Output = T::Output;

    fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
        self.future.as_mut().poll(cx)
    }
}

impl<T: Stream> Stream for Spawn<T> {
    type Item = T::Item;

    fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
        self.future.as_mut().poll_next(cx)
    }
}

impl MockTask {
    /// Creates new mock task
    fn new() -> Self {
        MockTask {
            waker: Arc::new(ThreadWaker::new()),
        }
    }

    /// Runs a closure from the context of the task.
    ///
    /// Any wake notifications resulting from the execution of the closure are
    /// tracked.
    fn enter<F, R>(&mut self, f: F) -> R
    where
        F: FnOnce(&mut Context<'_>) -> R,
    {
        self.waker.clear();
        let waker = self.waker();
        let mut cx = Context::from_waker(&waker);

        f(&mut cx)
    }

    /// Returns `true` if the inner future has received a wake notification
    /// since the last call to `enter`.
    fn is_woken(&self) -> bool {
        self.waker.is_woken()
    }

    /// Returns the number of references to the task waker
    ///
    /// The task itself holds a reference. The return value will never be zero.
    fn waker_ref_count(&self) -> usize {
        Arc::strong_count(&self.waker)
    }

    fn waker(&self) -> Waker {
        unsafe {
            let raw = to_raw(self.waker.clone());
            Waker::from_raw(raw)
        }
    }
}

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

impl ThreadWaker {
    fn new() -> Self {
        ThreadWaker {
            state: Mutex::new(IDLE),
            condvar: Condvar::new(),
        }
    }

    /// Clears any previously received wakes, avoiding potential spurrious
    /// wake notifications. This should only be called immediately before running the
    /// task.
    fn clear(&self) {
        *self.state.lock().unwrap() = IDLE;
    }

    fn is_woken(&self) -> bool {
        match *self.state.lock().unwrap() {
            IDLE => false,
            WAKE => true,
            _ => unreachable!(),
        }
    }

    fn wake(&self) {
        // First, try transitioning from IDLE -> NOTIFY, this does not require a lock.
        let mut state = self.state.lock().unwrap();
        let prev = *state;

        if prev == WAKE {
            return;
        }

        *state = WAKE;

        if prev == IDLE {
            return;
        }

        // The other half is sleeping, so we wake it up.
        assert_eq!(prev, SLEEP);
        self.condvar.notify_one();
    }
}

static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop_waker);

unsafe fn to_raw(waker: Arc<ThreadWaker>) -> RawWaker {
    RawWaker::new(Arc::into_raw(waker) as *const (), &VTABLE)
}

unsafe fn from_raw(raw: *const ()) -> Arc<ThreadWaker> {
    Arc::from_raw(raw as *const ThreadWaker)
}

unsafe fn clone(raw: *const ()) -> RawWaker {
    let waker = from_raw(raw);

    // Increment the ref count
    mem::forget(waker.clone());

    to_raw(waker)
}

unsafe fn wake(raw: *const ()) {
    let waker = from_raw(raw);
    waker.wake();
}

unsafe fn wake_by_ref(raw: *const ()) {
    let waker = from_raw(raw);
    waker.wake();

    // We don't actually own a reference to the unparker
    mem::forget(waker);
}

unsafe fn drop_waker(raw: *const ()) {
    let _ = from_raw(raw);
}
<|endoftext|>


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

use schema::{
    DirectiveValue, EnumID, FieldID, InputObjectID, InterfaceID, ObjectID, SDLSchema, ScalarID,
    Schema, Type, UnionID,
};
use schema_documentation::SchemaDocumentation;
use serde::{Deserialize, Serialize};

const DEFAULT_COUNT: usize = 100;

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerTypeReference<T> {
    name: String,
    description: Option<String>,
    kind: String,
    #[serde(flatten)]
    inner: T,
}

#[derive(Deserialize, Serialize)]
#[serde(tag = "kind")]
pub(crate) enum SchemaExplorerSchemaType {
    Object(SchemaExplorerObject),
    Interface(SchemaExplorerInterface),
    Enum(SchemaExplorerEnum),
    Union(SchemaExplorerUnion),
    Scalar(SchemaExplorerScalar),
    InputObject(SchemaExplorerInputObject),
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerInterface {
    implementing_objects: Vec<SchemaExplorerTypeReference<()>>,
    fields: Vec<SchemaExplorerField>,
    interfaces: Vec<SchemaExplorerTypeReference<()>>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}
#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerObject {
    fields: Vec<SchemaExplorerField>,
    interfaces: Vec<SchemaExplorerTypeReference<()>>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerInputObject {
    // This is named differently than in definitions.rs
    arguments: Vec<SchemaExplorerFieldArgument>,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerEnum {
    values: Vec<String>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerUnion {
    is_extension: bool,
    members: Vec<SchemaExplorerTypeReference<()>>,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerField {
    field_name: String,
    arguments: Vec<SchemaExplorerFieldArgument>,
    rendered_type_name: String,
    type_reference: SchemaExplorerTypeReference<()>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerFieldArgument {
    argument_name: String,
    default_value: Option<String>,
    rendered_type_name: String,
    type_reference: SchemaExplorerTypeReference<()>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerScalar {
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerDirective {
    directive_name: String,
    arguments: Vec<SchemaExplorerDirectiveArgument>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerDirectiveArgument {
    argument_name: String,
    value: String,
}

pub(crate) fn get_full_schema_explorer_type_reference(
    type_: Type,
    type_name: &str,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
    count: Option<usize>,
) -> SchemaExplorerTypeReference<SchemaExplorerSchemaType> {
    let description = documentation
        .get_type_description(&type_name)
        .map(|description| description.to_string());

    let schema_explorer_schema_type = match type_ {
        Type::Object(object_id) => {
            let object =
                get_schema_explorer_object(object_id, schema, documentation, filter, count);
            SchemaExplorerSchemaType::Object(object)
        }
        Type::Interface(interface_id) => {
            let interface =
                get_schema_explorer_interface(interface_id, schema, documentation, filter);
            SchemaExplorerSchemaType::Interface(interface)
        }
        Type::Enum(enum_id) => {
            let enum_ = get_schema_explorer_enum(enum_id, schema);
            SchemaExplorerSchemaType::Enum(enum_)
        }
        Type::InputObject(input_object_id) => {
            let input_object =
                get_schema_explorer_input_object(input_object_id, schema, documentation);
            SchemaExplorerSchemaType::InputObject(input_object)
        }
        Type::Union(union_id) => {
            let union_ = get_schema_explorer_union(union_id, schema, documentation);
            SchemaExplorerSchemaType::Union(union_)
        }
        Type::Scalar(scalar_id) => {
            let scalar = get_schema_explorer_scalar(scalar_id, schema);
            SchemaExplorerSchemaType::Scalar(scalar)
        }
    };

    SchemaExplorerTypeReference {
        kind: get_kind(type_),
        name: type_name.to_string(),
        description,
        inner: schema_explorer_schema_type,
    }
}

fn get_empty_schema_explorer_type_reference(
    type_: Type,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
) -> SchemaExplorerTypeReference<()> {
    let name = schema.get_type_name(type_).to_string();
    let description = documentation
        .get_type_description(&name)
        .map(|description| description.to_string());
    SchemaExplorerTypeReference {
        kind: get_kind(type_),
        name,
        description,
        inner: (),
    }
}

fn get_schema_explorer_enum(enum_id: EnumID, schema: &SDLSchema) -> SchemaExplorerEnum {
    let enum_ = schema.enum_(enum_id);
    let values = enum_
        .values
        .iter()
        .map(|value| value.value.to_string())
        .collect::<Vec<_>>();
    SchemaExplorerEnum {
        values,
        is_extension: enum_.is_extension,
        directives: get_schema_explorer_directives(&enum_.directives),
    }
}

fn get_schema_explorer_interface(
    interface_id: InterfaceID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
) -> SchemaExplorerInterface {
    let interface = schema.interface(interface_id);
    let implementing_objects = interface
        .implementing_objects
        .iter()
        .map(|object_id| {
            get_empty_schema_explorer_type_reference(
                Type::Object(*object_id),
                schema,
                documentation,
            )
        })
        .collect::<Vec<_>>();

    let fields = interface
        .fields
        .iter()
        .filter_map(|field_id| get_schema_explorer_field(*field_id, schema, documentation, filter))
        .collect::<Vec<_>>();

    let subinterfaces = interface
        .interfaces
        .iter()
        .map(|interface_id| {
            get_empty_schema_explorer_type_reference(
                Type::Interface(*interface_id),
                schema,
                documentation,
            )
        })
        .collect::<Vec<_>>();

    SchemaExplorerInterface {
        implementing_objects,
        fields,
        interfaces: subinterfaces,
        is_extension: interface.is_extension,
        directives: get_schema_explorer_directives(&interface.directives),
    }
}

fn get_schema_explorer_input_object(
    input_object_id: InputObjectID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
) -> SchemaExplorerInputObject {
    let input_object = schema.input_object(input_object_id);
    let arguments = input_object
        .fields
        .iter()
        .map(|arg| {
            let type_reference =
                get_empty_schema_explorer_type_reference(arg.type_.inner(), schema, documentation);

            SchemaExplorerFieldArgument {
                argument_name: arg.name.to_string(),
                default_value: arg.default_value.as_ref().map(|value| value.to_string()),
                rendered_type_name: schema.get_type_string(&arg.type_),
                type_reference,
            }
        })
        .collect::<Vec<_>>();

    SchemaExplorerInputObject {
        arguments,
        directives: get_schema_explorer_directives(&input_object.directives),
    }
}

fn get_schema_explorer_object(
    object_id: ObjectID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
    count: Option<usize>,
) -> SchemaExplorerObject {
    let object = schema.object(object_id);
    let fields = object
        .fields
        .iter()
        .filter_map(|field_id| get_schema_explorer_field(*field_id, schema, documentation, filter))
        .take(count.unwrap_or(DEFAULT_COUNT))
        .collect::<Vec<_>>();

    let interfaces = object
        .interfaces
        .iter()
        .map(|interface_id| {
            get_empty_schema_explorer_type_reference(
                Type::Interface(*interface_id),
                schema,
                documentation,
            )
        })
        .collect::<Vec<_>>();

    SchemaExplorerObject {
        fields,
        interfaces,
        is_extension: object.is_extension,
        directives: get_schema_explorer_directives(&object.directives),
    }
}

fn get_schema_explorer_union(
    union_id: UnionID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
) -> SchemaExplorerUnion {
    let union_ = schema.union(union_id);

    let members = union_
        .members
        .iter()
        .map(|id| {
            get_empty_schema_explorer_type_reference(Type::Object(*id), schema, documentation)
        })
        .collect::<Vec<_>>();

    SchemaExplorerUnion {
        members,
        is_extension: union_.is_extension,
        directives: get_schema_explorer_directives(&union_.directives),
    }
}

fn get_schema_explorer_scalar(scalar_id: ScalarID, schema: &SDLSchema) -> SchemaExplorerScalar {
    let scalar = schema.scalar(scalar_id);

    SchemaExplorerScalar {
        is_extension: scalar.is_extension,
        directives: get_schema_explorer_directives(&scalar.directives),
    }
}

fn get_schema_explorer_field(
    field_id: FieldID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
) -> Option<SchemaExplorerField> {
    let field = schema.field(field_id);
    let field_name = field.name.to_string();

    let field_type_name = schema.get_type_name(field.type_.inner()).to_string();

    let field_type_description = documentation
        .get_type_description(&field_type_name)
        .map(|x| x.to_string());

    if let Some(filter) = filter {
        if !field_passes_filter(
            filter,
            field_name.to_lowercase(),
            field_type_name.to_lowercase(),
            field_type_description.as_ref().map(|s| s.to_lowercase()),
        ) {
            return None;
        }
    }

    let type_reference =
        get_empty_schema_explorer_type_reference(field.type_.inner(), schema, documentation);

    let arguments = field
        .arguments
        .iter()
        .map(|arg| {
            let type_reference =
                get_empty_schema_explorer_type_reference(arg.type_.inner(), schema, documentation);
            SchemaExplorerFieldArgument {
                argument_name: arg.name.to_string(),
                rendered_type_name: schema.get_type_string(&arg.type_),
                type_reference,
                default_value: arg.default_value.as_ref().map(|value| value.to_string()),
            }
        })
        .collect::<Vec<_>>();

    Some(SchemaExplorerField {
        field_name,
        type_reference,
        rendered_type_name: schema.get_type_string(&field.type_),
        arguments,
        is_extension: field.is_extension,
        directives: get_schema_explorer_directives(&field.directives),
    })
}

fn field_passes_filter(
    filter: &str,
    field_name: String,
    field_type_name: String,
    field_type_description: Option<String>,
) -> bool {
    if field_name.contains(filter) || field_type_name.contains(filter) {
        true
    } else if let Some(field_type_description) = field_type_description {
        field_type_description.contains(filter)
    } else {
        false
    }
}

fn get_schema_explorer_directives(directives: &[DirectiveValue]) -> Vec<SchemaExplorerDirective> {
    directives
        .iter()
        .map(|directive| -> SchemaExplorerDirective {
            SchemaExplorerDirective {
                directive_name: directive.name.to_string(),
                arguments: directive
                    .arguments
                    .iter()
                    .map(|arg| SchemaExplorerDirectiveArgument {
                        argument_name: arg.name.to_string(),
                        value: arg.value.to_string(),
                    })
                    .collect::<Vec<_>>(),
            }
        })
        .collect::<Vec<_>>()
}

fn get_kind(type_: Type) -> String {
    match type_ {
        Type::Enum(_) => "Enum".to_string(),
        Type::Union(_) => "Union".to_string(),
        Type::InputObject(_) => "InputObject".to_string(),
        Type::Object(_) => "Object".to_string(),
        Type::Scalar(_) => "Scalar".to_string(),
        Type::Interface(_) => "Interface".to_string(),
    }
}
<commit_msg>Don't show type descriptions where we have field and argument descriptions<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use interner::StringKey;
use schema::{
    DirectiveValue, EnumID, FieldID, InputObjectID, InterfaceID, ObjectID, SDLSchema, ScalarID,
    Schema, Type, UnionID,
};
use schema_documentation::SchemaDocumentation;
use serde::{Deserialize, Serialize};

const DEFAULT_COUNT: usize = 100;

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerTypeReference<T> {
    name: String,
    description: Option<String>,
    kind: String,
    #[serde(flatten)]
    inner: T,
}

#[derive(Deserialize, Serialize)]
#[serde(tag = "kind")]
pub(crate) enum SchemaExplorerSchemaType {
    Object(SchemaExplorerObject),
    Interface(SchemaExplorerInterface),
    Enum(SchemaExplorerEnum),
    Union(SchemaExplorerUnion),
    Scalar(SchemaExplorerScalar),
    InputObject(SchemaExplorerInputObject),
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerInterface {
    implementing_objects: Vec<SchemaExplorerTypeReference<()>>,
    fields: Vec<SchemaExplorerField>,
    interfaces: Vec<SchemaExplorerTypeReference<()>>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}
#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerObject {
    fields: Vec<SchemaExplorerField>,
    interfaces: Vec<SchemaExplorerTypeReference<()>>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerInputObject {
    // This is named differently than in definitions.rs
    arguments: Vec<SchemaExplorerFieldArgument>,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerEnum {
    values: Vec<String>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerUnion {
    is_extension: bool,
    members: Vec<SchemaExplorerTypeReference<()>>,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerField {
    field_name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    field_description: Option<String>,
    arguments: Vec<SchemaExplorerFieldArgument>,
    rendered_type_name: String,
    type_reference: SchemaExplorerTypeReference<()>,
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerFieldArgument {
    argument_name: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    argument_description: Option<String>,
    default_value: Option<String>,
    rendered_type_name: String,
    type_reference: SchemaExplorerTypeReference<()>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerScalar {
    is_extension: bool,
    directives: Vec<SchemaExplorerDirective>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerDirective {
    directive_name: String,
    arguments: Vec<SchemaExplorerDirectiveArgument>,
}

#[derive(Deserialize, Serialize)]
pub(crate) struct SchemaExplorerDirectiveArgument {
    argument_name: String,
    value: String,
}

pub(crate) fn get_full_schema_explorer_type_reference(
    type_: Type,
    type_name: &str,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
    count: Option<usize>,
) -> SchemaExplorerTypeReference<SchemaExplorerSchemaType> {
    let description = documentation
        .get_type_description(&type_name)
        .map(|description| description.to_string());

    let schema_explorer_schema_type = match type_ {
        Type::Object(object_id) => {
            let object =
                get_schema_explorer_object(object_id, schema, documentation, filter, count);
            SchemaExplorerSchemaType::Object(object)
        }
        Type::Interface(interface_id) => {
            let interface =
                get_schema_explorer_interface(interface_id, schema, documentation, filter);
            SchemaExplorerSchemaType::Interface(interface)
        }
        Type::Enum(enum_id) => {
            let enum_ = get_schema_explorer_enum(enum_id, schema);
            SchemaExplorerSchemaType::Enum(enum_)
        }
        Type::InputObject(input_object_id) => {
            let input_object =
                get_schema_explorer_input_object(input_object_id, schema, documentation);
            SchemaExplorerSchemaType::InputObject(input_object)
        }
        Type::Union(union_id) => {
            let union_ = get_schema_explorer_union(union_id, schema, documentation);
            SchemaExplorerSchemaType::Union(union_)
        }
        Type::Scalar(scalar_id) => {
            let scalar = get_schema_explorer_scalar(scalar_id, schema);
            SchemaExplorerSchemaType::Scalar(scalar)
        }
    };

    SchemaExplorerTypeReference {
        kind: get_kind(type_),
        name: type_name.to_string(),
        description,
        inner: schema_explorer_schema_type,
    }
}

fn get_empty_schema_explorer_type_reference(
    type_: Type,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
) -> SchemaExplorerTypeReference<()> {
    let name = schema.get_type_name(type_).to_string();
    let description = documentation
        .get_type_description(&name)
        .map(|description| description.to_string());
    SchemaExplorerTypeReference {
        kind: get_kind(type_),
        name,
        description,
        inner: (),
    }
}

fn get_schema_explorer_enum(enum_id: EnumID, schema: &SDLSchema) -> SchemaExplorerEnum {
    let enum_ = schema.enum_(enum_id);
    let values = enum_
        .values
        .iter()
        .map(|value| value.value.to_string())
        .collect::<Vec<_>>();
    SchemaExplorerEnum {
        values,
        is_extension: enum_.is_extension,
        directives: get_schema_explorer_directives(&enum_.directives),
    }
}

fn get_schema_explorer_interface(
    interface_id: InterfaceID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
) -> SchemaExplorerInterface {
    let interface = schema.interface(interface_id);
    let implementing_objects = interface
        .implementing_objects
        .iter()
        .map(|object_id| {
            get_empty_schema_explorer_type_reference(
                Type::Object(*object_id),
                schema,
                documentation,
            )
        })
        .collect::<Vec<_>>();

    let interface_type_name = schema.get_type_name(Type::Interface(interface_id));
    let fields = interface
        .fields
        .iter()
        .filter_map(|field_id| {
            get_schema_explorer_field(
                *field_id,
                interface_type_name,
                schema,
                documentation,
                filter,
            )
        })
        .collect::<Vec<_>>();

    let subinterfaces = interface
        .interfaces
        .iter()
        .map(|interface_id| {
            get_empty_schema_explorer_type_reference(
                Type::Interface(*interface_id),
                schema,
                documentation,
            )
        })
        .collect::<Vec<_>>();

    SchemaExplorerInterface {
        implementing_objects,
        fields,
        interfaces: subinterfaces,
        is_extension: interface.is_extension,
        directives: get_schema_explorer_directives(&interface.directives),
    }
}

fn get_schema_explorer_input_object(
    input_object_id: InputObjectID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
) -> SchemaExplorerInputObject {
    let input_object = schema.input_object(input_object_id);
    let arguments = input_object
        .fields
        .iter()
        .map(|arg| {
            let type_reference =
                get_empty_schema_explorer_type_reference(arg.type_.inner(), schema, documentation);

            SchemaExplorerFieldArgument {
                argument_name: arg.name.to_string(),
                argument_description: None,
                default_value: arg.default_value.as_ref().map(|value| value.to_string()),
                rendered_type_name: schema.get_type_string(&arg.type_),
                type_reference,
            }
        })
        .collect::<Vec<_>>();

    SchemaExplorerInputObject {
        arguments,
        directives: get_schema_explorer_directives(&input_object.directives),
    }
}

fn get_schema_explorer_object(
    object_id: ObjectID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
    count: Option<usize>,
) -> SchemaExplorerObject {
    let object = schema.object(object_id);
    let object_type_name = schema.get_type_name(Type::Object(object_id));
    let fields = object
        .fields
        .iter()
        .filter_map(|field_id| {
            get_schema_explorer_field(*field_id, object_type_name, schema, documentation, filter)
        })
        .take(count.unwrap_or(DEFAULT_COUNT))
        .collect::<Vec<_>>();

    let interfaces = object
        .interfaces
        .iter()
        .map(|interface_id| {
            get_empty_schema_explorer_type_reference(
                Type::Interface(*interface_id),
                schema,
                documentation,
            )
        })
        .collect::<Vec<_>>();

    SchemaExplorerObject {
        fields,
        interfaces,
        is_extension: object.is_extension,
        directives: get_schema_explorer_directives(&object.directives),
    }
}

fn get_schema_explorer_union(
    union_id: UnionID,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
) -> SchemaExplorerUnion {
    let union_ = schema.union(union_id);

    let members = union_
        .members
        .iter()
        .map(|id| {
            get_empty_schema_explorer_type_reference(Type::Object(*id), schema, documentation)
        })
        .collect::<Vec<_>>();

    SchemaExplorerUnion {
        members,
        is_extension: union_.is_extension,
        directives: get_schema_explorer_directives(&union_.directives),
    }
}

fn get_schema_explorer_scalar(scalar_id: ScalarID, schema: &SDLSchema) -> SchemaExplorerScalar {
    let scalar = schema.scalar(scalar_id);

    SchemaExplorerScalar {
        is_extension: scalar.is_extension,
        directives: get_schema_explorer_directives(&scalar.directives),
    }
}

fn get_schema_explorer_field(
    field_id: FieldID,
    parent_type_name: StringKey,
    schema: &SDLSchema,
    documentation: &impl SchemaDocumentation,
    filter: &Option<String>,
) -> Option<SchemaExplorerField> {
    let field = schema.field(field_id);
    let field_name = field.name.to_string();

    let field_type_name = schema.get_type_name(field.type_.inner()).to_string();

    let field_type_description = documentation
        .get_type_description(&field_type_name)
        .map(|field_type_description| field_type_description.to_string());
    let field_description = documentation
        .get_field_description(parent_type_name.lookup(), &field_name)
        .map(|field_description| field_description.to_string());

    if let Some(filter) = filter {
        if !field_passes_filter(
            filter,
            field_name.to_lowercase(),
            field_type_name.to_lowercase(),
            field_type_description.as_ref().map(|s| s.to_lowercase()),
        ) {
            return None;
        }
    }

    let type_reference =
        get_empty_schema_explorer_type_reference(field.type_.inner(), schema, documentation);

    let arguments = field
        .arguments
        .iter()
        .map(|arg| {
            let type_reference =
                get_empty_schema_explorer_type_reference(arg.type_.inner(), schema, documentation);

            let argument_description = documentation
                .get_field_argument_description(
                    parent_type_name.lookup(),
                    &field_name,
                    arg.name.lookup(),
                )
                .map(|field_argument_description| field_argument_description.to_string());

            SchemaExplorerFieldArgument {
                argument_name: arg.name.to_string(),
                argument_description,
                rendered_type_name: schema.get_type_string(&arg.type_),
                type_reference,
                default_value: arg.default_value.as_ref().map(|value| value.to_string()),
            }
        })
        .collect::<Vec<_>>();

    Some(SchemaExplorerField {
        field_name,
        field_description,
        type_reference,
        rendered_type_name: schema.get_type_string(&field.type_),
        arguments,
        is_extension: field.is_extension,
        directives: get_schema_explorer_directives(&field.directives),
    })
}

fn field_passes_filter(
    filter: &str,
    field_name: String,
    field_type_name: String,
    field_type_description: Option<String>,
) -> bool {
    if field_name.contains(filter) || field_type_name.contains(filter) {
        true
    } else if let Some(field_type_description) = field_type_description {
        field_type_description.contains(filter)
    } else {
        false
    }
}

fn get_schema_explorer_directives(directives: &[DirectiveValue]) -> Vec<SchemaExplorerDirective> {
    directives
        .iter()
        .map(|directive| -> SchemaExplorerDirective {
            SchemaExplorerDirective {
                directive_name: directive.name.to_string(),
                arguments: directive
                    .arguments
                    .iter()
                    .map(|arg| SchemaExplorerDirectiveArgument {
                        argument_name: arg.name.to_string(),
                        value: arg.value.to_string(),
                    })
                    .collect::<Vec<_>>(),
            }
        })
        .collect::<Vec<_>>()
}

fn get_kind(type_: Type) -> String {
    match type_ {
        Type::Enum(_) => "Enum".to_string(),
        Type::Union(_) => "Union".to_string(),
        Type::InputObject(_) => "InputObject".to_string(),
        Type::Object(_) => "Object".to_string(),
        Type::Scalar(_) => "Scalar".to_string(),
        Type::Interface(_) => "Interface".to_string(),
    }
}
<|endoftext|>


<commit_before>use num_bigint::BigInt;
use num_traits::ToPrimitive;
use std::str::FromStr;

mod precision;
use self::precision::*;

pub mod error;
pub use self::error::*;

pub const MAX_STR_LEN: usize = 26;
pub const MAX_PRECISION: u8 = 5;

#[derive(Copy, Clone, Debug, Default, PartialEq, PartialOrd)]
pub struct Asset {
    pub amount: i64,
}

impl Asset {
    #[inline]
    pub const fn new(amount: i64) -> Asset {
        Asset { amount }
    }

    #[inline]
    pub fn checked_add(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_add(other.amount)?,
        })
    }

    #[inline]
    pub fn checked_sub(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_sub(other.amount)?,
        })
    }

    pub fn checked_mul(self, other: Self) -> Option<Self> {
        const MUL_PRECISION: u8 = MAX_PRECISION * 2;
        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, MUL_PRECISION, MAX_PRECISION)?;
        if final_mul > i128::from(::std::i64::MAX) {
            return None;
        }
        Some(Asset {
            amount: final_mul as i64,
        })
    }

    pub fn checked_div(self, other: Self) -> Option<Self> {
        if other.amount == 0 {
            return None;
        }
        const DIV_PRECISION: u8 = MAX_PRECISION * 2;
        let a = set_decimals_i64(self.amount, MAX_PRECISION, DIV_PRECISION)?;
        Some(Asset {
            amount: a.checked_div(other.amount)?,
        })
    }

    pub fn checked_pow(self, num: u16) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, MAX_PRECISION)?,
            });
        }

        let decimals = u16::from(MAX_PRECISION).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = num;
            loop {
                if exp & 1 == 1 {
                    res = &res * &base;
                }
                exp >>= 1;
                if exp == 0 {
                    break;
                };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(MAX_PRECISION));
        Some(Asset {
            amount: res.to_i64()?,
        })
    }
}

impl ToString for Asset {
    fn to_string(&self) -> String {
        let mut s = self.amount.to_string();
        {
            let len = s.len();
            if len < MAX_PRECISION as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = MAX_PRECISION as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == MAX_PRECISION as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (MAX_PRECISION as usize), '.');
            }
        }
        s.push_str(" GRAEL");
        s
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > MAX_STR_LEN {
            return Err(AssetError {
                kind: AssetErrorKind::StrTooLarge,
            });
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        match split.next() {
            Some(x) => {
                match x.find('.') {
                    Some(pos) => {
                        // Check decimal precision is correct
                        {
                            let decimals = {
                                let len = x.len() - 1;
                                if pos > 0 {
                                    (len - pos) as u8
                                } else {
                                    len as u8
                                }
                            };

                            if decimals != MAX_PRECISION {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidFormat,
                                });
                            }
                        }

                        // Actually parse the amount
                        amount = match x.replace('.', "").parse() {
                            Ok(x) => x,
                            Err(_) => {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidAmount,
                                });
                            }
                        }
                    }
                    None => {
                        return Err(AssetError {
                            kind: AssetErrorKind::InvalidFormat,
                        });
                    }
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        match split.next() {
            Some(x) => {
                if x != "GRAEL" {
                    return Err(AssetError {
                        kind: AssetErrorKind::InvalidAssetType,
                    });
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        Ok(Asset { amount })
    }
}

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

    #[test]
    fn parse_valid_input() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.amount.to_string(), amount);
        };

        c(get_asset("1.00000 GRAEL"), "100000");
        c(get_asset("-1.00000 GRAEL"), "-100000");
        c(get_asset(".10000 GRAEL"), "10000");
        c(get_asset("-.10000 GRAEL"), "-10000");
        c(get_asset("0.10000 GRAEL"), "10000");
        c(get_asset("0.00000 GRAEL"), "0");
        c(get_asset("-0.00000 GRAEL"), "0");
    }

    #[test]
    fn asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(asset.to_string(), s);
        };
        c(get_asset("1.00001 GRAEL"), "1.00001 GRAEL");
        c(get_asset("0.00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset("0.00010 GRAEL"), "0.00010 GRAEL");
        c(get_asset("-0.00001 GRAEL"), "-0.00001 GRAEL");
        c(get_asset(".00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset(".10000 GRAEL"), "0.10000 GRAEL");
        c(get_asset("1.00000 GRAEL"), "1.00000 GRAEL");
    }

    #[test]
    fn fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1b10.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("a100.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("100.0000a GRAEL", AssetErrorKind::InvalidAmount);

        c("1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1. GRAEL", AssetErrorKind::InvalidFormat);
        c(".1 GRAEL", AssetErrorKind::InvalidFormat);
        c("-.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("0.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-0.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-1.0 GRAEL", AssetErrorKind::InvalidFormat);

        c("123456789012345678901 GRAEL", AssetErrorKind::StrTooLarge);
        c("1.000000 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0000", AssetErrorKind::InvalidFormat);

        c("1.00000 GRAEL a", AssetErrorKind::InvalidAssetType);
        c("1.00000 grael", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn perform_arithmetic() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.to_string(), amount);
        };

        let a = get_asset("123.45600 GRAEL");
        c(
            a.checked_add(get_asset("2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset("-2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset(".00001 GRAEL")).unwrap(),
            "123.45601 GRAEL",
        );
        c(
            a.checked_sub(get_asset("2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_sub(get_asset("-2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_mul(get_asset("100000.11111 GRAEL")).unwrap(),
            "12345613.71719 GRAEL",
        );
        c(
            a.checked_mul(get_asset("-100000.11111 GRAEL")).unwrap(),
            "-12345613.71719 GRAEL",
        );
        c(
            a.checked_div(get_asset("23.00000 GRAEL")).unwrap(),
            "5.36765 GRAEL",
        );
        c(
            a.checked_div(get_asset("-23.00000 GRAEL")).unwrap(),
            "-5.36765 GRAEL",
        );
        c(a.checked_pow(2).unwrap(), "15241.38393 GRAEL");
        c(a.checked_pow(3).unwrap(), "1881640.29520 GRAEL");
        c(a, "123.45600 GRAEL");

        c(
            get_asset("1.00020 GRAEL").checked_pow(1000).unwrap(),
            "1.22137 GRAEL",
        );
        c(
            get_asset("10.00000 GRAEL")
                .checked_div(get_asset("2.00000 GRAEL"))
                .unwrap(),
            "5.00000 GRAEL",
        );
        c(
            get_asset("5.00000 GRAEL")
                .checked_div(get_asset("10.00000 GRAEL"))
                .unwrap(),
            "0.50000 GRAEL",
        );

        assert!(a.checked_div(get_asset("0.00000 GRAEL")).is_none());
    }

    #[test]
    fn invalid_arithmetic() {
        let a = get_asset("10.00000 GRAEL");
        let b = get_asset("92233720368547.75807 GRAEL");

        assert_eq!(a.checked_add(b), None);
        assert_eq!(a.checked_mul(Asset::new(-1)).unwrap().checked_sub(b), None);
        assert_eq!(a.checked_div(Asset::new(0)), None);
        assert_eq!(a.checked_mul(b), None);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<commit_msg>Manually impl Debug for Asset for friendly printing<commit_after>use num_bigint::BigInt;
use num_traits::ToPrimitive;
use std::{fmt, str::FromStr};

mod precision;
use self::precision::*;

pub mod error;
pub use self::error::*;

pub const MAX_STR_LEN: usize = 26;
pub const MAX_PRECISION: u8 = 5;

#[derive(Copy, Clone, Default, PartialEq, PartialOrd)]
pub struct Asset {
    pub amount: i64,
}

impl Asset {
    #[inline]
    pub const fn new(amount: i64) -> Asset {
        Asset { amount }
    }

    #[inline]
    pub fn checked_add(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_add(other.amount)?,
        })
    }

    #[inline]
    pub fn checked_sub(self, other: Self) -> Option<Self> {
        Some(Asset {
            amount: self.amount.checked_sub(other.amount)?,
        })
    }

    pub fn checked_mul(self, other: Self) -> Option<Self> {
        const MUL_PRECISION: u8 = MAX_PRECISION * 2;
        let mul = i128::from(self.amount).checked_mul(i128::from(other.amount))?;
        let final_mul = set_decimals_i128(mul, MUL_PRECISION, MAX_PRECISION)?;
        if final_mul > i128::from(::std::i64::MAX) {
            return None;
        }
        Some(Asset {
            amount: final_mul as i64,
        })
    }

    pub fn checked_div(self, other: Self) -> Option<Self> {
        if other.amount == 0 {
            return None;
        }
        const DIV_PRECISION: u8 = MAX_PRECISION * 2;
        let a = set_decimals_i64(self.amount, MAX_PRECISION, DIV_PRECISION)?;
        Some(Asset {
            amount: a.checked_div(other.amount)?,
        })
    }

    pub fn checked_pow(self, num: u16) -> Option<Self> {
        if num == 0 {
            return Some(Asset {
                amount: set_decimals_i64(1, 0, MAX_PRECISION)?,
            });
        }

        let decimals = u16::from(MAX_PRECISION).checked_mul(num)?;
        let mut res = BigInt::from(1);
        {
            let mut base = BigInt::from(self.amount);
            let mut exp = num;
            loop {
                if exp & 1 == 1 {
                    res = &res * &base;
                }
                exp >>= 1;
                if exp == 0 {
                    break;
                };
                base = &base * &base;
            }
        }

        res = set_decimals_big(&res, decimals, u16::from(MAX_PRECISION));
        Some(Asset {
            amount: res.to_i64()?,
        })
    }
}

impl fmt::Debug for Asset {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
        write!(fmt, "Asset(\"{}\")", self.to_string())
    }
}

impl ToString for Asset {
    fn to_string(&self) -> String {
        let mut s = self.amount.to_string();
        {
            let len = s.len();
            if len < MAX_PRECISION as usize {
                let start = if self.amount < 0 { 1 } else { 0 };
                let diff = MAX_PRECISION as usize - len + start;
                s.insert_str(start, "0.");
                s.insert_str(start + 2, &"0".repeat(diff));
            } else if len == MAX_PRECISION as usize {
                s.insert_str(0, "0.");
            } else {
                s.insert(len - (MAX_PRECISION as usize), '.');
            }
        }
        s.push_str(" GRAEL");
        s
    }
}

impl FromStr for Asset {
    type Err = AssetError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        if s.len() > MAX_STR_LEN {
            return Err(AssetError {
                kind: AssetErrorKind::StrTooLarge,
            });
        }
        let mut split = s.trim().splitn(2, ' ');

        let amount: i64;
        match split.next() {
            Some(x) => {
                match x.find('.') {
                    Some(pos) => {
                        // Check decimal precision is correct
                        {
                            let decimals = {
                                let len = x.len() - 1;
                                if pos > 0 {
                                    (len - pos) as u8
                                } else {
                                    len as u8
                                }
                            };

                            if decimals != MAX_PRECISION {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidFormat,
                                });
                            }
                        }

                        // Actually parse the amount
                        amount = match x.replace('.', "").parse() {
                            Ok(x) => x,
                            Err(_) => {
                                return Err(AssetError {
                                    kind: AssetErrorKind::InvalidAmount,
                                });
                            }
                        }
                    }
                    None => {
                        return Err(AssetError {
                            kind: AssetErrorKind::InvalidFormat,
                        });
                    }
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        match split.next() {
            Some(x) => {
                if x != "GRAEL" {
                    return Err(AssetError {
                        kind: AssetErrorKind::InvalidAssetType,
                    });
                }
            }
            None => {
                return Err(AssetError {
                    kind: AssetErrorKind::InvalidFormat,
                });
            }
        };

        Ok(Asset { amount })
    }
}

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

    #[test]
    fn parse_valid_input() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.amount.to_string(), amount);
        };

        c(get_asset("1.00000 GRAEL"), "100000");
        c(get_asset("-1.00000 GRAEL"), "-100000");
        c(get_asset(".10000 GRAEL"), "10000");
        c(get_asset("-.10000 GRAEL"), "-10000");
        c(get_asset("0.10000 GRAEL"), "10000");
        c(get_asset("0.00000 GRAEL"), "0");
        c(get_asset("-0.00000 GRAEL"), "0");
    }

    #[test]
    fn asset_to_str() {
        let c = |asset: Asset, s: &str| {
            assert_eq!(asset.to_string(), s);
        };
        c(get_asset("1.00001 GRAEL"), "1.00001 GRAEL");
        c(get_asset("0.00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset("0.00010 GRAEL"), "0.00010 GRAEL");
        c(get_asset("-0.00001 GRAEL"), "-0.00001 GRAEL");
        c(get_asset(".00001 GRAEL"), "0.00001 GRAEL");
        c(get_asset(".10000 GRAEL"), "0.10000 GRAEL");
        c(get_asset("1.00000 GRAEL"), "1.00000 GRAEL");
    }

    #[test]
    fn fail_parsing_invalid_input() {
        let c = |asset: &str, err: AssetErrorKind| {
            let e = Asset::from_str(asset).err().unwrap();
            assert_eq!(e.kind, err);
        };

        c("1b10.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("a100.00000 GRAEL", AssetErrorKind::InvalidAmount);
        c("100.0000a GRAEL", AssetErrorKind::InvalidAmount);

        c("1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1. GRAEL", AssetErrorKind::InvalidFormat);
        c(".1 GRAEL", AssetErrorKind::InvalidFormat);
        c("-.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("0.1 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-0.0 GRAEL", AssetErrorKind::InvalidFormat);
        c("-1.0 GRAEL", AssetErrorKind::InvalidFormat);

        c("123456789012345678901 GRAEL", AssetErrorKind::StrTooLarge);
        c("1.000000 GRAEL", AssetErrorKind::InvalidFormat);
        c("1.0000", AssetErrorKind::InvalidFormat);

        c("1.00000 GRAEL a", AssetErrorKind::InvalidAssetType);
        c("1.00000 grael", AssetErrorKind::InvalidAssetType);
    }

    #[test]
    fn perform_arithmetic() {
        let c = |asset: Asset, amount: &str| {
            assert_eq!(asset.to_string(), amount);
        };

        let a = get_asset("123.45600 GRAEL");
        c(
            a.checked_add(get_asset("2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset("-2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_add(get_asset(".00001 GRAEL")).unwrap(),
            "123.45601 GRAEL",
        );
        c(
            a.checked_sub(get_asset("2.00000 GRAEL")).unwrap(),
            "121.45600 GRAEL",
        );
        c(
            a.checked_sub(get_asset("-2.00000 GRAEL")).unwrap(),
            "125.45600 GRAEL",
        );
        c(
            a.checked_mul(get_asset("100000.11111 GRAEL")).unwrap(),
            "12345613.71719 GRAEL",
        );
        c(
            a.checked_mul(get_asset("-100000.11111 GRAEL")).unwrap(),
            "-12345613.71719 GRAEL",
        );
        c(
            a.checked_div(get_asset("23.00000 GRAEL")).unwrap(),
            "5.36765 GRAEL",
        );
        c(
            a.checked_div(get_asset("-23.00000 GRAEL")).unwrap(),
            "-5.36765 GRAEL",
        );
        c(a.checked_pow(2).unwrap(), "15241.38393 GRAEL");
        c(a.checked_pow(3).unwrap(), "1881640.29520 GRAEL");
        c(a, "123.45600 GRAEL");

        c(
            get_asset("1.00020 GRAEL").checked_pow(1000).unwrap(),
            "1.22137 GRAEL",
        );
        c(
            get_asset("10.00000 GRAEL")
                .checked_div(get_asset("2.00000 GRAEL"))
                .unwrap(),
            "5.00000 GRAEL",
        );
        c(
            get_asset("5.00000 GRAEL")
                .checked_div(get_asset("10.00000 GRAEL"))
                .unwrap(),
            "0.50000 GRAEL",
        );

        assert!(a.checked_div(get_asset("0.00000 GRAEL")).is_none());
    }

    #[test]
    fn invalid_arithmetic() {
        let a = get_asset("10.00000 GRAEL");
        let b = get_asset("92233720368547.75807 GRAEL");

        assert_eq!(a.checked_add(b), None);
        assert_eq!(a.checked_mul(Asset::new(-1)).unwrap().checked_sub(b), None);
        assert_eq!(a.checked_div(Asset::new(0)), None);
        assert_eq!(a.checked_mul(b), None);
    }

    fn get_asset(s: &str) -> Asset {
        Asset::from_str(s).unwrap()
    }
}
<|endoftext|>


<commit_before>use super::Position;
use super::Range;
use std::str::from_utf8;

pub struct GapBuffer {
    data: Vec<u8>,
    gap_start: usize,
    gap_length: usize,
}

pub fn new(mut data: String) -> GapBuffer {
    let mut bytes = data.into_bytes();
    let capacity = bytes.capacity();
    let gap_start = bytes.len();
    let gap_length = capacity - gap_start;
    unsafe {
        bytes.set_len(capacity);
    }
    GapBuffer{ data: bytes, gap_start: gap_start, gap_length: gap_length }
}

impl GapBuffer {
    // TODO: Return an optional error.
    pub fn insert(&mut self, data: &str, position: &Position) {
        // Ensure we have the capacity to insert this data.
        if data.len() > self.gap_length {
            // TODO: Move gap to the end before resizing buffer.
            self.data.reserve(data.len());
            let capacity = self.data.capacity();
            self.gap_length = capacity - self.gap_start;
            unsafe {
                self.data.set_len(capacity);
            }
        }

        let offset = match self.find_offset(position) {
            Some(o) => o,
            None => return,
        };

        self.move_gap(offset);
        self.write_to_gap(data);
    }

    pub fn to_string(&self) -> String {
        from_utf8(&self.data[..self.gap_start]).unwrap().to_string() + from_utf8(&self.data[self.gap_start+self.gap_length..]).unwrap()
    }

    pub fn remove(&mut self, range: &Range) {
        let start_offset = match self.find_offset(&range.start) {
            Some(o) => o,
            None => return,
        };
        self.move_gap(start_offset);

        let end_offset = match self.find_offset(&range.end) {
            Some(o) => o,
            None => return,
        };
        self.gap_length=end_offset-start_offset;
    }

    // Maps a position to its offset equivalent in the data.
    fn find_offset(&self, position: &Position) -> Option<usize> {
        let first_half = from_utf8(&self.data[..self.gap_start]).unwrap();
        let mut line = 0;
        let mut line_offset = 0;

        for char_index in first_half.char_indices() {
            let (offset, character) = char_index;

            // Check to see if we've found the position yet.
            if line == position.line && line_offset == position.offset {
                return Some(offset);
            }

            // Advance the line and offset characters.
            if character == '\n' {
                line+=1;
                line_offset = 0;
            } else {
                line_offset+=1;
            }
        }

        // We didn't find the position *within* the first half, but it could
        // be right after it, which means it's right at the start of the gap.
        if line == position.line && line_offset == position.offset {
            return Some(self.gap_start);
        }

        // We haven't reached the position yet, so we'll move on to the other half.
        let second_half = from_utf8(&self.data[self.gap_start+self.gap_length..]).unwrap();
        for char_index in second_half.char_indices() {
            let (offset, character) = char_index;

            // Check to see if we've found the position yet.
            if line == position.line && line_offset == position.offset {
                return Some(self.gap_start + self.gap_length + offset);
            }

            // Advance the line and offset characters.
            if character == '\n' {
                line+=1;
                line_offset = 0;
            } else {
                line_offset+=1;
            }
        }

        // We didn't find the position *within* the second half, but it could
        // be right after it, which means it's at the end of the buffer.
        if line == position.line && line_offset == position.offset {
            return Some(self.data.len());
        }

        None
    }

    fn move_gap(&mut self, offset: usize) {
        // We don't need to move any data if the buffer is at capacity.
        if self.gap_length == 0 {
            self.gap_start = offset;
            return;
        }

        if offset < self.gap_start {
            // Shift the gap to the left one byte at a time.
            for index in (offset..self.gap_start) {
                self.data[index + self.gap_length] = self.data[index];
                self.data[index] = 0;
            }

            self.gap_start = offset;
        } else if offset > self.gap_start {
            // Shift the gap to the right one byte at a time.
            for index in (self.gap_start+self.gap_length..offset) {
                self.data[index-self.gap_length] = self.data[index];
                self.data[index] = 0;
            }

            // Because the offset was after the gap, its value included the
            // gap length. We must remove it to determine the starting point.
            self.gap_start = offset - self.gap_length;
        }
    }

    fn write_to_gap(&mut self, data: &str) {
        for byte in data.bytes() {
            self.data[self.gap_start] = byte;
            self.gap_start+=1;
            self.gap_length-=1;
        }
    }
}

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

    #[test]
    fn move_gap_works() {
        let mut gb = new("This is a test.".to_string());
        gb.move_gap(0);
        assert_eq!(gb.to_string(), "This is a test.");
    }

    #[test]
    fn inserting_at_the_start_works() {
        let mut gb = new("This is a test.".to_string());
        gb.insert("Hi. ", &Position { line: 0, offset: 0 });
        assert_eq!(gb.to_string(), "Hi. This is a test.");
    }

    #[test]
    fn inserting_in_the_middle_works() {
        let mut gb = new("This is a test.\nPlease be gentle.".to_string());
        gb.insert(" very", &Position { line: 1, offset: 9 });
        assert_eq!(gb.to_string(), "This is a test.\nPlease be very gentle.");
    }

    #[test]
    fn inserting_at_the_end_works() {
        let mut gb = new("This is a test.".to_string());
        gb.insert(" Seriously.", &Position { line: 0, offset: 15 });
        assert_eq!(gb.to_string(), "This is a test. Seriously.");
    }

    #[test]
    fn inserting_in_different_spots_twice_works() {
        let mut gb = new("This is a test.".to_string());
        gb.insert("Hi. ", &Position { line: 0, offset: 0 });
        gb.insert(" Thank you.", &Position { line: 0, offset: 19 });
        assert_eq!(gb.to_string(), "Hi. This is a test. Thank you.");
    }

    #[test]
    fn inserting_at_an_invalid_position_does_nothing() {
        let mut gb = new("This is a test.".to_string());
        gb.insert(" Seriously.", &Position { line: 0, offset: 35 });
        assert_eq!(gb.to_string(), "This is a test.");
    }

    #[test]
    fn removing_works() {
        let mut gb = new("This is a test.\nSee what happens.".to_string());
        let start = Position{ line: 0, offset: 8 };
        let end = Position{ line: 1, offset: 4 };
        gb.remove(&Range{ start: start, end: end });
        assert_eq!(gb.to_string(), "This is what happens.");
    }
}
<commit_msg>Add documentation to buffer insert method.<commit_after>use super::Position;
use super::Range;
use std::str::from_utf8;

pub struct GapBuffer {
    data: Vec<u8>,
    gap_start: usize,
    gap_length: usize,
}

pub fn new(mut data: String) -> GapBuffer {
    let mut bytes = data.into_bytes();
    let capacity = bytes.capacity();
    let gap_start = bytes.len();
    let gap_length = capacity - gap_start;
    unsafe {
        bytes.set_len(capacity);
    }
    GapBuffer{ data: bytes, gap_start: gap_start, gap_length: gap_length }
}

impl GapBuffer {
    /// Inserts the specified data into the buffer at the specified position.
    /// The buffer will reallocate if there is insufficient space. If the
    /// position is out of bounds, the buffer contents will remain unchanged.
    ///
    /// # Examples
    ///
    /// let mut b = new("my buffer data");
    /// b.insert(" changed", Position{ line: 0, offset: 2});
    /// assert_eq!("my changed buffer data", b.to_string());
    ///
    pub fn insert(&mut self, data: &str, position: &Position) {
        // Ensure we have the capacity to insert this data.
        if data.len() > self.gap_length {
            // TODO: Move gap to the end before resizing buffer.
            self.data.reserve(data.len());
            let capacity = self.data.capacity();
            self.gap_length = capacity - self.gap_start;
            unsafe {
                self.data.set_len(capacity);
            }
        }

        let offset = match self.find_offset(position) {
            Some(o) => o,
            None => return,
        };

        self.move_gap(offset);
        self.write_to_gap(data);
    }

    pub fn to_string(&self) -> String {
        from_utf8(&self.data[..self.gap_start]).unwrap().to_string() + from_utf8(&self.data[self.gap_start+self.gap_length..]).unwrap()
    }

    pub fn remove(&mut self, range: &Range) {
        let start_offset = match self.find_offset(&range.start) {
            Some(o) => o,
            None => return,
        };
        self.move_gap(start_offset);

        let end_offset = match self.find_offset(&range.end) {
            Some(o) => o,
            None => return,
        };
        self.gap_length=end_offset-start_offset;
    }

    // Maps a position to its offset equivalent in the data.
    fn find_offset(&self, position: &Position) -> Option<usize> {
        let first_half = from_utf8(&self.data[..self.gap_start]).unwrap();
        let mut line = 0;
        let mut line_offset = 0;

        for char_index in first_half.char_indices() {
            let (offset, character) = char_index;

            // Check to see if we've found the position yet.
            if line == position.line && line_offset == position.offset {
                return Some(offset);
            }

            // Advance the line and offset characters.
            if character == '\n' {
                line+=1;
                line_offset = 0;
            } else {
                line_offset+=1;
            }
        }

        // We didn't find the position *within* the first half, but it could
        // be right after it, which means it's right at the start of the gap.
        if line == position.line && line_offset == position.offset {
            return Some(self.gap_start);
        }

        // We haven't reached the position yet, so we'll move on to the other half.
        let second_half = from_utf8(&self.data[self.gap_start+self.gap_length..]).unwrap();
        for char_index in second_half.char_indices() {
            let (offset, character) = char_index;

            // Check to see if we've found the position yet.
            if line == position.line && line_offset == position.offset {
                return Some(self.gap_start + self.gap_length + offset);
            }

            // Advance the line and offset characters.
            if character == '\n' {
                line+=1;
                line_offset = 0;
            } else {
                line_offset+=1;
            }
        }

        // We didn't find the position *within* the second half, but it could
        // be right after it, which means it's at the end of the buffer.
        if line == position.line && line_offset == position.offset {
            return Some(self.data.len());
        }

        None
    }

    fn move_gap(&mut self, offset: usize) {
        // We don't need to move any data if the buffer is at capacity.
        if self.gap_length == 0 {
            self.gap_start = offset;
            return;
        }

        if offset < self.gap_start {
            // Shift the gap to the left one byte at a time.
            for index in (offset..self.gap_start) {
                self.data[index + self.gap_length] = self.data[index];
                self.data[index] = 0;
            }

            self.gap_start = offset;
        } else if offset > self.gap_start {
            // Shift the gap to the right one byte at a time.
            for index in (self.gap_start+self.gap_length..offset) {
                self.data[index-self.gap_length] = self.data[index];
                self.data[index] = 0;
            }

            // Because the offset was after the gap, its value included the
            // gap length. We must remove it to determine the starting point.
            self.gap_start = offset - self.gap_length;
        }
    }

    fn write_to_gap(&mut self, data: &str) {
        for byte in data.bytes() {
            self.data[self.gap_start] = byte;
            self.gap_start+=1;
            self.gap_length-=1;
        }
    }
}

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

    #[test]
    fn move_gap_works() {
        let mut gb = new("This is a test.".to_string());
        gb.move_gap(0);
        assert_eq!(gb.to_string(), "This is a test.");
    }

    #[test]
    fn inserting_at_the_start_works() {
        let mut gb = new("This is a test.".to_string());
        gb.insert("Hi. ", &Position { line: 0, offset: 0 });
        assert_eq!(gb.to_string(), "Hi. This is a test.");
    }

    #[test]
    fn inserting_in_the_middle_works() {
        let mut gb = new("This is a test.\nPlease be gentle.".to_string());
        gb.insert(" very", &Position { line: 1, offset: 9 });
        assert_eq!(gb.to_string(), "This is a test.\nPlease be very gentle.");
    }

    #[test]
    fn inserting_at_the_end_works() {
        let mut gb = new("This is a test.".to_string());
        gb.insert(" Seriously.", &Position { line: 0, offset: 15 });
        assert_eq!(gb.to_string(), "This is a test. Seriously.");
    }

    #[test]
    fn inserting_in_different_spots_twice_works() {
        let mut gb = new("This is a test.".to_string());
        gb.insert("Hi. ", &Position { line: 0, offset: 0 });
        gb.insert(" Thank you.", &Position { line: 0, offset: 19 });
        assert_eq!(gb.to_string(), "Hi. This is a test. Thank you.");
    }

    #[test]
    fn inserting_at_an_invalid_position_does_nothing() {
        let mut gb = new("This is a test.".to_string());
        gb.insert(" Seriously.", &Position { line: 0, offset: 35 });
        assert_eq!(gb.to_string(), "This is a test.");
    }

    #[test]
    fn removing_works() {
        let mut gb = new("This is a test.\nSee what happens.".to_string());
        let start = Position{ line: 0, offset: 8 };
        let end = Position{ line: 1, offset: 4 };
        gb.remove(&Range{ start: start, end: end });
        assert_eq!(gb.to_string(), "This is what happens.");
    }
}
<|endoftext|>