use crate::{Error, Result};
use serde_json::Value;
use value_ext::JsonValueExt;
#[derive(Debug, strum::AsRefStr)]
pub enum DevaiCustom {
Skip {
reason: Option<String>,
},
BeforeAllResponse(BeforeAllResponse),
}
#[derive(Debug, Default)]
pub struct BeforeAllResponse {
pub inputs: Option<Vec<Value>>,
pub before_all: Option<Value>,
pub options: Option<Value>,
}
#[derive(Debug)]
pub enum FromValue {
DevaiCustom(DevaiCustom),
OriginalValue(Value),
}
impl DevaiCustom {
pub fn from_value(value: Value) -> Result<FromValue> {
let Some(kind) = value.x_get::<String>("/_devai_/kind").ok() else {
return Ok(FromValue::OriginalValue(value));
};
if kind == "Skip" {
let reason: Option<String> = value.x_get("/_devai_/data/reason").ok();
Ok(FromValue::DevaiCustom(Self::Skip { reason }))
} else if kind == "BeforeAllResponse" {
let custom_data: Option<Value> = value.x_get("/_devai_/data").ok();
let before_all_response = extract_inputs_and_before_all(custom_data)?;
Ok(FromValue::DevaiCustom(DevaiCustom::BeforeAllResponse(
before_all_response,
)))
} else {
Err(format!("_devai_ kind '{kind}' is not known.").into())
}
}
}
fn extract_inputs_and_before_all(custom_data: Option<Value>) -> Result<BeforeAllResponse> {
let Some(custom_data) = custom_data else {
return Ok(BeforeAllResponse::default());
};
const ERROR_CAUSE: &str = "devai::before_all_response(data), can only have `.input` and `.before_all`)";
let before_all_response = match custom_data {
Value::Object(mut obj) => {
let all_inputs = obj.remove("inputs");
let before_all = obj.remove("before_all");
let options = obj.remove("options");
let inputs = match all_inputs {
Some(Value::Array(new_inputs)) => Some(new_inputs),
Some(Value::Null) => None,
Some(_) => {
return Err(Error::BeforeAllFailWrongReturn {
cause: "devai::before_all_response data .inputs must be an Null or an Array".to_string(),
});
}
None => None,
};
let keys: Vec<String> = obj.keys().map(|k| k.to_string()).collect();
if !keys.is_empty() {
return Err(Error::BeforeAllFailWrongReturn {
cause: format!("{ERROR_CAUSE}. But also contained: {}", keys.join(", ")),
});
}
BeforeAllResponse {
inputs,
before_all,
options,
}
}
_ => BeforeAllResponse::default(),
};
Ok(before_all_response)
}
#[cfg(test)]
mod tests {
type Result<T> = core::result::Result<T, Box<dyn std::error::Error>>;
use super::*;
use crate::_test_support::assert_contains;
use serde_json::json;
#[test]
fn test_devai_custom_before_all_inputs() -> Result<()> {
let fx_custom = json!({
"_devai_": {
"kind": "BeforeAllResponse",
"data": {
"inputs": ["A", "B", 123],
"before_all": "Some before all data"
}
}
});
let custom = DevaiCustom::from_value(fx_custom)?;
let FromValue::DevaiCustom(custom) = custom else {
return Err("Should be a devai custom".into());
};
let debug_string = format!("{:?}", custom);
assert_contains(&debug_string, r#"inputs: Some([String("A"), String("B"), Number(123)]"#);
assert_contains(&debug_string, r#"before_all: Some(String("Some before all data"))"#);
Ok(())
}
}