use std::{collections::HashMap, fmt::Debug, rc::Rc};
use crate::runtime::document::DocNode;
pub use crate::runtime::document::{DocumentBuilder, QualifiedName};
use crate::{
runtime::{Context, RuntimeError},
Location, Reference,
};
pub type Object = HashMap<String, Value>;
pub type Array = Vec<Value>;
pub trait Function {
fn apply(
&self,
location: Location,
context: &dyn Context,
arguments: &[Value],
) -> Result<Value, RuntimeError>;
}
struct DefaultFunction<F>(F);
impl<F> Function for DefaultFunction<F>
where
F: Fn(Location, &dyn Context, &[Value]) -> Result<Value, RuntimeError>,
{
fn apply(
&self,
location: Location,
context: &dyn Context,
arguments: &[Value],
) -> Result<Value, RuntimeError> {
self.0(location, context, arguments)
}
}
#[derive(Clone)]
pub(super) struct FunctionValue(Rc<dyn Function>);
impl PartialEq for FunctionValue {
fn eq(&self, _: &Self) -> bool {
false
}
}
impl Debug for FunctionValue {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "NativeFunction")
}
}
#[derive(Debug, Clone)]
pub struct RuntimeNumber {
value: f64,
representation: String,
}
impl RuntimeNumber {
pub fn representation(&self) -> &str {
&self.representation
}
}
impl PartialEq for RuntimeNumber {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
}
#[derive(Debug, Clone)]
pub(super) enum InternalValue {
Null,
Bool(bool),
Number(RuntimeNumber),
String(String),
Binary(Vec<u8>),
Array(Rc<Array>),
Object(Rc<Object>),
Function(FunctionValue),
Reference(Reference),
QualifiedName(QualifiedName),
Node(DocNode),
#[cfg(feature = "experimental_coerced_type")]
CoercedType(Rc<InternalValue>, Rc<String>),
}
impl PartialEq for InternalValue {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(InternalValue::Null, InternalValue::Null) => true,
(InternalValue::Bool(first), InternalValue::Bool(second)) => first.eq(second),
(InternalValue::Number(first), InternalValue::Number(second)) => first.eq(second),
(InternalValue::String(first), InternalValue::String(second)) => first.eq(second),
(InternalValue::Array(first), InternalValue::Array(second)) => first.eq(second),
(InternalValue::Object(first), InternalValue::Object(second)) => first.eq(second),
(InternalValue::Function(first), InternalValue::Function(second)) => first.eq(second),
(InternalValue::Reference(first), InternalValue::Reference(second)) => first.eq(second),
(InternalValue::Binary(first), InternalValue::Binary(second)) => first.eq(second),
#[cfg(feature = "experimental_coerced_type")]
(InternalValue::CoercedType(value, coerced_value), second) => {
value.as_ref().eq(second)
|| match second {
InternalValue::String(second) => coerced_value.as_ref().eq(second),
_ => false,
}
}
#[cfg(feature = "experimental_coerced_type")]
(first, InternalValue::CoercedType(value, coerced_value)) => {
first.eq(value)
|| match first {
InternalValue::String(first) => first.eq(coerced_value.as_ref()),
_ => false,
}
}
(InternalValue::Node(first), second) => first.compare(second),
(first, InternalValue::Node(second)) => second.compare(first),
(InternalValue::QualifiedName(first), InternalValue::QualifiedName(second)) => {
first.eq(second)
}
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Value {
pub(super) internal: InternalValue,
}
impl Value {
pub fn null() -> Self {
Self {
internal: InternalValue::Null,
}
}
pub fn bool(b: bool) -> Self {
Self {
internal: InternalValue::Bool(b),
}
}
pub fn number(value: f64) -> Self {
Self::number_with_representation(value, value.to_string())
}
pub(crate) fn number_with_representation(value: f64, representation: String) -> Self {
Self {
internal: InternalValue::Number(RuntimeNumber {
value,
representation,
}),
}
}
pub fn string<S: Into<String>>(s: S) -> Self {
Self {
internal: InternalValue::String(s.into()),
}
}
pub fn binary<S: Into<Vec<u8>>>(s: S) -> Self {
Self {
internal: InternalValue::Binary(s.into()),
}
}
#[cfg(feature = "experimental_coerced_type")]
pub fn coerced_array(v: Vec<Value>, coerced: Rc<String>) -> Self {
Self {
internal: InternalValue::CoercedType(
Rc::new(InternalValue::Array(Rc::new(v))),
coerced,
),
}
}
pub fn array(v: Vec<Value>) -> Self {
Self {
internal: InternalValue::Array(Rc::new(v)),
}
}
pub fn object(o: Object) -> Self {
Self {
internal: InternalValue::Object(Rc::new(o)),
}
}
#[cfg(feature = "experimental_coerced_type")]
pub fn coerced_object(object: Object, coerced: Rc<String>) -> Self {
Self {
internal: InternalValue::CoercedType(
Rc::new(InternalValue::Object(Rc::new(object))),
coerced,
),
}
}
pub(crate) fn qualified_name(qualified_name: QualifiedName) -> Self {
Self {
internal: InternalValue::QualifiedName(qualified_name),
}
}
#[cfg(feature = "experimental_coerced_type")]
pub(crate) fn coerced_node(node: DocNode, coerced: Rc<String>) -> Self {
Self {
internal: InternalValue::CoercedType(Rc::new(InternalValue::Node(node)), coerced),
}
}
pub(crate) fn node(node: DocNode) -> Self {
Self {
internal: InternalValue::Node(node),
}
}
pub fn reference(reference: Reference) -> Self {
Self {
internal: InternalValue::Reference(reference),
}
}
pub fn function<F>(f: F) -> Self
where
F: 'static + Function,
{
Self {
internal: InternalValue::Function(FunctionValue(Rc::new(f))),
}
}
pub fn function_from_fn<F>(f: F) -> Self
where
F: 'static + Fn(Location, &dyn Context, &[Value]) -> Result<Value, RuntimeError>,
{
Self::function(DefaultFunction(f))
}
pub fn is_null(&self) -> bool {
match &self.internal {
InternalValue::Null => true,
InternalValue::Node(n) => n.is_null(),
_ => false,
}
}
pub fn as_bool(&self) -> Option<bool> {
match &self.internal {
InternalValue::Bool(b) => Some(*b),
_ => None,
}
}
pub fn as_f64(&self) -> Option<f64> {
match &self.internal {
InternalValue::Number(n) => Some(n.value),
_ => None,
}
}
pub(super) fn as_number(&self) -> Option<&RuntimeNumber> {
match &self.internal {
InternalValue::Number(n) => Some(n),
_ => None,
}
}
pub fn as_str(&self) -> Option<&str> {
match &self.internal {
InternalValue::String(s) => Some(s),
InternalValue::Node(n) => n.as_str(),
#[cfg(feature = "experimental_coerced_type")]
InternalValue::CoercedType(value, coerced) => match value.as_ref() {
InternalValue::String(s) => Some(s),
InternalValue::Node(n) => n.as_str().or_else(|| Some(coerced)),
_ => Some(coerced),
},
_ => None,
}
}
pub fn as_binary(&self) -> Option<&[u8]> {
match &self.internal {
InternalValue::Binary(reference) => Some(reference),
_ => None,
}
}
pub(crate) fn as_qname(&self) -> Option<&QualifiedName> {
match &self.internal {
InternalValue::QualifiedName(s) => Some(s),
_ => None,
}
}
pub fn as_slice(&self) -> Option<&[Value]> {
match &self.internal {
InternalValue::Array(array) => Some(array),
#[cfg(feature = "experimental_coerced_type")]
InternalValue::CoercedType(value, _) => match value.as_ref() {
InternalValue::Array(array) => Some(array),
_ => None,
},
_ => None,
}
}
pub fn as_object(&self) -> Option<&Object> {
match &self.internal {
InternalValue::Object(object) => Some(object),
#[cfg(feature = "experimental_coerced_type")]
InternalValue::CoercedType(value, _) => match value.as_ref() {
InternalValue::Object(object) => Some(object),
_ => None,
},
_ => None,
}
}
pub fn as_function(&self) -> Option<&dyn Function> {
match &self.internal {
InternalValue::Function(FunctionValue(function)) => Some(function.as_ref()),
_ => None,
}
}
pub fn as_reference(&self) -> Option<Reference> {
match self.internal {
InternalValue::Reference(reference) => Some(reference),
_ => None,
}
}
pub fn as_doc_node(&self) -> Option<DocNode> {
match &self.internal {
InternalValue::Node(node) => Some(node.clone()),
#[cfg(feature = "experimental_coerced_type")]
InternalValue::CoercedType(value, _) => match value.as_ref() {
InternalValue::Node(node) => Some(node.clone()),
_ => None,
},
_ => None,
}
}
pub fn is_empty(&self) -> bool {
match &self.internal {
InternalValue::Array(array) => array.is_empty(),
InternalValue::String(s) => s.is_empty(),
InternalValue::Object(object) => object.is_empty(),
InternalValue::Null => true,
InternalValue::Node(n) => n.is_empty(),
_ => false,
}
}
}