mentat/query-projector/src/binding_tuple.rs
Nick Alexander 2cb7d441dc Part 2: Make it easier to match tuple results.
Right now, we write code like
```rust
match q_once(q, inputs)?.into_tuple()? {
    Some(vs) => match (vs.len(), vs.get(0), vs.get(1)) {
        (2, &Some(Binding::Scalar(TypedValue::Long(a))), &Some(Binding::Scalar(TypedValue::Instant(ref b)))) => Some((a, b.clone())),
        _ => panic!(),
    },
    None => None,
}
```
to length-check tuples coming out of the database.  It can also lead
to a lot of cloning because references are the easiest thing to hand.

This commit allows to write code like
```rust
match q_once(q, inputs)?.into_tuple()? {
    Some((Binding::Scalar(TypedValue::Long(a)), Binding::Scalar(TypedValue::Instant(b)))) => Some((a, b)),
    Some(_) => panic!(),
    None => None,
}
```
which is generally much easier to reason about.
2018-07-05 16:45:42 -07:00

160 lines
6 KiB
Rust

// Copyright 2018 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.
use mentat_core::{
Binding,
};
use errors::{
ProjectorError,
Result,
};
/// A `BindingTuple` is any type that can accommodate a Mentat tuple query result of fixed length.
///
/// Currently Rust tuples of length 1 through 6 (i.e., `(A)` through `(A, B, C, D, E, F)`) are
/// supported as are vectors (i.e., `Vec<>`).
pub trait BindingTuple: Sized {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>>;
}
// This is a no-op, essentially: we can always produce a vector representation of a tuple result.
impl BindingTuple for Vec<Binding> {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
} else {
Ok(Some(vec))
}
},
}
}
}
// TODO: generate these repetitive implementations with a little macro.
impl BindingTuple for (Binding,) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 1 {
return Err(ProjectorError::UnexpectedResultsTupleLength(1, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
} else {
let mut iter = vec.into_iter();
Ok(Some((iter.next().unwrap(),)))
}
}
}
}
}
impl BindingTuple for (Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 2 {
return Err(ProjectorError::UnexpectedResultsTupleLength(2, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
} else {
let mut iter = vec.into_iter();
Ok(Some((iter.next().unwrap(), iter.next().unwrap())))
}
}
}
}
}
impl BindingTuple for (Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 3 {
return Err(ProjectorError::UnexpectedResultsTupleLength(3, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
} else {
let mut iter = vec.into_iter();
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
}
}
}
}
}
impl BindingTuple for (Binding, Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 4 {
return Err(ProjectorError::UnexpectedResultsTupleLength(4, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
} else {
let mut iter = vec.into_iter();
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
}
}
}
}
}
impl BindingTuple for (Binding, Binding, Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 5 {
return Err(ProjectorError::UnexpectedResultsTupleLength(5, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
} else {
let mut iter = vec.into_iter();
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
}
}
}
}
}
// TODO: allow binding tuples of length more than 6. Folks who are binding such large tuples are
// probably doing something wrong -- they should investigate a pull expression.
impl BindingTuple for (Binding, Binding, Binding, Binding, Binding, Binding) {
fn from_binding_vec(expected: usize, vec: Option<Vec<Binding>>) -> Result<Option<Self>> {
if expected != 6 {
return Err(ProjectorError::UnexpectedResultsTupleLength(6, expected));
}
match vec {
None => Ok(None),
Some(vec) => {
if expected != vec.len() {
Err(ProjectorError::UnexpectedResultsTupleLength(expected, vec.len()))
} else {
let mut iter = vec.into_iter();
Ok(Some((iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap(), iter.next().unwrap())))
}
}
}
}
}