mentat/edn/src/entities.rs

241 lines
7.3 KiB
Rust

// Copyright 2016 Mozilla
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use
// this file except in compliance with the License. You may obtain a copy of the
// License at http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software distributed
// under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//! This module defines core types that support the transaction processor.
use std::collections::BTreeMap;
use std::fmt;
use crate::value_rc::ValueRc;
use crate::symbols::{Keyword, PlainSymbol};
use crate::types::ValueAndSpan;
/// `EntityPlace` and `ValuePlace` embed values, either directly (i.e., `ValuePlace::Atom`) or
/// indirectly (i.e., `EntityPlace::LookupRef`). In order to maintain the graph of `Into` and
/// `From` relations, we need to ensure that `{Value,Entity}Place` can't match as a potential value.
/// (If it does, the `impl Into<T> for T` default conflicts.) This marker trait allows to mark
/// acceptable values, thereby removing `{Entity,Value}Place` from consideration.
pub trait TransactableValueMarker {}
/// `ValueAndSpan` is the value type coming out of the entity parser.
impl TransactableValueMarker for ValueAndSpan {}
/// A tempid, either an external tempid given in a transaction (usually as an `Value::Text`),
/// or an internal tempid allocated by Mentat itself.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum TempId {
External(String),
Internal(i64),
}
impl TempId {
pub fn into_external(self) -> Option<String> {
match self {
TempId::External(s) => Some(s),
TempId::Internal(_) => None,
}
}
}
impl fmt::Display for TempId {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
TempId::External(ref s) => write!(f, "{}", s),
TempId::Internal(x) => write!(f, "<tempid {}>", x),
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum EntidOrIdent {
Entid(i64),
Ident(Keyword),
}
impl From<i64> for EntidOrIdent {
fn from(v: i64) -> Self {
EntidOrIdent::Entid(v)
}
}
impl From<Keyword> for EntidOrIdent {
fn from(v: Keyword) -> Self {
EntidOrIdent::Ident(v)
}
}
impl EntidOrIdent {
pub fn unreversed(&self) -> Option<EntidOrIdent> {
match self {
EntidOrIdent::Entid(_) => None,
EntidOrIdent::Ident(ref a) => a.unreversed().map(EntidOrIdent::Ident),
}
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub struct LookupRef<V> {
pub a: AttributePlace,
// In theory we could allow nested lookup-refs. In practice this would require us to process
// lookup-refs in multiple phases, like how we resolve tempids, which isn't worth the effort.
pub v: V, // An atom.
}
/// A "transaction function" that exposes some value determined by the current transaction. The
/// prototypical example is the current transaction ID, `(transaction-tx)`.
///
/// A natural next step might be to expose the current transaction instant `(transaction-instant)`,
/// but that's more difficult: the transaction itself can set the transaction instant (with some
/// restrictions), so the transaction function must be late-binding. Right now, that's difficult to
/// arrange in the transactor.
///
/// In the future, we might accept arguments; for example, perhaps we might expose `(ancestor
/// (transaction-tx) n)` to find the n-th ancestor of the current transaction. If we do accept
/// arguments, then the special case of `(lookup-ref a v)` should be handled as part of the
/// generalization.
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub struct TxFunction {
pub op: PlainSymbol,
}
pub type MapNotation<V> = BTreeMap<EntidOrIdent, ValuePlace<V>>;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum ValuePlace<V> {
// We never know at parse-time whether an integer or ident is really an entid, but we will often
// know when building entities programmatically.
Entid(EntidOrIdent),
// We never know at parse-time whether a string is really a tempid, but we will often know when
// building entities programmatically.
TempId(ValueRc<TempId>),
LookupRef(LookupRef<V>),
TxFunction(TxFunction),
Vector(Vec<ValuePlace<V>>),
Atom(V),
MapNotation(MapNotation<V>),
}
impl<V: TransactableValueMarker> From<EntidOrIdent> for ValuePlace<V> {
fn from(v: EntidOrIdent) -> Self {
ValuePlace::Entid(v)
}
}
impl<V: TransactableValueMarker> From<TempId> for ValuePlace<V> {
fn from(v: TempId) -> Self {
ValuePlace::TempId(v.into())
}
}
impl<V: TransactableValueMarker> From<ValueRc<TempId>> for ValuePlace<V> {
fn from(v: ValueRc<TempId>) -> Self {
ValuePlace::TempId(v)
}
}
impl<V: TransactableValueMarker> From<LookupRef<V>> for ValuePlace<V> {
fn from(v: LookupRef<V>) -> Self {
ValuePlace::LookupRef(v)
}
}
impl<V: TransactableValueMarker> From<TxFunction> for ValuePlace<V> {
fn from(v: TxFunction) -> Self {
ValuePlace::TxFunction(v)
}
}
impl<V: TransactableValueMarker> From<Vec<ValuePlace<V>>> for ValuePlace<V> {
fn from(v: Vec<ValuePlace<V>>) -> Self {
ValuePlace::Vector(v)
}
}
impl<V: TransactableValueMarker> From<V> for ValuePlace<V> {
fn from(v: V) -> Self {
ValuePlace::Atom(v)
}
}
impl<V: TransactableValueMarker> From<MapNotation<V>> for ValuePlace<V> {
fn from(v: MapNotation<V>) -> Self {
ValuePlace::MapNotation(v)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum EntityPlace<V> {
Entid(EntidOrIdent),
TempId(ValueRc<TempId>),
LookupRef(LookupRef<V>),
TxFunction(TxFunction),
}
impl<V, E: Into<EntidOrIdent>> From<E> for EntityPlace<V> {
fn from(v: E) -> Self {
EntityPlace::Entid(v.into())
}
}
impl<V: TransactableValueMarker> From<TempId> for EntityPlace<V> {
fn from(v: TempId) -> Self {
EntityPlace::TempId(v.into())
}
}
impl<V: TransactableValueMarker> From<ValueRc<TempId>> for EntityPlace<V> {
fn from(v: ValueRc<TempId>) -> Self {
EntityPlace::TempId(v)
}
}
impl<V: TransactableValueMarker> From<LookupRef<V>> for EntityPlace<V> {
fn from(v: LookupRef<V>) -> Self {
EntityPlace::LookupRef(v)
}
}
impl<V: TransactableValueMarker> From<TxFunction> for EntityPlace<V> {
fn from(v: TxFunction) -> Self {
EntityPlace::TxFunction(v)
}
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum AttributePlace {
Entid(EntidOrIdent),
}
impl<A: Into<EntidOrIdent>> From<A> for AttributePlace {
fn from(v: A) -> Self {
AttributePlace::Entid(v.into())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum OpType {
Add,
Retract,
}
#[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)]
pub enum Entity<V> {
// Like [:db/add|:db/retract e a v].
AddOrRetract {
op: OpType,
e: EntityPlace<V>,
a: AttributePlace,
v: ValuePlace<V>,
},
// Like {:db/id "tempid" a1 v1 a2 v2}.
MapNotation(MapNotation<V>),
}