use super::special::FromSpecial;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize, de::DeserializeOwned};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
#[schemars(rename = "functions.expression.OneOrMany.{T}")]
pub enum OneOrMany<T> {
#[schemars(title = "One")]
One(T),
#[schemars(title = "Many")]
Many(Vec<T>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
#[serde(deny_unknown_fields)]
#[schemars(rename = "functions.expression.Expression")]
pub enum Expression {
#[schemars(title = "JMESPath")]
#[serde(rename = "$jmespath")]
JMESPath(String),
#[schemars(title = "Starlark")]
#[serde(rename = "$starlark")]
Starlark(String),
#[schemars(title = "Special")]
#[serde(rename = "$special")]
Special(super::Special),
}
impl Expression {
pub fn compile_one_or_many<T>(
&self,
params: &super::Params,
) -> Result<OneOrMany<T>, super::ExpressionError>
where
T: DeserializeOwned
+ super::starlark::FromStarlarkValue
+ super::special::FromSpecial,
{
match self {
Expression::JMESPath(jmespath) => {
let expr = super::JMESPATH_RUNTIME.compile(jmespath)?;
let value = expr.search(params)?;
let json = serde_json::to_value(value)?;
Self::deserialize_result(json)
}
Expression::Starlark(starlark) => {
OneOrMany::<T>::from_starlark(starlark, params)
}
Expression::Special(special) => {
OneOrMany::<T>::from_special(special, params)
}
}
}
fn deserialize_result<T>(
value: serde_json::Value,
) -> Result<OneOrMany<T>, super::ExpressionError>
where
T: DeserializeOwned,
{
let value: Option<OneOrMany<Option<T>>> = serde_json::from_value(value)
.map_err(super::ExpressionError::DeserializationError)?;
Ok(match value {
Some(OneOrMany::One(Some(v))) => OneOrMany::One(v),
Some(OneOrMany::One(None)) => OneOrMany::Many(Vec::new()),
Some(OneOrMany::Many(mut vs)) => {
vs.retain(|v| v.is_some());
if vs.is_empty() {
OneOrMany::Many(Vec::new())
} else if vs.len() == 1 {
OneOrMany::One(vs.into_iter().flatten().next().unwrap())
} else {
OneOrMany::Many(vs.into_iter().flatten().collect())
}
}
None => OneOrMany::Many(Vec::new()),
})
}
pub fn compile_one<T>(
&self,
params: &super::Params,
) -> Result<T, super::ExpressionError>
where
T: DeserializeOwned
+ super::starlark::FromStarlarkValue
+ super::special::FromSpecial,
{
let result = self.compile_one_or_many(params)?;
match result {
OneOrMany::One(value) => Ok(value),
OneOrMany::Many(_) => {
Err(super::ExpressionError::ExpectedOneValueFoundMany)
}
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema, arbitrary::Arbitrary)]
#[serde(untagged)]
#[schemars(rename = "functions.expression.WithExpression.{T}")]
pub enum WithExpression<T> {
#[schemars(title = "Expression")]
Expression(Expression),
#[schemars(title = "Value")]
Value(T),
}
impl<T> std::default::Default for WithExpression<T>
where
T: Default,
{
fn default() -> Self {
WithExpression::Value(T::default())
}
}
impl<T> WithExpression<T>
where
T: DeserializeOwned
+ super::starlark::FromStarlarkValue
+ super::special::FromSpecial,
{
pub fn compile_one_or_many(
self,
params: &super::Params,
) -> Result<OneOrMany<T>, super::ExpressionError> {
match self {
WithExpression::Expression(expr) => {
expr.compile_one_or_many(params)
}
WithExpression::Value(value) => Ok(OneOrMany::One(value)),
}
}
pub fn compile_one(
self,
params: &super::Params,
) -> Result<T, super::ExpressionError> {
match self {
WithExpression::Expression(expr) => expr.compile_one(params),
WithExpression::Value(value) => Ok(value),
}
}
}
impl<T: super::starlark::FromStarlarkValue> super::starlark::FromStarlarkValue
for WithExpression<T>
{
fn from_starlark_value(
value: &starlark::values::Value,
) -> Result<Self, super::ExpressionError> {
T::from_starlark_value(value).map(WithExpression::Value)
}
}
impl<T: super::special::FromSpecial> FromSpecial
for super::expression::WithExpression<T>
{
fn from_special(
special: &super::special::Special,
params: &super::Params,
) -> Result<Self, super::ExpressionError> {
Ok(super::expression::WithExpression::Value(T::from_special(
special, params,
)?))
}
}
impl<T> WithExpression<Option<T>> {
pub fn is_none(&self) -> bool {
matches!(self, WithExpression::Value(None))
}
}