use super::{Expr, ExprKind, Literal, Name};
use crate::entities::JsonSerializationError;
use crate::parser;
use serde::{Deserialize, Serialize};
use smol_str::SmolStr;
use std::hash::{Hash, Hasher};
use std::ops::Deref;
use thiserror::Error;
#[derive(Deserialize, Serialize, Hash, Debug, Clone, PartialEq, Eq)]
#[serde(transparent)]
pub struct RestrictedExpr(Expr);
impl RestrictedExpr {
pub fn new(expr: Expr) -> Result<Self, RestrictedExpressionError> {
is_restricted(&expr)?;
Ok(Self(expr))
}
pub fn new_unchecked(expr: Expr) -> Self {
if cfg!(debug_assertions) {
#[allow(clippy::unwrap_used)]
Self::new(expr).unwrap()
} else {
Self(expr)
}
}
pub fn val(v: impl Into<Literal>) -> Self {
Self::new_unchecked(Expr::val(v))
}
pub fn set(exprs: impl IntoIterator<Item = RestrictedExpr>) -> Self {
Self::new_unchecked(Expr::set(exprs.into_iter().map(Into::into)))
}
pub fn record(pairs: impl IntoIterator<Item = (SmolStr, RestrictedExpr)>) -> Self {
Self::new_unchecked(Expr::record(pairs.into_iter().map(|(k, v)| (k, v.into()))))
}
pub fn call_extension_fn(function_name: Name, args: Vec<RestrictedExpr>) -> Self {
Self::new_unchecked(Expr::call_extension_fn(
function_name,
args.into_iter().map(Into::into).collect(),
))
}
}
impl std::str::FromStr for RestrictedExpr {
type Err = parser::err::ParseErrors;
fn from_str(s: &str) -> Result<RestrictedExpr, Self::Err> {
parser::parse_restrictedexpr(s)
}
}
#[derive(Serialize, Hash, Debug, Clone, PartialEq, Eq)]
pub struct BorrowedRestrictedExpr<'a>(&'a Expr);
impl<'a> BorrowedRestrictedExpr<'a> {
pub fn new(expr: &'a Expr) -> Result<Self, RestrictedExpressionError> {
is_restricted(expr)?;
Ok(Self(expr))
}
pub fn new_unchecked(expr: &'a Expr) -> Self {
if cfg!(debug_assertions) {
#[allow(clippy::unwrap_used)]
Self::new(expr).unwrap()
} else {
Self(expr)
}
}
pub fn to_natural_json(self) -> Result<serde_json::Value, JsonSerializationError> {
Ok(serde_json::to_value(
crate::entities::JSONValue::from_expr(self)?,
)?)
}
}
fn is_restricted(expr: &Expr) -> Result<(), RestrictedExpressionError> {
match expr.expr_kind() {
ExprKind::Lit(_) => Ok(()),
ExprKind::Unknown { .. } => Ok(()),
ExprKind::Var(_) => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: expr.to_string(),
}),
ExprKind::Slot(_) => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "template slots".into(),
}),
ExprKind::If { .. } => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "if-then-else".into(),
}),
ExprKind::And { .. } => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "&&".into(),
}),
ExprKind::Or { .. } => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "||".into(),
}),
ExprKind::UnaryApp { op, .. } => {
Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: op.to_string(),
})
}
ExprKind::BinaryApp { op, .. } => {
Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: op.to_string(),
})
}
ExprKind::MulByConst { .. } => {
Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "multiplication".into(),
})
}
ExprKind::GetAttr { .. } => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "get-attribute".into(),
}),
ExprKind::HasAttr { .. } => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "'has'".into(),
}),
ExprKind::Like { .. } => Err(RestrictedExpressionError::InvalidRestrictedExpression {
feature: "'like'".into(),
}),
ExprKind::ExtensionFunctionApp { args, .. } => args.iter().try_for_each(is_restricted),
ExprKind::Set(exprs) => exprs.iter().try_for_each(is_restricted),
ExprKind::Record { pairs } => pairs.iter().map(|(_, v)| v).try_for_each(is_restricted),
}
}
impl From<RestrictedExpr> for Expr {
fn from(r: RestrictedExpr) -> Expr {
r.0
}
}
impl AsRef<Expr> for RestrictedExpr {
fn as_ref(&self) -> &Expr {
&self.0
}
}
impl Deref for RestrictedExpr {
type Target = Expr;
fn deref(&self) -> &Expr {
self.as_ref()
}
}
impl std::fmt::Display for RestrictedExpr {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
impl<'a> From<BorrowedRestrictedExpr<'a>> for &'a Expr {
fn from(r: BorrowedRestrictedExpr<'a>) -> &'a Expr {
r.0
}
}
impl<'a> AsRef<Expr> for BorrowedRestrictedExpr<'a> {
fn as_ref(&self) -> &Expr {
self.0
}
}
impl RestrictedExpr {
pub fn as_borrowed(&self) -> BorrowedRestrictedExpr<'_> {
BorrowedRestrictedExpr::new_unchecked(self.as_ref())
}
}
impl<'a> Deref for BorrowedRestrictedExpr<'a> {
type Target = Expr;
fn deref(&self) -> &Expr {
self.as_ref()
}
}
impl<'a> std::fmt::Display for BorrowedRestrictedExpr<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", &self.0)
}
}
#[derive(Eq, Debug, Clone)]
pub struct RestrictedExprShapeOnly<'a>(BorrowedRestrictedExpr<'a>);
impl<'a> RestrictedExprShapeOnly<'a> {
pub fn new(e: BorrowedRestrictedExpr<'a>) -> RestrictedExprShapeOnly<'a> {
RestrictedExprShapeOnly(e)
}
}
impl<'a> PartialEq for RestrictedExprShapeOnly<'a> {
fn eq(&self, other: &Self) -> bool {
self.0.eq_shape(&other.0)
}
}
impl<'a> Hash for RestrictedExprShapeOnly<'a> {
fn hash<H: Hasher>(&self, state: &mut H) {
self.0.hash_shape(state);
}
}
#[derive(Debug, Clone, PartialEq, Hash, Error)]
pub enum RestrictedExpressionError {
#[error("not allowed to use {feature} in a restricted expression")]
InvalidRestrictedExpression {
feature: String,
},
}