2020-09-30 10:59:35 -06:00
|
|
|
use crate::{model::Sections, section_runner::SectionRunner};
|
|
|
|
|
2020-09-30 11:59:59 -06:00
|
|
|
use futures_util::ready;
|
2020-09-29 23:25:28 -06:00
|
|
|
use futures_util::FutureExt;
|
|
|
|
use num_derive::FromPrimitive;
|
|
|
|
use serde::{Deserialize, Serialize};
|
2020-09-30 11:59:59 -06:00
|
|
|
use std::{fmt, future::Future, pin::Pin, task::Poll};
|
2020-09-30 10:59:35 -06:00
|
|
|
|
2020-09-30 11:59:59 -06:00
|
|
|
mod sections;
|
2020-09-29 23:25:28 -06:00
|
|
|
|
|
|
|
pub struct RequestContext {
|
|
|
|
pub sections: Sections,
|
|
|
|
pub section_runner: SectionRunner,
|
|
|
|
}
|
|
|
|
|
|
|
|
type BoxFuture<Output> = Pin<Box<dyn Future<Output = Output>>>;
|
|
|
|
|
|
|
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, FromPrimitive)]
|
|
|
|
#[repr(u16)]
|
|
|
|
pub enum ErrorCode {
|
|
|
|
BadRequest = 100,
|
|
|
|
NotSpecified = 101,
|
|
|
|
Parse = 102,
|
|
|
|
Range = 103,
|
|
|
|
InvalidData = 104,
|
|
|
|
BadToken = 105,
|
|
|
|
Unauthorized = 106,
|
|
|
|
NoPermission = 107,
|
|
|
|
NotFound = 109,
|
|
|
|
// NotUnique = 110,
|
2020-09-30 13:48:32 -06:00
|
|
|
NoSuchSection = 120,
|
|
|
|
NoSuchSectionRun = 121,
|
2020-09-29 23:25:28 -06:00
|
|
|
Internal = 200,
|
|
|
|
NotImplemented = 201,
|
|
|
|
Timeout = 300,
|
|
|
|
// ServerDisconnected = 301,
|
|
|
|
// BrokerDisconnected = 302,
|
|
|
|
}
|
|
|
|
|
|
|
|
mod ser {
|
|
|
|
use super::ErrorCode;
|
|
|
|
use num_traits::FromPrimitive;
|
|
|
|
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
|
|
|
|
|
|
|
impl Serialize for ErrorCode {
|
|
|
|
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
|
|
|
|
where
|
|
|
|
S: Serializer,
|
|
|
|
{
|
|
|
|
serializer.serialize_u16(*self as u16)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'de> Deserialize<'de> for ErrorCode {
|
|
|
|
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
|
|
|
where
|
|
|
|
D: Deserializer<'de>,
|
|
|
|
{
|
|
|
|
let prim = u16::deserialize(deserializer)?;
|
|
|
|
ErrorCode::from_u16(prim)
|
|
|
|
.ok_or_else(|| <D::Error as serde::de::Error>::custom("invalid ErrorCode"))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase", tag = "result")]
|
|
|
|
pub struct RequestError {
|
|
|
|
code: ErrorCode,
|
|
|
|
message: String,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
|
|
name: Option<String>,
|
|
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
|
|
cause: Option<String>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl fmt::Display for RequestError {
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
write!(f, "request error (code {:?}", self.code)?;
|
|
|
|
if let Some(name) = &self.name {
|
|
|
|
write!(f, "on {}", name)?;
|
|
|
|
}
|
|
|
|
write!(f, "): {}", self.message)?;
|
|
|
|
if let Some(cause) = &self.cause {
|
|
|
|
write!(f, ", caused by {}", cause)?;
|
|
|
|
}
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for RequestError {}
|
|
|
|
|
|
|
|
impl From<eyre::Report> for RequestError {
|
|
|
|
fn from(report: eyre::Report) -> Self {
|
|
|
|
let mut chain = report.chain();
|
|
|
|
let message = match chain.next() {
|
|
|
|
Some(a) => a.to_string(),
|
|
|
|
None => "unknown error".to_string(),
|
|
|
|
};
|
|
|
|
let cause = chain.fold(None, |cause, err| match cause {
|
|
|
|
Some(cause) => Some(format!("{}: {}", cause, err)),
|
|
|
|
None => Some(err.to_string()),
|
|
|
|
});
|
|
|
|
RequestError::new(ErrorCode::Internal, message, None, cause)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[allow(dead_code)]
|
|
|
|
impl RequestError {
|
|
|
|
pub fn new<M>(code: ErrorCode, message: M, name: Option<String>, cause: Option<String>) -> Self
|
|
|
|
where
|
|
|
|
M: ToString,
|
|
|
|
{
|
|
|
|
Self {
|
|
|
|
code,
|
|
|
|
message: message.to_string(),
|
|
|
|
name,
|
|
|
|
cause,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn simple<M>(code: ErrorCode, message: M) -> Self
|
|
|
|
where
|
|
|
|
M: ToString,
|
|
|
|
{
|
|
|
|
Self::new(code, message, None, None)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_name<M, N>(code: ErrorCode, message: M, name: N) -> Self
|
|
|
|
where
|
|
|
|
M: ToString,
|
|
|
|
N: ToString,
|
|
|
|
{
|
|
|
|
Self::new(code, message, Some(name.to_string()), None)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_cause<M, C>(code: ErrorCode, message: M, cause: C) -> Self
|
|
|
|
where
|
|
|
|
M: ToString,
|
|
|
|
C: ToString,
|
|
|
|
{
|
|
|
|
Self::new(code, message, None, Some(cause.to_string()))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn with_name_and_cause<M, N, C>(code: ErrorCode, message: M, name: N, cause: C) -> Self
|
|
|
|
where
|
|
|
|
M: ToString,
|
|
|
|
N: ToString,
|
|
|
|
C: ToString,
|
|
|
|
{
|
|
|
|
Self::new(
|
|
|
|
code,
|
|
|
|
message,
|
|
|
|
Some(name.to_string()),
|
|
|
|
Some(cause.to_string()),
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-30 11:59:59 -06:00
|
|
|
#[derive(Debug, Serialize, Deserialize)]
|
2020-09-30 13:48:32 -06:00
|
|
|
struct ResponseMessage {
|
|
|
|
message: String,
|
|
|
|
}
|
2020-09-30 11:59:59 -06:00
|
|
|
|
|
|
|
impl ResponseMessage {
|
|
|
|
fn new<M>(message: M) -> Self
|
|
|
|
where
|
|
|
|
M: ToString,
|
|
|
|
{
|
2020-09-30 13:48:32 -06:00
|
|
|
ResponseMessage {
|
|
|
|
message: message.to_string(),
|
|
|
|
}
|
2020-09-30 11:59:59 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<String> for ResponseMessage {
|
|
|
|
fn from(message: String) -> Self {
|
2020-09-30 13:48:32 -06:00
|
|
|
ResponseMessage { message }
|
2020-09-30 11:59:59 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type ResponseValue = serde_json::Value;
|
|
|
|
|
|
|
|
type RequestResult<Ok = ResponseValue> = Result<Ok, RequestError>;
|
|
|
|
type RequestFuture<Ok = ResponseValue> = BoxFuture<RequestResult<Ok>>;
|
2020-09-29 23:25:28 -06:00
|
|
|
|
|
|
|
trait IRequest {
|
2020-09-30 11:59:59 -06:00
|
|
|
type Response: Serialize;
|
|
|
|
|
|
|
|
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture<Self::Response>;
|
|
|
|
|
|
|
|
fn exec_erased(&mut self, ctx: &mut RequestContext) -> RequestFuture
|
|
|
|
where
|
|
|
|
Self::Response: 'static,
|
|
|
|
{
|
|
|
|
// TODO: figure out how to get rid of this nested box
|
|
|
|
Box::pin(ErasedRequestFuture(self.exec(ctx)))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct ErasedRequestFuture<Response>(RequestFuture<Response>)
|
|
|
|
where
|
|
|
|
Response: Serialize;
|
|
|
|
|
|
|
|
impl<Response> Future for ErasedRequestFuture<Response>
|
|
|
|
where
|
|
|
|
Response: Serialize,
|
|
|
|
{
|
|
|
|
type Output = RequestResult;
|
|
|
|
|
|
|
|
fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
|
|
|
|
use eyre::WrapErr;
|
|
|
|
let response = ready!(self.as_mut().0.poll_unpin(cx));
|
|
|
|
Poll::Ready(response.and_then(|res| {
|
|
|
|
serde_json::to_value(res)
|
|
|
|
.wrap_err("could not serialize response")
|
|
|
|
.map_err(RequestError::from)
|
|
|
|
}))
|
|
|
|
}
|
2020-09-29 23:25:28 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
#[serde(rename_all = "camelCase", tag = "result")]
|
|
|
|
pub enum Response {
|
|
|
|
Success(ResponseValue),
|
|
|
|
Error(RequestError),
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<RequestResult> for Response {
|
|
|
|
fn from(res: RequestResult) -> Self {
|
|
|
|
match res {
|
|
|
|
Ok(value) => Response::Success(value),
|
|
|
|
Err(error) => Response::Error(error),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl From<RequestError> for Response {
|
|
|
|
fn from(error: RequestError) -> Self {
|
|
|
|
Response::Error(error)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-09-30 13:48:32 -06:00
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
pub struct WithRequestId<T> {
|
|
|
|
pub rid: i32,
|
|
|
|
#[serde(flatten)]
|
|
|
|
pub rest: T,
|
|
|
|
}
|
|
|
|
|
|
|
|
pub type ResponseWithId = WithRequestId<Response>;
|
|
|
|
|
|
|
|
#[derive(Debug, Deserialize, Serialize)]
|
|
|
|
#[serde(rename_all = "camelCase", tag = "type")]
|
|
|
|
pub enum Request {
|
|
|
|
RunSection(sections::RunSectionRequest),
|
|
|
|
CancelSection(sections::CancelSectionRequest),
|
|
|
|
CancelSectionRunId(sections::CancelSectionRunIdRequest),
|
|
|
|
}
|
|
|
|
|
2020-09-29 23:25:28 -06:00
|
|
|
impl IRequest for Request {
|
2020-09-30 11:59:59 -06:00
|
|
|
type Response = ResponseValue;
|
|
|
|
|
|
|
|
fn exec(&mut self, ctx: &mut RequestContext) -> RequestFuture {
|
2020-09-29 23:25:28 -06:00
|
|
|
match self {
|
2020-09-30 11:59:59 -06:00
|
|
|
Request::RunSection(req) => req.exec_erased(ctx),
|
|
|
|
Request::CancelSection(req) => req.exec_erased(ctx),
|
2020-09-30 13:48:32 -06:00
|
|
|
Request::CancelSectionRunId(req) => req.exec_erased(ctx),
|
2020-09-29 23:25:28 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl Request {
|
|
|
|
pub fn execute(&mut self, ctx: &mut RequestContext) -> impl Future<Output = Response> {
|
|
|
|
self.exec(ctx).map(Response::from)
|
|
|
|
}
|
|
|
|
}
|