tc-transact 0.21.1

Traits and locking utilities for a TinyChain transaction.
Documentation
use std::fmt;

use futures::future;
use safecast::{TryCastFrom, TryCastInto};

use tc_error::*;
use tc_value::TCString;
use tcgeneric::{Id, TCPath, Tuple};

use super::{
    DeleteHandler, GetHandler, Handler, HandlerType, PostHandler, PutHandler, StateInstance,
};

pub struct EchoHandler;

impl<'a, State> Handler<'a, State> for EchoHandler
where
    State: StateInstance,
{
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, key| {
            Box::pin(async move { Ok(key.into()) })
        }))
    }
}

pub struct ErrorHandler<'a> {
    code: &'a Id,
}

impl<'a, State> Handler<'a, State> for ErrorHandler<'a>
where
    State: StateInstance,
    TCString: TryCastFrom<State>,
    Tuple<TCString>: TryCastFrom<State>,
{
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, key| {
            Box::pin(async move {
                let message: TCString =
                    key.try_cast_into(|v| TCError::unexpected(v, "an error message"))?;

                if let Some(err_type) = error_type(self.code) {
                    Err(TCError::new(err_type, message.to_string()))
                } else {
                    Err(TCError::not_found(self.code))
                }
            })
        }))
    }

    fn post<'b>(self: Box<Self>) -> Option<PostHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, mut params| {
            Box::pin(async move {
                let message: TCString = params.require("message")?;
                let stack: Tuple<TCString> = params.require("stack")?;
                params.expect_empty()?;

                if let Some(err_type) = error_type(self.code) {
                    Err(TCError::with_stack(err_type, message, stack))
                } else {
                    Err(TCError::not_found(self.code))
                }
            })
        }))
    }
}

impl<'a> From<&'a Id> for ErrorHandler<'a> {
    fn from(code: &'a Id) -> Self {
        Self { code }
    }
}

pub struct AttributeHandler<T> {
    attribute: T,
}

impl<'a, State, T> Handler<'a, State> for AttributeHandler<T>
where
    State: StateInstance + From<T>,
    T: Clone + Send + Sync + 'a,
{
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, key| {
            Box::pin(async move {
                if key.is_none() {
                    Ok(self.attribute.into())
                } else {
                    Err(TCError::not_found(format!("attribute {key:?}")))
                }
            })
        }))
    }
}

impl<T> From<T> for AttributeHandler<T> {
    fn from(attribute: T) -> Self {
        Self { attribute }
    }
}

pub struct MethodNotAllowedHandler;

impl<'a, State: StateInstance> Handler<'a, State> for MethodNotAllowedHandler {
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(move |_txn, _key| {
            Box::pin(future::ready(Err(TCError::method_not_allowed(
                HandlerType::Get,
                TCPath::default(),
            ))))
        }))
    }

    fn put<'b>(self: Box<Self>) -> Option<PutHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(move |_txn, _key, _value| {
            Box::pin(future::ready(Err(TCError::method_not_allowed(
                HandlerType::Put,
                TCPath::default(),
            ))))
        }))
    }

    fn post<'b>(self: Box<Self>) -> Option<PostHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(move |_txn, _key| {
            Box::pin(future::ready(Err(TCError::method_not_allowed(
                HandlerType::Post,
                TCPath::default(),
            ))))
        }))
    }

    fn delete<'b>(self: Box<Self>) -> Option<DeleteHandler<'a, 'b, State::Txn>>
    where
        'b: 'a,
    {
        Some(Box::new(move |_txn, _key| {
            Box::pin(future::ready(Err(TCError::method_not_allowed(
                HandlerType::Delete,
                TCPath::default(),
            ))))
        }))
    }
}

pub struct SelfHandler<'a, T> {
    subject: &'a T,
}

impl<'a, State, T> Handler<'a, State> for SelfHandler<'a, T>
where
    State: StateInstance + From<T>,
    T: Clone + Send + Sync + fmt::Debug,
{
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, key| {
            Box::pin(async move {
                if key.is_none() {
                    Ok(self.subject.clone().into())
                } else {
                    Err(TCError::not_found(format!(
                        "attribute {:?} of {:?}",
                        key, self.subject
                    )))
                }
            })
        }))
    }
}

impl<'a, T> From<&'a T> for SelfHandler<'a, T> {
    fn from(subject: &'a T) -> Self {
        Self { subject }
    }
}

pub struct SelfHandlerOwned<T> {
    subject: T,
}

impl<'a, State, T> Handler<'a, State> for SelfHandlerOwned<T>
where
    State: StateInstance + From<T>,
    T: Send + Sync + fmt::Debug + 'a,
{
    fn get<'b>(self: Box<Self>) -> Option<GetHandler<'a, 'b, State::Txn, State>>
    where
        'b: 'a,
    {
        Some(Box::new(|_txn, key| {
            Box::pin(async move {
                if key.is_none() {
                    Ok(self.subject.into())
                } else {
                    Err(TCError::not_found(format!(
                        "attribute {:?} of {:?}",
                        key, self.subject
                    )))
                }
            })
        }))
    }
}

impl<'a, T> From<T> for SelfHandlerOwned<T> {
    fn from(subject: T) -> Self {
        Self { subject }
    }
}

fn error_type(err_type: &Id) -> Option<ErrorKind> {
    match err_type.as_str() {
        "bad_gateway" => Some(ErrorKind::BadGateway),
        "bad_request" => Some(ErrorKind::BadRequest),
        "conflict" => Some(ErrorKind::Conflict),
        "forbidden" => Some(ErrorKind::Forbidden),
        "internal" => Some(ErrorKind::Internal),
        "method_not_allowed" => Some(ErrorKind::MethodNotAllowed),
        "not_found" => Some(ErrorKind::NotFound),
        "not_implemented" => Some(ErrorKind::NotImplemented),
        "timeout" => Some(ErrorKind::Timeout),
        "unauthorized" => Some(ErrorKind::Unauthorized),
        _ => None,
    }
}