use crate::{
Expected, Literal, RenderError, ValueError, WithValue,
error::RenderErrorContext,
parse::{FALSE, NULL, TRUE},
};
use bytes::{Bytes, BytesMut};
use derive_more::{Display, From};
use futures::{TryStreamExt, stream::BoxStream};
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use std::{collections::VecDeque, fmt::Debug, path::PathBuf};
#[derive(Clone, Debug, Default, From, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum Value {
#[default]
Null,
Boolean(bool),
Integer(i64),
Float(f64),
String(String),
#[from(skip)] Array(Vec<Self>),
Object(IndexMap<String, Self>),
Bytes(Bytes),
}
impl Value {
pub fn to_bool(&self) -> bool {
match self {
Self::Null => false,
Self::Boolean(b) => *b,
Self::Integer(i) => *i != 0,
Self::Float(f) => *f != 0.0,
Self::String(s) => !s.is_empty(),
Self::Bytes(bytes) => !bytes.is_empty(),
Self::Array(array) => !array.is_empty(),
Self::Object(object) => !object.is_empty(),
}
}
#[must_use = "Returned value must be used"]
pub fn decode_bytes(self) -> Self {
match self {
Self::Bytes(bytes) => match String::from_utf8(bytes.into()) {
Ok(s) => Self::String(s),
Err(error) => Self::Bytes(error.into_bytes().into()),
},
_ => self,
}
}
pub fn try_into_string(self) -> Result<String, WithValue<ValueError>> {
match self {
Self::Null => Ok(NULL.into()),
Self::Boolean(false) => Ok(FALSE.into()),
Self::Boolean(true) => Ok(TRUE.into()),
Self::Integer(i) => Ok(i.to_string()),
Self::Float(f) => Ok(f.to_string()),
Self::String(s) => Ok(s),
Self::Bytes(bytes) => String::from_utf8(bytes.into())
.map_err(|error| {
WithValue::new(
Self::Bytes(error.as_bytes().to_owned().into()),
error.utf8_error(),
)
}),
Self::Array(_) | Self::Object(_) => Ok(self.to_string()),
}
}
pub fn into_bytes(self) -> Bytes {
match self {
Self::Null => NULL.into(),
Self::Boolean(false) => FALSE.into(),
Self::Boolean(true) => TRUE.into(),
Self::Integer(i) => i.to_string().into(),
Self::Float(f) => f.to_string().into(),
Self::String(s) => s.into(),
Self::Bytes(bytes) => bytes,
Self::Array(_) | Self::Object(_) => self.to_string().into(),
}
}
pub fn from_json(json: serde_json::Value) -> Self {
serde_json::from_value(json).unwrap()
}
}
impl From<&Literal> for Value {
fn from(literal: &Literal) -> Self {
match literal {
Literal::Null => Value::Null,
Literal::Boolean(b) => Value::Boolean(*b),
Literal::Integer(i) => Value::Integer(*i),
Literal::Float(f) => Value::Float(*f),
Literal::String(s) => Value::String(s.clone()),
Literal::Bytes(bytes) => Value::Bytes(bytes.clone()),
}
}
}
impl From<&str> for Value {
fn from(value: &str) -> Self {
Self::String(value.into())
}
}
impl<const N: usize> From<&'static [u8; N]> for Value {
fn from(value: &'static [u8; N]) -> Self {
Self::Bytes(value.as_slice().into())
}
}
impl<T> From<Option<T>> for Value
where
Value: From<T>,
{
fn from(value: Option<T>) -> Self {
value.map(Value::from).unwrap_or(Value::Null)
}
}
impl<T> From<Vec<T>> for Value
where
Value: From<T>,
{
fn from(value: Vec<T>) -> Self {
Self::Array(value.into_iter().map(Self::from).collect())
}
}
impl<K, V> From<Vec<(K, V)>> for Value
where
String: From<K>,
Value: From<V>,
{
fn from(value: Vec<(K, V)>) -> Self {
Self::Object(
value
.into_iter()
.map(|(key, value)| (key.into(), value.into()))
.collect(),
)
}
}
impl From<serde_json::Value> for Value {
fn from(value: serde_json::Value) -> Self {
Self::from_json(value)
}
}
#[derive(derive_more::Debug)]
pub enum ValueStream {
Value(Value),
Stream {
source: StreamSource,
#[debug(skip)]
stream: BoxStream<'static, Result<Bytes, RenderError>>,
},
}
impl ValueStream {
pub async fn resolve(self) -> Result<Value, RenderError> {
match self {
Self::Value(value) => Ok(value),
Self::Stream { stream, .. } => stream
.try_collect::<BytesMut>()
.await
.map(|bytes| Value::Bytes(bytes.into())),
}
}
}
impl<T: Into<Value>> From<T> for ValueStream {
fn from(value: T) -> Self {
Self::Value(value.into())
}
}
#[derive(Clone, Debug, Display, PartialEq)]
pub enum StreamSource {
#[display("command `{}`", command.join(" "))]
Command {
command: Vec<String>,
},
#[display("file {}", path.display())]
File {
path: PathBuf,
},
Compound,
}
pub trait RenderValue: Sized {
fn from_value(value: Value) -> Self;
async fn try_resolve_stream(self) -> Result<Value, RenderError>;
}
impl RenderValue for Value {
fn from_value(value: Value) -> Self {
value
}
async fn try_resolve_stream(self) -> Result<Value, RenderError> {
Ok(self)
}
}
impl RenderValue for ValueStream {
fn from_value(value: Value) -> Self {
Self::Value(value)
}
async fn try_resolve_stream(self) -> Result<Value, RenderError> {
self.resolve().await
}
}
pub trait TryFromValue: Sized {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>>;
}
impl TryFromValue for Value {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
Ok(value)
}
}
impl TryFromValue for bool {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
Ok(value.to_bool())
}
}
impl TryFromValue for f64 {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
match value {
Value::Float(f) => Ok(f),
_ => Err(WithValue::new(
value,
ValueError::Type {
expected: Expected::Float,
},
)),
}
}
}
impl TryFromValue for i64 {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
match value {
Value::Integer(i) => Ok(i),
_ => Err(WithValue::new(
value,
ValueError::Type {
expected: Expected::Integer,
},
)),
}
}
}
impl TryFromValue for u32 {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
match &value {
Value::Integer(i) => (*i).try_into().map_err(|_| {
WithValue::new(
value,
ValueError::IntegerRange {
expected: format!("[{}, {}]", u32::MIN, u32::MAX),
},
)
}),
_ => Err(WithValue::new(
value,
ValueError::Type {
expected: Expected::Integer,
},
)),
}
}
}
impl TryFromValue for String {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
value.try_into_string()
}
}
impl TryFromValue for Bytes {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
Ok(value.into_bytes())
}
}
impl<T> TryFromValue for Option<T>
where
T: TryFromValue,
{
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
if let Value::Null = value {
Ok(None)
} else {
T::try_from_value(value).map(Some)
}
}
}
impl<T> TryFromValue for Vec<T>
where
T: TryFromValue,
{
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
if let Value::Array(array) = value {
array.into_iter().map(T::try_from_value).collect()
} else {
Err(WithValue::new(
value,
ValueError::Type {
expected: Expected::Array,
},
))
}
}
}
impl TryFromValue for serde_json::Value {
fn try_from_value(value: Value) -> Result<Self, WithValue<ValueError>> {
match value {
Value::Null => Ok(serde_json::Value::Null),
Value::Boolean(b) => Ok(b.into()),
Value::Integer(i) => Ok(i.into()),
Value::Float(f) => Ok(f.into()),
Value::String(s) => Ok(s.into()),
Value::Array(array) => array
.into_iter()
.map(serde_json::Value::try_from_value)
.collect(),
Value::Object(map) => map
.into_iter()
.map(|(k, v)| Ok((k, serde_json::Value::try_from_value(v)?)))
.collect(),
Value::Bytes(_) => {
value.try_into_string().map(serde_json::Value::String)
}
}
}
}
#[macro_export]
macro_rules! impl_try_from_value_str {
($type:ty) => {
impl TryFromValue for $type {
fn try_from_value(
value: $crate::Value,
) -> Result<Self, $crate::WithValue<$crate::ValueError>> {
let s = String::try_from_value(value)?;
s.parse().map_err(|error| {
$crate::WithValue::new(
s.into(),
$crate::ValueError::other(error),
)
})
}
}
};
}
#[derive(Debug)]
pub struct Arguments<'ctx, Ctx> {
context: &'ctx Ctx,
position: VecDeque<Value>,
num_popped: usize,
keyword: IndexMap<String, Value>,
}
impl<'ctx, Ctx> Arguments<'ctx, Ctx> {
pub fn new(
context: &'ctx Ctx,
position: VecDeque<Value>,
keyword: IndexMap<String, Value>,
) -> Self {
Self {
context,
position,
num_popped: 0,
keyword,
}
}
pub fn context(&self) -> &'ctx Ctx {
self.context
}
pub fn pop_position<T: TryFromValue>(&mut self) -> Result<T, RenderError> {
let value = self
.position
.pop_front()
.ok_or(RenderError::TooFewArguments)?;
let arg_index = self.num_popped;
self.num_popped += 1;
T::try_from_value(value).map_err(|error| {
RenderError::Value(error.error).context(
RenderErrorContext::ArgumentConvert {
argument: arg_index.to_string(),
value: error.value,
},
)
})
}
pub fn pop_keyword<T: Default + TryFromValue>(
&mut self,
name: &str,
) -> Result<T, RenderError> {
match self.keyword.shift_remove(name) {
Some(value) => T::try_from_value(value).map_err(|error| {
RenderError::Value(error.error).context(
RenderErrorContext::ArgumentConvert {
argument: name.to_owned(),
value: error.value,
},
)
}),
None => Ok(T::default()),
}
}
pub fn ensure_consumed(self) -> Result<(), RenderError> {
if self.position.is_empty() && self.keyword.is_empty() {
Ok(())
} else {
Err(RenderError::TooManyArguments {
position: self.position.into(),
keyword: self.keyword,
})
}
}
pub(crate) fn push_piped(&mut self, argument: Value) {
self.position.push_back(argument);
}
}
pub trait FunctionOutput {
fn into_result(self) -> Result<ValueStream, RenderError>;
}
impl<T: Into<ValueStream>> FunctionOutput for T {
fn into_result(self) -> Result<ValueStream, RenderError> {
Ok(self.into())
}
}
impl<T, E> FunctionOutput for Result<T, E>
where
T: Into<ValueStream>,
E: Into<RenderError>,
{
fn into_result(self) -> Result<ValueStream, RenderError> {
self.map(T::into).map_err(E::into)
}
}
#[cfg(test)]
mod tests {
use super::*;
use futures::{StreamExt, future, stream};
use rstest::rstest;
use slumber_util::assert_result;
#[rstest]
#[case::value(ValueStream::Value("test".into()), Ok("test".into()))]
#[case::stream(
stream(Ok("test".into())),
Ok(b"test".into()),
)]
#[case::stream_error(
stream(Err(RenderError::FunctionUnknown)),
Err("Unknown function")
)]
#[tokio::test]
async fn test_stream_resolve(
#[case] stream: ValueStream,
#[case] expected: Result<Value, &str>,
) {
assert_result(stream.resolve().await, expected);
}
fn stream(result: Result<Bytes, RenderError>) -> ValueStream {
ValueStream::Stream {
stream: stream::once(future::ready(result)).boxed(),
source: StreamSource::File {
path: "bogus".into(),
},
}
}
}