use super::config::ValueKind;
use super::path::Path;
use super::rules::Shape;
use super::walk::reads_secret;
use crate::base::{Error, ErrorKind};
use datalogic_rs::Logic;
use datavalue::{DataValue, NumberValue, OwnedDataValue};
use serde_json::Value;
use std::borrow::Cow;
use std::fmt;
use std::str::FromStr;
pub(crate) mod sealed {
use datalogic_rs::{DataValue, Logic};
use datavalue::OwnedDataValue;
pub trait Sealed {}
#[derive(Clone, Copy, Debug)]
pub struct Crate(());
pub(crate) const CRATE: Crate = Crate(());
pub trait SealedExpr {
fn constant_value(&self, _: Crate) -> Option<OwnedDataValue>;
fn logic(&self, _: Crate) -> &Logic;
fn value_of(&self, view: &DataValue<'_>, _: Crate) -> crate::Result<OwnedDataValue>;
}
}
pub trait FromValue: Sized + Clone + Send + Sync + 'static + sealed::Sealed {
#[doc(hidden)]
const KIND: ValueKind;
type Out<'a>;
#[doc(hidden)]
fn from_view(v: &DataValue<'_>) -> Result<Self, String>;
#[doc(hidden)]
fn lend(c: &Self) -> Self::Out<'_>;
#[doc(hidden)]
fn give<'a>(v: Self) -> Self::Out<'a>;
#[doc(hidden)]
fn to_value(c: &Self) -> OwnedDataValue {
Self::into_value(c.clone())
}
#[doc(hidden)]
fn into_value(v: Self) -> OwnedDataValue;
#[doc(hidden)]
fn type_name() -> &'static str;
}
impl sealed::Sealed for OwnedDataValue {}
impl sealed::Sealed for String {}
impl sealed::Sealed for u64 {}
impl sealed::Sealed for i64 {}
impl sealed::Sealed for f64 {}
impl sealed::Sealed for bool {}
impl sealed::Sealed for Path {}
impl FromValue for OwnedDataValue {
const KIND: ValueKind = ValueKind::Any;
type Out<'a> = Cow<'a, Self>;
fn from_view(v: &DataValue<'_>) -> Result<Self, String> {
Ok(v.to_owned())
}
fn lend(c: &Self) -> Self::Out<'_> {
Cow::Borrowed(c)
}
fn give<'a>(v: Self) -> Self::Out<'a> {
Cow::Owned(v)
}
fn into_value(v: Self) -> OwnedDataValue {
v
}
fn type_name() -> &'static str {
"a value"
}
}
impl FromValue for String {
const KIND: ValueKind = ValueKind::String;
type Out<'a> = Cow<'a, str>;
fn from_view(v: &DataValue<'_>) -> Result<Self, String> {
Ok(match v {
DataValue::String(s) => (*s).to_owned(),
other => other.to_string(),
})
}
fn lend(c: &Self) -> Self::Out<'_> {
Cow::Borrowed(c)
}
fn give<'a>(v: Self) -> Self::Out<'a> {
Cow::Owned(v)
}
fn into_value(v: Self) -> OwnedDataValue {
OwnedDataValue::String(v)
}
fn type_name() -> &'static str {
"a string"
}
}
impl FromValue for u64 {
const KIND: ValueKind = ValueKind::Number;
type Out<'a> = Self;
fn from_view(v: &DataValue<'_>) -> Result<Self, String> {
match v {
DataValue::Number(n) => n
.as_u64()
.ok_or_else(|| "a negative or fractional number is not a u64".to_owned()),
other => Err(format!("{} is not a u64", Shape::of_view(other).describe())),
}
}
fn lend(c: &Self) -> Self::Out<'_> {
*c
}
fn give<'a>(v: Self) -> Self::Out<'a> {
v
}
fn into_value(v: Self) -> OwnedDataValue {
v.into()
}
fn type_name() -> &'static str {
"a u64"
}
}
impl FromValue for i64 {
const KIND: ValueKind = ValueKind::Number;
type Out<'a> = Self;
fn from_view(v: &DataValue<'_>) -> Result<Self, String> {
match v {
DataValue::Number(n) => n
.as_i64()
.ok_or_else(|| "an out-of-range or fractional number is not an i64".to_owned()),
other => Err(format!(
"{} is not an i64",
Shape::of_view(other).describe()
)),
}
}
fn lend(c: &Self) -> Self::Out<'_> {
*c
}
fn give<'a>(v: Self) -> Self::Out<'a> {
v
}
fn into_value(v: Self) -> OwnedDataValue {
v.into()
}
fn type_name() -> &'static str {
"an i64"
}
}
impl FromValue for f64 {
const KIND: ValueKind = ValueKind::Number;
type Out<'a> = Self;
fn from_view(v: &DataValue<'_>) -> Result<Self, String> {
match v {
DataValue::Number(n) if n.as_f64().is_finite() => Ok(n.as_f64()),
DataValue::Number(_) => Err("a non-finite number is not a finite f64".to_owned()),
other => Err(format!(
"{} is not an f64",
Shape::of_view(other).describe()
)),
}
}
fn lend(c: &Self) -> Self::Out<'_> {
*c
}
fn give<'a>(v: Self) -> Self::Out<'a> {
v
}
fn into_value(v: Self) -> OwnedDataValue {
OwnedDataValue::Number(NumberValue::Float(v))
}
fn type_name() -> &'static str {
"an f64"
}
}
impl FromValue for bool {
const KIND: ValueKind = ValueKind::Bool;
type Out<'a> = Self;
fn from_view(v: &DataValue<'_>) -> Result<Self, String> {
match v {
DataValue::Bool(b) => Ok(*b),
other => Err(format!(
"{} is not a bool",
Shape::of_view(other).describe()
)),
}
}
fn lend(c: &Self) -> Self::Out<'_> {
*c
}
fn give<'a>(v: Self) -> Self::Out<'a> {
v
}
fn into_value(v: Self) -> OwnedDataValue {
v.into()
}
fn type_name() -> &'static str {
"a bool"
}
}
impl FromValue for Path {
const KIND: ValueKind = ValueKind::Path;
type Out<'a> = Cow<'a, Self>;
fn from_view(v: &DataValue<'_>) -> Result<Self, String> {
match v {
DataValue::String(s) => Self::from_str(s).map_err(|e| e.message().to_owned()),
other => Err(format!(
"{} is not a path",
Shape::of_view(other).describe()
)),
}
}
fn lend(c: &Self) -> Self::Out<'_> {
Cow::Borrowed(c)
}
fn give<'a>(v: Self) -> Self::Out<'a> {
Cow::Owned(v)
}
fn into_value(v: Self) -> OwnedDataValue {
OwnedDataValue::String(v.to_string())
}
fn type_name() -> &'static str {
"a path"
}
}
#[derive(Clone, Debug, PartialEq)]
#[non_exhaustive]
pub struct Evaluated<T> {
pub value: T,
pub ops: Option<u64>,
}
pub struct Expr<T: FromValue = OwnedDataValue> {
pub(super) raw: Value,
pub(super) logic: Logic,
pub(super) constant: Option<T>,
}
pub(crate) fn snippet(raw: &Value) -> String {
const MAX: usize = 120;
let text = raw.to_string();
if text.len() <= MAX {
return text;
}
let end = text.floor_char_boundary(MAX);
format!("{}…", &text[..end])
}
impl Expr<OwnedDataValue> {
pub(crate) fn is_trivially_true(&self) -> bool {
self.constant() == Some(&OwnedDataValue::Bool(true))
}
}
impl<T: FromValue> Expr<T> {
pub fn constant(&self) -> Option<&T> {
self.constant.as_ref()
}
pub fn raw(&self) -> &Value {
&self.raw
}
pub(crate) fn logic(&self) -> &Logic {
&self.logic
}
pub(crate) fn convert(&self, view: &DataValue<'_>) -> crate::Result<T> {
T::from_view(view).map_err(|why| {
let shown = snippet(&self.raw);
let message = if reads_secret(&self.raw) {
format!("`{shown}` did not give {}", T::type_name())
} else {
format!("`{shown}` did not give {}: {why}", T::type_name())
};
Error::new(ErrorKind::Eval, message)
})
}
}
impl<T: FromValue> fmt::Debug for Expr<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Expr")
.field("raw", &self.raw)
.field("folded", &self.constant.is_some())
.finish()
}
}
pub trait ResolveExpr: Send + Sync + sealed::SealedExpr {
fn raw(&self) -> &Value;
}
impl<T: FromValue> ResolveExpr for Expr<T> {
fn raw(&self) -> &Value {
&self.raw
}
}
impl<T: FromValue> sealed::SealedExpr for Expr<T> {
fn constant_value(&self, _: sealed::Crate) -> Option<OwnedDataValue> {
self.constant.as_ref().map(T::to_value)
}
fn logic(&self, _: sealed::Crate) -> &Logic {
&self.logic
}
fn value_of(&self, view: &DataValue<'_>, _: sealed::Crate) -> crate::Result<OwnedDataValue> {
self.convert(view).map(T::into_value)
}
}