use crate::compiler::{
Context, Expression,
expression::{Container, Resolved, Variable},
parser::ast::Ident,
state::ExternalEnv,
state::{TypeInfo, TypeState},
type_def::Details,
};
use crate::path::{OwnedTargetPath, OwnedValuePath, PathPrefix};
use crate::value::Value;
use std::fmt;
#[derive(Clone, PartialEq)]
pub struct Query {
target: Target,
path: OwnedTargetPath,
}
impl Query {
#[must_use]
pub fn new(target: Target, path: OwnedValuePath) -> Self {
let prefix = match &target {
Target::External(prefix) => *prefix,
_ => PathPrefix::Event,
};
let path = OwnedTargetPath { prefix, path };
Query { target, path }
}
#[must_use]
pub fn path(&self) -> &OwnedValuePath {
&self.path.path
}
#[must_use]
pub fn target(&self) -> &Target {
&self.target
}
#[must_use]
pub fn is_external(&self) -> bool {
matches!(self.target, Target::External(_))
}
#[must_use]
pub fn external_path(&self) -> Option<&OwnedTargetPath> {
match self.target {
Target::External(_) => Some(&self.path),
_ => None,
}
}
#[must_use]
pub fn as_variable(&self) -> Option<&Variable> {
match &self.target {
Target::Internal(variable) => Some(variable),
_ => None,
}
}
#[must_use]
pub fn variable_ident(&self) -> Option<&Ident> {
match &self.target {
Target::Internal(v) => Some(v.ident()),
_ => None,
}
}
#[must_use]
pub fn expression_target(&self) -> Option<&dyn Expression> {
match &self.target {
Target::FunctionCall(expr) => Some(expr),
Target::Container(expr) => Some(expr),
_ => None,
}
}
pub fn delete_type_def(&self, external: &mut ExternalEnv, compact: bool) {
if let Some(target_path) = self.external_path() {
match target_path.prefix {
PathPrefix::Event => {
let mut type_def = external.target().type_def.clone();
type_def.remove(&target_path.path, compact);
external.update_target(Details {
type_def,
value: None,
});
}
PathPrefix::Metadata => {
let mut kind = external.metadata_kind().clone();
kind.remove(&target_path.path, compact);
external.update_metadata(kind);
}
}
}
}
}
impl Expression for Query {
fn resolve(&self, ctx: &mut Context) -> Resolved {
use Target::{Container, External, FunctionCall, Internal};
let owned_value;
let value = match &self.target {
External(_) => {
return Ok(ctx
.target()
.target_get(&self.path)
.ok()
.flatten()
.cloned()
.unwrap_or(Value::Null));
}
Internal(variable) => ctx
.state()
.variable(variable.ident())
.unwrap_or(&Value::Null),
FunctionCall(call) => {
owned_value = call.resolve(ctx)?;
&owned_value
}
Container(container) => {
owned_value = container.resolve(ctx)?;
&owned_value
}
};
Ok(value.get(self.path()).cloned().unwrap_or(Value::Null))
}
fn resolve_constant(&self, state: &TypeState) -> Option<Value> {
match self.target {
Target::Internal(ref variable) => variable
.resolve_constant(state)
.and_then(|v| v.get(self.path()).cloned()),
_ => None,
}
}
fn type_info(&self, state: &TypeState) -> TypeInfo {
use Target::{Container, External, FunctionCall, Internal};
match &self.target {
External(prefix) => {
let result = state.external.kind(*prefix).at_path(self.path()).into();
TypeInfo::new(state, result)
}
Internal(variable) => {
let result = variable.type_def(state).at_path(self.path());
TypeInfo::new(state, result)
}
FunctionCall(call) => call
.type_info(state)
.map_result(|result| result.at_path(self.path())),
Container(container) => container
.type_info(state)
.map_result(|result| result.at_path(self.path())),
}
}
}
impl fmt::Display for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.target {
Target::Internal(_)
if !self.path().is_root() && !self.path().segments.first().unwrap().is_index() =>
{
write!(f, "{}.{}", self.target, self.path())
}
_ => write!(f, "{}{}", self.target, self.path()),
}
}
}
impl fmt::Debug for Query {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Query({:?}, {:?})", self.target, self.path())
}
}
#[derive(Clone, PartialEq)]
pub enum Target {
Internal(Variable),
External(PathPrefix),
FunctionCall(crate::compiler::expression::FunctionCall),
Container(Container),
}
impl fmt::Display for Target {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Target::{Container, External, FunctionCall, Internal};
match self {
Internal(v) => v.fmt(f),
External(prefix) => match prefix {
PathPrefix::Event => write!(f, "."),
PathPrefix::Metadata => write!(f, "%"),
},
FunctionCall(v) => v.fmt(f),
Container(v) => v.fmt(f),
}
}
}
impl fmt::Debug for Target {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
use Target::{Container, External, FunctionCall, Internal};
match self {
Internal(v) => write!(f, "Internal({v:?})"),
External(prefix) => match prefix {
PathPrefix::Event => f.write_str("External(Event)"),
PathPrefix::Metadata => f.write_str("External(Metadata)"),
},
FunctionCall(v) => v.fmt(f),
Container(v) => v.fmt(f),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::compiler::{
TimeZone, TypeDef,
state::{LocalEnv, RuntimeState},
};
use crate::owned_value_path;
#[test]
fn variable_query_returns_null_when_runtime_variable_is_missing() {
let ident = Ident::new("foo");
let mut local = LocalEnv::default();
local.insert_variable(
ident.clone(),
Details {
type_def: TypeDef::any(),
value: None,
},
);
let variable = Variable::new((0, 0).into(), ident, &local).unwrap();
let query = Query::new(Target::Internal(variable), owned_value_path!("field"));
let mut state = RuntimeState::default();
let mut target = Value::Null;
let timezone = TimeZone::default();
let mut ctx = Context::new(&mut target, &mut state, &timezone);
assert_eq!(query.resolve(&mut ctx), Ok(Value::Null));
}
#[test]
fn external_root_query_has_infallible_object_type() {
let query = Query::new(Target::External(PathPrefix::Event), OwnedValuePath::root());
let state = TypeState::default();
let type_def = query.type_info(&state).result;
assert!(type_def.is_infallible());
assert!(type_def.is_object());
let object = type_def.as_object().unwrap();
assert!(object.is_any());
}
}