// 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. #![allow(dead_code)] //! Types used only within the transactor. These should not be exposed outside of this crate. use std::collections::HashMap; use std::rc::Rc; use errors; use errors::ErrorKind; use types::{ AVMap, AVPair, Entid, TypedValue, }; use mentat_tx::entities::{ OpType, TempId, }; #[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] pub enum Term { AddOrRetract(OpType, E, Entid, V), } #[derive(Clone, Debug, Eq, Hash, Ord, PartialOrd, PartialEq)] pub enum Either { Left(L), Right(R), } use self::Either::*; pub type EntidOr = Either; pub type TypedValueOr = Either; pub type TempIdHandle = Rc; pub type TempIdMap = HashMap; pub type LookupRef = Rc; /// Internal representation of an entid on its way to resolution. We either have the simple case (a /// numeric entid), a lookup-ref that still needs to be resolved (an atomized [a v] pair), or a temp /// ID that needs to be upserted or allocated (an atomized tempid). #[derive(Clone,Debug,Eq,Hash,Ord,PartialOrd,PartialEq)] pub enum LookupRefOrTempId { LookupRef(LookupRef), TempId(TempIdHandle) } pub type TermWithTempIdsAndLookupRefs = Term, TypedValueOr>; pub type TermWithTempIds = Term, TypedValueOr>; pub type TermWithoutTempIds = Term; pub type Population = Vec; impl TermWithTempIds { // These have no tempids by definition, and just need to be unwrapped. This operation might // also be called "lowering" or "level lowering", but the concept of "unwrapping" is common in // Rust and seems appropriate here. pub fn unwrap(self) -> TermWithoutTempIds { match self { Term::AddOrRetract(op, Left(n), a, Left(v)) => Term::AddOrRetract(op, n, a, v), _ => unreachable!(), } } } /// Given an `EntidOr` or a `TypedValueOr`, replace any internal `LookupRef` with the entid from /// the given map. Fail if any `LookupRef` cannot be replaced. /// /// `lift` allows to specify how the entid found is mapped into the output type. (This could /// also be an `Into` or `From` requirement.) /// /// The reason for this awkward expression is that we're parameterizing over the _type constructor_ /// (`EntidOr` or `TypedValueOr`), which is not trivial to express in Rust. This only works because /// they're both the same `Result<...>` type with different parameterizations. pub fn replace_lookup_ref(lookup_map: &AVMap, desired_or: Either, lift: U) -> errors::Result> where U: FnOnce(Entid) -> T { match desired_or { Left(desired) => Ok(Left(desired)), // N.b., must unwrap here -- the ::Left types are different! Right(other) => { match other { LookupRefOrTempId::TempId(t) => Ok(Right(t)), LookupRefOrTempId::LookupRef(av) => lookup_map.get(&*av) .map(|x| lift(*x)).map(Left) // XXX TODO: fix this error kind! .ok_or_else(|| ErrorKind::UnrecognizedIdent(format!("couldn't lookup [a v]: {:?}", (*av).clone())).into()), } } } }