use super::errors::err_missing_attribute;
use crate::data::ApplicationData;
use actix_web::web::Json;
use actix_web::{post, web};
use dsntk_common::DsntkError;
use dsntk_feel::Name;
use dsntk_feel::context::FeelContext;
use dsntk_feel::dto::ValueDto;
use dsntk_feel::values::Value;
use dsntk_workspace::Workspaces;
use serde::{Deserialize, Serialize};
use std::borrow::Borrow;
use std::{fmt, io};
#[derive(Deserialize)]
pub struct InputNodeDto {
#[serde(rename = "name")]
pub name: String,
#[serde(rename = "value")]
pub value: ValueDto,
}
#[derive(Serialize)]
pub struct OutputNodeDto {
#[serde(rename = "value")]
pub value: Option<ValueDto>,
}
#[derive(Serialize)]
pub struct TckErrorDto {
#[serde(rename = "detail")]
detail: String,
}
#[derive(Serialize)]
pub struct TckResultDto<T> {
#[serde(rename = "data", skip_serializing_if = "Option::is_none")]
data: Option<T>,
#[serde(rename = "errors", skip_serializing_if = "Vec::is_empty")]
errors: Vec<TckErrorDto>,
}
impl<T> Default for TckResultDto<T> {
fn default() -> Self {
Self { data: None, errors: vec![] }
}
}
impl<T: Serialize> fmt::Display for TckResultDto<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", serde_json::to_string(self).unwrap_or("conversion to JSON failed for ResultDto".to_string()))
}
}
impl<T> TckResultDto<T> {
pub fn data(d: T) -> TckResultDto<T> {
TckResultDto {
data: Some(d),
..Default::default()
}
}
pub fn error(err: impl fmt::Display) -> TckResultDto<T> {
TckResultDto {
errors: vec![TckErrorDto { detail: format!("{err}") }],
..Default::default()
}
}
}
#[derive(Deserialize)]
pub struct TckEvaluateParams {
#[serde(rename = "invocable")]
invocable_path: Option<String>,
#[serde(rename = "input")]
input_values: Option<Vec<InputNodeDto>>,
}
#[post("/tck")]
pub async fn evaluate_tck_post(params: Json<TckEvaluateParams>, data: web::Data<ApplicationData>) -> io::Result<Json<TckResultDto<OutputNodeDto>>> {
let workspace: &Workspaces = data.workspaces.borrow();
match do_evaluate_tck(workspace, params.into_inner()) {
Ok(response) => Ok(Json(TckResultDto::data(response))),
Err(reason) => Ok(Json(TckResultDto::error(reason))),
}
}
fn do_evaluate_tck(workspace: &Workspaces, params: TckEvaluateParams) -> Result<OutputNodeDto, DsntkError> {
if let Some(invocable_path) = params.invocable_path {
if let Some(input_values) = params.input_values {
let input_data = process_input_node_dto_list(input_values)?;
let result = workspace.evaluate(&invocable_path, &input_data)?;
Ok(prepare_output_node_dto(result))
} else {
Err(err_missing_attribute("input"))
}
} else {
Err(err_missing_attribute("invocable"))
}
}
fn process_input_node_dto_list(input_values: Vec<InputNodeDto>) -> Result<FeelContext, DsntkError> {
let mut ctx: FeelContext = Default::default();
for item in input_values {
let name = Name::from(item.name.as_str());
ctx.set_entry(&name, Value::try_from(&item.value)?);
}
Ok(ctx)
}
fn prepare_output_node_dto(value: Value) -> OutputNodeDto {
match ValueDto::try_from(&value) {
Ok(value_dto) => OutputNodeDto { value: Some(value_dto) },
_ => OutputNodeDto { value: None },
}
}