use super::{
parse::{EntityKey, Verb},
Autogenerate, CLIError, EntityInterface,
};
use crate::{
query::{Insertable, Queryable, RelationInterface},
schema::{
datum::{Datum, DatumDiscriminatorRef, OwnedDatumList},
entity::{Entity, EntityID, EntityPart, EntityPartList, EntityPartVisitor},
relation::{Relation, RelationDomain, RelationMap, RelationRange},
},
DBResult, Transaction,
};
type UniqueList<E> = <<E as Entity>::Keys as EntityPartList>::DatumList;
impl<EI: EntityInterface> Autogenerate<EI> {
pub fn perform(
self,
ctx: &EI::Context,
txn: &mut Transaction,
query_ctx: impl Queryable<EntityOutput = EI::Entity> + Insertable<EI::Entity>,
) -> Result<(), EI::Error> {
match self.verb {
Verb::Attach {
local_keys,
relation,
remote_keys,
} => {
let local_keys = EntityKey::to_string_vec::<EI>(&local_keys, ctx);
let remote_keys = EntityKey::to_string_vec::<EI>(&remote_keys, ctx);
let outer_obj = query_ctx
.keyed(UniqueList::<EI::Entity>::build_equivalent(
local_keys.iter().map(String::as_str),
))
.get(txn)?
.ok_or(<EI::Error>::no_such_entity(
EI::Entity::entity_name(),
local_keys
.iter()
.cloned()
.reduce(|a, b| format!("{},{}", a, b))
.unwrap()
.to_string(),
))?;
let mut attacher = Attacher {
do_attach: true,
relation: relation.as_str(),
txn,
remote_keys,
err: None,
_ghost: Default::default(),
};
outer_obj.accept_part_visitor_ref(&mut attacher)?;
if let Some(err) = attacher.err {
return Err(err);
}
},
Verb::Delete(keys) => {
let keys = EntityKey::to_string_vec::<EI>(&keys, ctx);
query_ctx
.keyed(UniqueList::<EI::Entity>::build_equivalent(
keys.iter().map(String::as_str),
))
.delete(txn)?;
},
Verb::Detach {
local_keys,
relation,
remote_keys,
} => {
let local_keys = EntityKey::to_string_vec::<EI>(&local_keys, ctx);
let remote_keys = EntityKey::to_string_vec::<EI>(&remote_keys, ctx);
let outer_obj = query_ctx
.keyed(UniqueList::<EI::Entity>::build_equivalent(
local_keys.iter().map(String::as_str),
))
.get(txn)?
.ok_or(<EI::Error>::no_such_entity(
EI::Entity::entity_name(),
local_keys
.iter()
.cloned()
.reduce(|a, b| format!("{},{}", a, b))
.unwrap()
.to_string(),
))?;
let mut attacher = Attacher {
do_attach: false,
relation: relation.as_str(),
txn,
remote_keys,
err: None,
_ghost: Default::default(),
};
outer_obj.accept_part_visitor_ref(&mut attacher)?;
if let Some(err) = attacher.err {
return Err(err);
}
},
Verb::ListAll => {
println!(
"Listing all {}(s): ({})",
EI::Entity::entity_name(),
query_ctx.clone().count(txn)?
);
for obj in query_ctx.get(txn)?.into_iter() {
println!(
" - {}",
EI::summarize(&obj).unwrap_or_else(|| format!("{:?}", obj))
);
}
},
Verb::Inspect(keys) => {
let keys = EntityKey::to_string_vec::<EI>(&keys, ctx);
let obj = query_ctx
.keyed(UniqueList::<EI::Entity>::build_equivalent(
keys.iter().map(String::as_str),
))
.get(txn)?
.ok_or(<EI::Error>::no_such_entity(
EI::Entity::entity_name(),
keys.iter()
.cloned()
.reduce(|a, b| format!("{},{}", a, b))
.unwrap() .to_string(),
))?;
println!("{:#?}", obj.as_ref());
fn inspect_ai<AI: RelationInterface>(
name: &'static str,
txn: &mut Transaction,
ai: &AI,
) {
println!("{}: ({})", name, ai.count(txn).unwrap());
for a in ai.get(txn).expect("couldn't get object relations") {
println!("[#{:3}]: {:?}", a.id().into_raw(), a.wrapped());
}
}
struct RelationFieldWalker<'r, E: Entity>(
&'r mut Transaction,
std::marker::PhantomData<E>,
);
impl<E: Entity> EntityPartVisitor for RelationFieldWalker<'_, E> {
type Entity = E;
fn visit_datum<EP: EntityPart>(&mut self, datum: &EP::Datum) -> DBResult<()> {
struct Discriminator<'r>(&'r mut Transaction, &'static str);
impl DatumDiscriminatorRef for Discriminator<'_> {
fn visit_serialized<
T: serde::Serialize + serde::de::DeserializeOwned,
>(
&mut self,
_: &T,
) {
}
fn visit_bare_field<T: Datum>(&mut self, _: &T) {}
fn visit_entity_id<E: Entity>(&mut self, _: &E::ID) {}
fn visit_relation_map<E: Entity>(&mut self, amap: &RelationMap<E>) {
inspect_ai(self.1, self.0, amap);
}
fn visit_relation_domain<R: Relation>(
&mut self,
adomain: &RelationDomain<R>,
) {
inspect_ai(self.1, self.0, adomain);
}
fn visit_relation_range<R: Relation>(
&mut self,
arange: &RelationRange<R>,
) {
inspect_ai(self.1, self.0, arange);
}
fn visit_value<T: serde::de::DeserializeOwned>(&mut self, _: &T) {}
}
datum.accept_discriminator_ref(&mut Discriminator(self.0, EP::part_name()));
Ok(())
}
}
obj.accept_part_visitor_ref(&mut RelationFieldWalker(txn, Default::default()))?;
},
Verb::Custom(custom) => {
EI::run_custom(ctx, custom, txn, query_ctx)?;
},
}
Ok(())
}
}
struct Attacher<'r, Error: CLIError, E: Entity> {
do_attach: bool,
relation: &'r str,
txn: &'r mut Transaction,
remote_keys: Vec<String>,
err: Option<Error>,
_ghost: std::marker::PhantomData<E>,
}
impl<Error: CLIError, OE: Entity> Attacher<'_, Error, OE> {
fn do_operation<E: Entity>(&mut self, map: &impl RelationInterface<RemoteEntity = E>) {
match map
.query_all()
.keyed(UniqueList::<E>::build_equivalent(
self.remote_keys.iter().map(String::as_str),
))
.get(self.txn)
{
Ok(Some(obj)) => {
if self.do_attach {
self.err = map.connect_to(self.txn, obj.id()).err().map(Into::into);
} else {
self.err = map
.disconnect_from(self.txn, obj.id())
.err()
.map(Into::into);
}
},
Ok(None) => {
self.err = Some(Error::no_such_entity(
E::entity_name(),
self.remote_keys
.iter()
.cloned()
.reduce(|a, b| format!("{},{}", a, b))
.unwrap_or_default()
.to_string(),
));
},
Err(e) => {
self.err = Some(e.into());
},
}
}
}
impl<Error: CLIError, E: Entity> EntityPartVisitor for Attacher<'_, Error, E> {
type Entity = E;
fn visit_datum<EP: EntityPart>(&mut self, datum: &EP::Datum) -> DBResult<()> {
if EP::part_name() != self.relation {
return Ok(());
}
datum.accept_discriminator_ref(self);
Ok(())
}
}
impl<Error: CLIError, OE: Entity> DatumDiscriminatorRef for Attacher<'_, Error, OE> {
fn visit_entity_id<E: Entity>(&mut self, _: &E::ID) {
unreachable!()
}
fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(&mut self, _: &T) {
unreachable!()
}
fn visit_bare_field<T: Datum>(&mut self, _: &T) {
unreachable!()
}
fn visit_value<T: serde::de::DeserializeOwned>(&mut self, _: &T) {
unreachable!()
}
fn visit_relation_map<E: Entity>(&mut self, map: &RelationMap<E>) {
self.do_operation(map);
}
fn visit_relation_range<R: Relation>(&mut self, map: &RelationRange<R>) {
self.do_operation(map);
}
fn visit_relation_domain<R: Relation>(&mut self, map: &RelationDomain<R>) {
self.do_operation(map);
}
}