use std::borrow::Cow;
use compact_str::CompactString;
use crate::graph::GraphStorage;
use crate::traversal::optimizer::path_analysis::{PathAnalysis, PathRequirement};
use crate::traversal::step::local_scope::stored_value;
use crate::value::temporal::zone::Zone;
use crate::value::temporal::{Temporal, TemporalError};
use crate::*;
pub(crate) fn temporal_error(step: &'static str, error: TemporalError) -> Box<TraverserError> {
Box::new(TraverserError::Value(ValueError::Temporal { step, error }))
}
pub(crate) fn input_error(
step: &'static str,
operand: &'static str,
expected: &'static str,
actual: String,
) -> Box<TraverserError> {
Box::new(TraverserError::Value(ValueError::TemporalInput {
step,
operand,
expected,
actual,
}))
}
pub(crate) fn describe(value: &ElementProperty) -> String {
match value {
ElementProperty::Null => "null".to_string(),
other => format!(
"{} {}",
crate::graph::schema::value_type_name(other),
crate::traversal::step::barrier_steps::numeric::preview(other)
),
}
}
pub(crate) fn held_value<'a, S: GraphStorage>(
context: &'a TraverserContext<'_, S>,
value: &'a TraverserValue<S>,
) -> TraverserResult<Result<Cow<'a, ElementProperty>, &'static str>> {
Ok(Ok(match value {
TraverserValue::Computed(property, _) => Cow::Borrowed(property),
TraverserValue::VertexProperty(_, _, kind) | TraverserValue::EdgeProperty(_, _, kind)
if !kind.reads_property() =>
{
Cow::Owned(value.into_node_property(context.storage)?)
}
TraverserValue::VertexProperty(..) | TraverserValue::EdgeProperty(..) => {
match stored_value(context, value) {
Some(stored) => Cow::Borrowed(stored),
None => Cow::Owned(ElementProperty::Null),
}
}
TraverserValue::PathProperty(prop) => Cow::Borrowed(&prop.1),
TraverserValue::Array(_) | TraverserValue::Path(_) => return Ok(Err("list")),
TraverserValue::Object(_) | TraverserValue::Record { .. } | TraverserValue::Entry(_) => {
return Ok(Err("map"));
}
other @ (TraverserValue::Vertex(_)
| TraverserValue::Edge(_)
| TraverserValue::Schema(_)
| TraverserValue::Graph(_)) => return Ok(Err(other.type_name())),
}))
}
pub(crate) fn incoming_temporal<S: GraphStorage>(
context: &TraverserContext<'_, S>,
value: &TraverserValue<S>,
step: &'static str,
expected: &'static str,
) -> TraverserResult<Temporal> {
match held_value(context, value)? {
Ok(held) => match held.as_ref() {
ElementProperty::Temporal(temporal) => Ok(*temporal),
other => Err(input_error(
step,
"incoming value",
expected,
describe(other),
)),
},
Err(kind) => Err(input_error(
step,
"incoming value",
expected,
format!("a {kind}"),
)),
}
}
pub(crate) fn computed<S: GraphStorage>(value: Temporal) -> TraverserValue<S> {
TraverserValue::Computed(ElementProperty::Temporal(value), None)
}
pub(crate) fn replaced<S: GraphStorage>(
source: Traverser<S>,
value: TraverserValue<S>,
) -> Traverser<S> {
Traverser::with_parts(value, source.path, source.bulk, source.sack)
}
#[derive(Clone)]
pub(crate) struct ZoneArg {
text: CompactString,
zone: Result<Zone, TemporalError>,
}
impl ZoneArg {
pub(crate) fn parse(text: &str) -> Self {
ZoneArg {
text: CompactString::new(text),
zone: Zone::parse(text),
}
}
pub(crate) fn get(&self, step: &'static str) -> TraverserResult<&Zone> {
self.zone
.as_ref()
.map_err(|error| temporal_error(step, error.clone()))
}
pub(crate) fn check(&self, step: &'static str) -> TraverserResult<()> {
self.get(step).map(|_| ())
}
pub(crate) fn literal(&self) -> String {
crate::value::literal::rhai_string(&self.text)
}
}
pub(crate) fn check_zone(zone: &Option<ZoneArg>, step: &'static str) -> TraverserResult<()> {
match zone {
Some(zone) => zone.check(step),
None => Ok(()),
}
}
pub(crate) fn zone_of<'a>(
zone: &'a Option<ZoneArg>,
step: &'static str,
) -> TraverserResult<Option<&'a Zone>> {
match zone {
Some(zone) => zone.get(step).map(Some),
None => Ok(None),
}
}
pub(crate) enum Operand<'g, S: GraphStorage + 'g> {
Value(ElementProperty),
Traversal(AnonymousTraversal<'g, S>),
}
impl<'g, S: GraphStorage + 'g> Clone for Operand<'g, S> {
fn clone(&self) -> Self {
match self {
Operand::Value(value) => Operand::Value(value.clone()),
Operand::Traversal(traversal) => Operand::Traversal(traversal.clone()),
}
}
}
impl<'g, S: GraphStorage + 'g> fmt::Display for Operand<'g, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Operand::Value(value) => f.write_str(&crate::value::literal::rhai_literal(value)),
Operand::Traversal(traversal) => write!(f, "{traversal}"),
}
}
}
impl<'g, S: GraphStorage + 'g> Operand<'g, S> {
pub(crate) fn resolve(
&self,
context: &mut TraverserContext<'_, S>,
source: &Traverser<S>,
step: &'static str,
) -> TraverserResult<Option<Temporal>> {
let expected = "a date or time";
match self {
Operand::Value(ElementProperty::Temporal(value)) => Ok(Some(*value)),
Operand::Value(ElementProperty::Null) => Ok(None),
Operand::Value(other) => Err(input_error(step, "argument", expected, describe(other))),
Operand::Traversal(traversal) => {
let Some(first) = traversal.first(context, source)? else {
return Err(input_error(
step,
"traversal argument",
expected,
"no result (the traversal yielded nothing)".to_string(),
));
};
match held_value(context, &first.value)? {
Ok(held) => match held.as_ref() {
ElementProperty::Temporal(value) => Ok(Some(*value)),
ElementProperty::Null => Ok(None),
other => Err(input_error(
step,
"traversal argument",
expected,
describe(other),
)),
},
Err(kind) => Err(input_error(
step,
"traversal argument",
expected,
format!("a {kind}"),
)),
}
}
}
}
pub(crate) fn check(&self, step: &'static str) -> TraverserResult<()> {
match self {
Operand::Value(ElementProperty::Temporal(_) | ElementProperty::Null)
| Operand::Traversal(_) => Ok(()),
Operand::Value(other) => Err(input_error(
step,
"argument",
"a date or time",
describe(other),
)),
}
}
pub(crate) fn analyze_paths(&mut self, analysis: &PathAnalysis) -> PathRequirement {
match self {
Operand::Traversal(traversal) => {
traversal.analyze_paths(analysis, PathRequirement::None)
}
Operand::Value(_) => PathRequirement::None,
}
}
pub(crate) fn children(&mut self) -> Vec<&mut AnonymousTraversal<'g, S>> {
match self {
Operand::Traversal(traversal) => vec![traversal],
Operand::Value(_) => Vec::new(),
}
}
}
pub(crate) fn epoch_of(kind: crate::value::temporal::TemporalKind) -> Option<Temporal> {
use crate::value::temporal::{Date, DateTime, LocalDateTime, TemporalKind as K, Time};
match kind {
K::DateTime => DateTime::from_micros(0, 0).ok().map(Temporal::DateTime),
K::LocalDateTime => LocalDateTime::from_micros(0)
.ok()
.map(Temporal::LocalDateTime),
K::Date => Date::from_days_since_epoch(0).ok().map(Temporal::Date),
K::Time => Time::from_micros_of_day(0).ok().map(Temporal::Time),
K::Duration => None,
}
}