WIP: nested pull.

This commit is contained in:
Richard Newman 2018-05-07 22:33:35 -07:00
parent dc608319b0
commit 2b12c41095
4 changed files with 63 additions and 4 deletions

View file

@ -366,7 +366,11 @@ fn validate_attributes<'a, I>(attrs: I) -> std::result::Result<(), &'static str>
return Err("wildcard with specified attributes");
}
},
// TODO: map form.
&PullAttributeSpec::Nested(ref _attr, ref patterns) => {
if patterns.is_empty() {
return Err("empty nested pull map");
}
},
}
}
Ok(())

View file

@ -27,6 +27,11 @@ error_chain! {
description(":db/id repeated")
display(":db/id repeated")
}
NonRefNestedPullAttribute {
description("nested pull attribute is non-ref")
display("nested pull attribute is non-ref")
}
}
links {

View file

@ -88,6 +88,7 @@ use mentat_core::{
StructuredMap,
TypedValue,
ValueRc,
ValueType,
};
use mentat_db::cache;
@ -144,8 +145,18 @@ pub struct Puller {
// The domain of this map is the set of attributes to fetch.
// The range is the set of aliases to use in the output.
attributes: BTreeMap<Entid, ValueRc<NamespacedKeyword>>,
// The original spec for this puller.
attribute_spec: cache::AttributeSpec,
// If :db/id is mentioned in the attribute list, its alias is this.
db_id_alias: Option<ValueRc<NamespacedKeyword>>,
// A pull expression can be arbitrarily nested. We represent this both
// within the `attribute_spec` itself and also as a nested set of `Puller`s.
// When an attribute in the list above returns an entity -- and it should! --
// it is accumulated and we recurse down into these nested layers.
nested: BTreeMap<Entid, Puller>,
}
impl Puller {
@ -156,8 +167,8 @@ impl Puller {
let lookup_name = |i: &Entid| {
// In the unlikely event that we have an attribute with no name, we bail.
schema.get_ident(*i)
.map(|ident| ValueRc::new(ident.clone()))
.ok_or_else(|| ErrorKind::UnnamedAttribute(*i))
.map(|ident| ValueRc::new(ident.clone()))
.ok_or_else(|| ErrorKind::UnnamedAttribute(*i))
};
let mut names: BTreeMap<Entid, ValueRc<NamespacedKeyword>> = Default::default();
@ -204,6 +215,20 @@ impl Puller {
},
}
},
// An attribute that nests must be ref-typed.
&PullAttributeSpec::Nested(ref attribute, ref patterns) => {
let value_type = attribute.get_attribute(schema)
.map(|(a, _e)| a.value_type);
let is_ref_typed = value_type.map(|v| v == ValueType::Ref)
.unwrap_or(false);
if !is_ref_typed {
bail!(ErrorKind::NonRefNestedPullAttribute);
}
// TODO
unimplemented!();
},
}
}
@ -211,6 +236,7 @@ impl Puller {
attributes: names,
attribute_spec: cache::AttributeSpec::specified(&attrs, schema),
db_id_alias,
nested: Default::default(),
})
}

View file

@ -55,7 +55,11 @@ pub use edn::{
};
use mentat_core::{
Attribute,
FromRc,
HasSchema,
KnownEntid,
Schema,
TypedValue,
ValueRc,
ValueType,
@ -499,6 +503,19 @@ pub enum PullConcreteAttribute {
Entid(i64),
}
impl PullConcreteAttribute {
pub fn get_attribute<'s>(&self, schema: &'s Schema) -> Option<(&'s Attribute, KnownEntid)> {
match self {
&PullConcreteAttribute::Ident(ref rc) => {
schema.attribute_for_ident(rc.as_ref())
},
&PullConcreteAttribute::Entid(e) => {
schema.attribute_for_entid(e).map(|a| (a, KnownEntid(e)))
},
}
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct NamedPullAttribute {
pub attribute: PullConcreteAttribute,
@ -518,7 +535,7 @@ impl From<PullConcreteAttribute> for NamedPullAttribute {
pub enum PullAttributeSpec {
Wildcard,
Attribute(NamedPullAttribute),
// PullMapSpec(Vec<…>),
Nested(PullConcreteAttribute, Vec<PullAttributeSpec>),
// LimitedAttribute(PullConcreteAttribute, u64), // Limit nil => Attribute instead.
// DefaultedAttribute(PullConcreteAttribute, PullDefaultValue),
}
@ -556,6 +573,13 @@ impl std::fmt::Display for PullAttributeSpec {
&PullAttributeSpec::Attribute(ref attr) => {
write!(f, "{}", attr)
},
&PullAttributeSpec::Nested(ref attr, ref patterns) => {
write!(f, "{{{} [", attr)?;
for p in patterns {
write!(f, " {}", p)?;
}
write!(f, "]}}")
},
}
}
}