use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use compact_str::CompactString;
use rustc_hash::FxHashMap;
use serde::ser::SerializeMap;
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use super::record::{Symbol, intern_symbol, symbol_name};
use super::{
Name, Record, RuntimeError, RuntimeJson, execute_contains_direct, from_json,
is_truthy as value_truthy, materialize_projected_async, read_field_ref_direct,
read_index_ref_direct, stringify_value_async, value_len, value_type_name, write_number,
};
pub const LASH_TYPE_KEY: &str = "$lash_type";
#[derive(Clone, Debug, PartialEq)]
pub struct ListValue {
values: Arc<Vec<Value>>,
}
impl ListValue {
pub fn into_vec(self) -> Vec<Value> {
match Arc::try_unwrap(self.values) {
Ok(values) => values,
Err(values) => values.as_ref().clone(),
}
}
pub(crate) fn make_mut(&mut self) -> &mut Vec<Value> {
Arc::make_mut(&mut self.values)
}
}
impl std::ops::Deref for ListValue {
type Target = [Value];
fn deref(&self) -> &Self::Target {
self.values.as_slice()
}
}
impl From<Vec<Value>> for ListValue {
fn from(values: Vec<Value>) -> Self {
Self {
values: Arc::new(values),
}
}
}
impl FromIterator<Value> for ListValue {
fn from_iter<T: IntoIterator<Item = Value>>(iter: T) -> Self {
iter.into_iter().collect::<Vec<_>>().into()
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ImageValue {
pub id: String,
pub label: String,
pub size: u64,
pub width: Option<u32>,
pub height: Option<u32>,
}
impl ImageValue {
pub fn new(
id: impl Into<String>,
label: impl Into<String>,
size: u64,
width: Option<u32>,
height: Option<u32>,
) -> Self {
Self {
id: id.into(),
label: label.into(),
size,
width,
height,
}
}
}
impl Serialize for ImageValue {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut map = serializer.serialize_map(Some(6))?;
map.serialize_entry("type", "image")?;
map.serialize_entry("id", &self.id)?;
map.serialize_entry("label", &self.label)?;
map.serialize_entry("size", &self.size)?;
map.serialize_entry("width", &self.width)?;
map.serialize_entry("height", &self.height)?;
map.end()
}
}
impl<'de> Deserialize<'de> for ImageValue {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
#[derive(Deserialize)]
struct ImageDescriptor {
#[serde(rename = "type")]
kind: String,
id: String,
label: String,
size: u64,
#[serde(default)]
width: Option<u32>,
#[serde(default)]
height: Option<u32>,
}
let descriptor = ImageDescriptor::deserialize(deserializer)?;
if descriptor.kind != "image" {
return Err(serde::de::Error::custom("expected image descriptor"));
}
Ok(Self {
id: descriptor.id,
label: descriptor.label,
size: descriptor.size,
width: descriptor.width,
height: descriptor.height,
})
}
}
#[derive(Clone, Debug)]
pub enum Value {
Null,
Bool(bool),
Number(f64),
String(CompactString),
Image(ImageValue),
List(ListValue),
Record(Arc<Record>),
Projected(ProjectedValue),
}
impl Value {
pub fn as_record(&self) -> Option<&Record> {
match self {
Self::Record(record) => Some(record.as_ref()),
_ => None,
}
}
}
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Self::Null, Self::Null) => true,
(Self::Bool(left), Self::Bool(right)) => left == right,
(Self::Number(left), Self::Number(right)) => left == right,
(Self::String(left), Self::String(right)) => left == right,
(Self::Image(left), Self::Image(right)) => left == right,
(Self::List(left), Self::List(right)) => left == right,
(Self::Record(left), Self::Record(right)) => left == right,
(Self::Projected(left), Self::Projected(right)) => left == right,
(Self::Projected(left), right) => left.materialize() == *right,
(left, Self::Projected(right)) => *left == right.materialize(),
_ => false,
}
}
}
impl Serialize for Value {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
RuntimeJson(self).serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Value {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
serde_json::Value::deserialize(deserializer).map(from_json)
}
}
#[derive(Clone, Default)]
pub struct ProjectedBindings {
bindings: FxHashMap<Symbol, ProjectedValue>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ProjectedBindingError {
name: String,
}
impl ProjectedBindingError {
pub fn duplicate(name: impl Into<String>) -> Self {
Self { name: name.into() }
}
pub fn name(&self) -> &str {
&self.name
}
}
impl std::fmt::Display for ProjectedBindingError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "projected binding `{}` is already bound", self.name)
}
}
impl std::error::Error for ProjectedBindingError {}
impl ProjectedBindings {
pub fn new() -> Self {
Self::default()
}
pub fn insert(&mut self, name: impl Into<String>, value: ProjectedValue) {
let name = name.into();
self.try_insert(name, value)
.expect("projected binding should not be inserted twice");
}
pub fn try_insert(
&mut self,
name: impl Into<String>,
value: ProjectedValue,
) -> Result<(), ProjectedBindingError> {
let name = name.into();
let symbol = intern_symbol(&name);
if self.bindings.contains_key(&symbol) {
return Err(ProjectedBindingError::duplicate(name));
}
self.bindings.insert(intern_symbol(&name), value);
Ok(())
}
pub(crate) fn get_symbol(&self, symbol: Symbol) -> Option<ProjectedValue> {
self.bindings.get(&symbol).cloned()
}
pub fn get(&self, name: &str) -> Option<ProjectedValue> {
self.bindings.get(&intern_symbol(name)).cloned()
}
pub fn names(&self) -> impl Iterator<Item = String> + '_ {
self.bindings
.keys()
.map(|symbol| symbol_name(*symbol).to_string())
}
}
#[derive(Clone)]
pub struct ProjectedValue {
name: Arc<str>,
kind: ProjectedKind,
}
#[derive(Clone)]
enum ProjectedKind {
Scalar(Arc<Value>),
Custom(Arc<dyn ProjectedHostValue>),
}
pub type ProjectedFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;
#[derive(Clone, Debug, PartialEq)]
pub enum ProjectedReadRequest {
Len,
Empty,
Truthy,
Field(Arc<str>),
Index(Value),
Contains(Value),
Find {
needle: Value,
start: usize,
},
GrepText(Value),
Keys,
Values,
StartsWith(Value),
EndsWith(Value),
Split(Value),
Join(Value),
Trim,
Slice {
start: Option<isize>,
end: Option<isize>,
},
Push(Value),
ToNumber,
JsonParse,
SliceBound,
RangeBound,
Render,
Materialize,
}
#[derive(Clone, Debug, PartialEq)]
pub enum ProjectedReadResponse {
Missing,
Value(Value),
Text(String),
Bool(bool),
Len(usize),
Keys(Vec<String>),
}
pub trait ProjectedHostValue: Send + Sync {
fn type_name(&self) -> &str;
fn read_one(
&self,
_request: ProjectedReadRequest,
) -> ProjectedFuture<'_, ProjectedReadResponse> {
Box::pin(async { ProjectedReadResponse::Missing })
}
fn read_many(
&self,
requests: Vec<ProjectedReadRequest>,
) -> ProjectedFuture<'_, Vec<ProjectedReadResponse>> {
Box::pin(async move {
let mut responses = Vec::with_capacity(requests.len());
for request in requests {
responses.push(self.read_one(request).await);
}
responses
})
}
}
impl ProjectedValue {
pub fn scalar(name: impl Into<Arc<str>>, value: Value) -> Self {
Self {
name: name.into(),
kind: ProjectedKind::Scalar(Arc::new(value)),
}
}
pub fn custom(name: impl Into<Arc<str>>, value: Arc<dyn ProjectedHostValue>) -> Self {
Self {
name: name.into(),
kind: ProjectedKind::Custom(value),
}
}
pub(crate) fn unavailable_after_restore(
name: impl Into<Arc<str>>,
type_name: impl Into<Arc<str>>,
) -> Self {
let name = name.into();
Self {
name: name.clone(),
kind: ProjectedKind::Custom(Arc::new(UnavailableProjectedValue::new(name, type_name))),
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn propagate_field(parent_name: &str, field: &str, inner: Value) -> Value {
match inner {
Value::Projected(_) => inner,
other => Value::Projected(ProjectedValue::scalar(
Arc::<str>::from(format!("{parent_name}.{field}")),
other,
)),
}
}
pub fn propagate_index(parent_name: &str, index: &Value, inner: Value) -> Value {
match inner {
Value::Projected(_) => inner,
other => {
let suffix = match index {
Value::String(s) => format!("[{s:?}]"),
Value::Number(n) => format!("[{n}]"),
other => format!("[{other}]"),
};
Value::Projected(ProjectedValue::scalar(
Arc::<str>::from(format!("{parent_name}{suffix}")),
other,
))
}
}
}
pub(crate) async fn len(&self) -> usize {
match &self.kind {
ProjectedKind::Scalar(value) => value_len(value).unwrap_or(0),
ProjectedKind::Custom(value) => match value.read_one(ProjectedReadRequest::Len).await {
ProjectedReadResponse::Len(value) => value,
ProjectedReadResponse::Value(value) => value_len(&value).unwrap_or(0),
ProjectedReadResponse::Missing
| ProjectedReadResponse::Text(_)
| ProjectedReadResponse::Bool(_)
| ProjectedReadResponse::Keys(_) => 0,
},
}
}
pub(crate) async fn empty(&self) -> Option<bool> {
match &self.kind {
ProjectedKind::Scalar(value) => value_len(value).map(|len| len == 0),
ProjectedKind::Custom(value) => match value.read_one(ProjectedReadRequest::Empty).await
{
ProjectedReadResponse::Bool(value) => Some(value),
ProjectedReadResponse::Value(Value::Bool(value)) => Some(value),
ProjectedReadResponse::Value(value) => Some(value_truthy(&value)),
ProjectedReadResponse::Missing
| ProjectedReadResponse::Text(_)
| ProjectedReadResponse::Len(_)
| ProjectedReadResponse::Keys(_) => None,
},
}
}
pub(crate) async fn truthy(&self) -> bool {
match &self.kind {
ProjectedKind::Scalar(value) => value_truthy(value),
ProjectedKind::Custom(value) => {
match value.read_one(ProjectedReadRequest::Truthy).await {
ProjectedReadResponse::Bool(value) => value,
ProjectedReadResponse::Value(value) => value_truthy(&value),
_ => false,
}
}
}
}
pub(crate) async fn get_index(&self, index: &Value) -> Result<Value, RuntimeError> {
let index = materialize_projected_async(index.clone()).await;
match &self.kind {
ProjectedKind::Scalar(value) => read_index_ref_direct(value, &index),
ProjectedKind::Custom(value) => {
match value.read_one(ProjectedReadRequest::Index(index)).await {
ProjectedReadResponse::Value(value) => Ok(value),
_ => Ok(Value::Null),
}
}
}
}
pub(crate) async fn get_field(&self, field: &Name) -> Result<Value, RuntimeError> {
match &self.kind {
ProjectedKind::Scalar(value) => read_field_ref_direct(value, field),
ProjectedKind::Custom(value) => match value
.read_one(ProjectedReadRequest::Field(field.text.clone()))
.await
{
ProjectedReadResponse::Value(value) => Ok(value),
_ => Ok(Value::Null),
},
}
}
pub(crate) async fn contains(&self, needle: &Value) -> Result<bool, RuntimeError> {
match &self.kind {
ProjectedKind::Scalar(value) => execute_contains_direct(value, needle),
ProjectedKind::Custom(value) => Ok(
match value
.read_one(ProjectedReadRequest::Contains(needle.clone()))
.await
{
ProjectedReadResponse::Bool(value) => value,
ProjectedReadResponse::Value(value) => value_truthy(&value),
_ => false,
},
),
}
}
pub(crate) async fn find(&self, needle: Value, start: usize) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::Find { needle, start })
.await
}
pub(crate) async fn grep_text(&self, needle: Value) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::GrepText(needle))
.await
}
pub(crate) async fn keys(&self) -> Vec<String> {
match &self.kind {
ProjectedKind::Scalar(value) => match value.as_ref() {
Value::Record(record) => record.keys().map(ToString::to_string).collect(),
_ => Vec::new(),
},
ProjectedKind::Custom(value) => {
match value.read_one(ProjectedReadRequest::Keys).await {
ProjectedReadResponse::Keys(value) => value,
ProjectedReadResponse::Value(Value::List(values)) => values
.iter()
.filter_map(|value| match value {
Value::String(value) => Some(value.to_string()),
_ => None,
})
.collect(),
_ => Vec::new(),
}
}
}
}
pub(crate) async fn values(&self) -> Option<Value> {
match &self.kind {
ProjectedKind::Scalar(value) => match value.as_ref() {
Value::Record(record) => Some(Value::List(
record.values().cloned().collect::<Vec<_>>().into(),
)),
Value::Null => Some(Value::List(Vec::new().into())),
_ => None,
},
ProjectedKind::Custom(_) => {
self.custom_read_or_missing(ProjectedReadRequest::Values)
.await
}
}
}
pub(crate) async fn starts_with(&self, prefix: Value) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::StartsWith(prefix))
.await
}
pub(crate) async fn ends_with(&self, suffix: Value) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::EndsWith(suffix))
.await
}
pub(crate) async fn split(&self, needle: Value) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::Split(needle))
.await
}
pub(crate) async fn join(&self, sep: Value) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::Join(sep))
.await
}
pub(crate) async fn trim(&self) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::Trim)
.await
}
pub(crate) async fn slice(&self, start: Option<isize>, end: Option<isize>) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::Slice { start, end })
.await
}
pub(crate) async fn push(&self, item: Value) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::Push(item))
.await
}
pub(crate) async fn to_number(&self) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::ToNumber)
.await
}
pub(crate) async fn json_parse(&self) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::JsonParse)
.await
}
pub(crate) async fn slice_bound(&self) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::SliceBound)
.await
}
pub(crate) async fn range_bound(&self) -> Option<Value> {
self.custom_read_or_missing(ProjectedReadRequest::RangeBound)
.await
}
async fn custom_read_or_missing(&self, request: ProjectedReadRequest) -> Option<Value> {
match &self.kind {
ProjectedKind::Scalar(_) => None,
ProjectedKind::Custom(value) => match value.read_one(request).await {
ProjectedReadResponse::Value(value) => Some(value),
ProjectedReadResponse::Bool(value) => Some(Value::Bool(value)),
ProjectedReadResponse::Len(value) => Some(Value::Number(value as f64)),
ProjectedReadResponse::Text(value) => Some(Value::String(value.into())),
ProjectedReadResponse::Keys(values) => Some(Value::List(
values
.into_iter()
.map(|value| Value::String(value.into()))
.collect::<Vec<_>>()
.into(),
)),
ProjectedReadResponse::Missing => None,
},
}
}
pub async fn render(&self) -> String {
match &self.kind {
ProjectedKind::Scalar(value) => stringify_value_async(value).await.unwrap_or_default(),
ProjectedKind::Custom(value) => {
match value.read_one(ProjectedReadRequest::Render).await {
ProjectedReadResponse::Text(value) => value,
ProjectedReadResponse::Value(value) => {
stringify_value_async(&value).await.unwrap_or_default()
}
_ => String::new(),
}
}
}
}
pub async fn materialize_async(&self) -> Value {
match &self.kind {
ProjectedKind::Scalar(value) => (**value).clone(),
ProjectedKind::Custom(value) => {
match value.read_one(ProjectedReadRequest::Materialize).await {
ProjectedReadResponse::Value(value) => value,
ProjectedReadResponse::Text(value) => Value::String(value.into()),
ProjectedReadResponse::Bool(value) => Value::Bool(value),
ProjectedReadResponse::Len(value) => Value::Number(value as f64),
ProjectedReadResponse::Keys(values) => Value::List(
values
.into_iter()
.map(|value| Value::String(value.into()))
.collect::<Vec<_>>()
.into(),
),
ProjectedReadResponse::Missing => Value::Null,
}
}
}
}
pub fn materialize(&self) -> Value {
futures_executor::block_on(self.materialize_async())
}
}
struct UnavailableProjectedValue {
name: Arc<str>,
type_name: Arc<str>,
}
impl UnavailableProjectedValue {
fn new(name: Arc<str>, type_name: impl Into<Arc<str>>) -> Self {
Self {
name,
type_name: type_name.into(),
}
}
fn message(&self) -> String {
format!(
"projected host value `{}` ({}) is unavailable after snapshot restore; rerun the producing tool to recreate it",
self.name, self.type_name
)
}
}
impl ProjectedHostValue for UnavailableProjectedValue {
fn type_name(&self) -> &str {
&self.type_name
}
fn read_one(
&self,
request: ProjectedReadRequest,
) -> ProjectedFuture<'_, ProjectedReadResponse> {
Box::pin(async move {
match request {
ProjectedReadRequest::Render => ProjectedReadResponse::Text(self.message()),
ProjectedReadRequest::Materialize => {
ProjectedReadResponse::Value(Value::String(self.message().into()))
}
_ => ProjectedReadResponse::Missing,
}
})
}
}
impl fmt::Debug for ProjectedValue {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ProjectedValue")
.field("name", &self.name)
.field("kind", &self.value_type_name())
.finish()
}
}
impl PartialEq for ProjectedValue {
fn eq(&self, other: &Self) -> bool {
self.materialize() == other.materialize()
}
}
impl ProjectedValue {
pub(crate) fn value_type_name(&self) -> &str {
match &self.kind {
ProjectedKind::Scalar(value) => value_type_name(value),
ProjectedKind::Custom(value) => value.type_name(),
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => write!(f, "null"),
Self::Bool(value) => write!(f, "{value}"),
Self::Number(value) => write_number(f, *value),
Self::String(value) => write!(f, "{value}"),
Self::Image(_) | Self::List(_) | Self::Record(_) | Self::Projected(_) => write!(
f,
"{}",
serde_json::to_string(&RuntimeJson(self)).unwrap_or_default()
),
}
}
}