use super::{Autogenerate, EntityInterface, ValueRole};
use crate::schema::{
datum::{Datum, DatumDiscriminator},
entity::{Entity, EntityPart, EntityPartList, EntityPartVisitor, EntityVisitor},
relation::Relation,
};
use clap::{FromArgMatches, Subcommand};
#[derive(Debug, Clone)]
pub enum EntityKey {
Placeholder {
entity: &'static str,
field: &'static str,
role: ValueRole,
},
UserInput(String),
}
impl EntityKey {
pub(crate) fn to_string_vec<EI: EntityInterface>(
vec: &[Self],
ctx: &EI::Context,
) -> Vec<String> {
vec.iter()
.map(|v| match v {
EntityKey::UserInput(s) => s.to_owned(),
EntityKey::Placeholder {
entity,
field,
role,
} => EI::override_for(ctx, entity, field, *role),
})
.collect()
}
}
fn add_keys<E: Entity, EI: EntityInterface>(
mut cmd: clap::Command,
role: ValueRole,
) -> clap::Command {
struct UVisitor<'a, E: Entity, EI: EntityInterface>(
&'a mut clap::Command,
ValueRole,
std::marker::PhantomData<(E, EI)>,
);
impl<'a, E: Entity, EI: EntityInterface> EntityPartVisitor for UVisitor<'a, E, EI> {
type Entity = E;
fn visit<EP: EntityPart>(&mut self) {
if !EI::should_override(EP::Entity::entity_name(), EP::part_name(), self.1) {
let arg = clap::Arg::new(EP::part_name())
.required(true)
.help(EP::desc());
*self.0 = self.0.clone().arg(arg);
}
}
}
<E::Keys as EntityPartList>::accept_part_visitor(&mut UVisitor::<E, EI>(
&mut cmd,
role,
Default::default(),
));
cmd
}
fn collect_keys<E: Entity, EI: EntityInterface>(
matches: &clap::ArgMatches,
role: ValueRole,
) -> Vec<EntityKey> {
struct UVisitor<'a, E: Entity, EI: EntityInterface>(
&'a clap::ArgMatches,
&'a mut Vec<EntityKey>,
ValueRole,
std::marker::PhantomData<(E, EI)>,
);
impl<'a, E: Entity, EI: EntityInterface> EntityPartVisitor for UVisitor<'a, E, EI> {
type Entity = E;
fn visit<EP: EntityPart>(&mut self) {
if !EI::should_override(EP::Entity::entity_name(), EP::part_name(), self.2) {
self.1.push(EntityKey::UserInput(
self.0
.get_one::<std::string::String>(EP::part_name())
.unwrap()
.clone(),
));
} else {
self.1.push(EntityKey::Placeholder {
entity: EP::Entity::entity_name(),
field: EP::part_name(),
role: self.2,
});
}
}
}
let mut key_values = vec![];
<E::Keys as EntityPartList>::accept_part_visitor(&mut UVisitor::<E, EI>(
matches,
&mut key_values,
role,
Default::default(),
));
key_values
}
#[derive(Clone, Debug)]
pub(crate) enum Verb<EI: EntityInterface> {
Attach {
local_keys: Vec<EntityKey>,
relation: String,
remote_keys: Vec<EntityKey>,
},
Delete(Vec<EntityKey>),
Detach {
local_keys: Vec<EntityKey>,
relation: String,
remote_keys: Vec<EntityKey>,
},
ListAll,
Inspect(Vec<EntityKey>),
Custom(EI::CustomCommand),
}
impl<EI: EntityInterface> Verb<EI> {
fn parse_attachment(
matches: &clap::ArgMatches,
) -> Result<(Vec<EntityKey>, String, Vec<EntityKey>), clap::Error> {
let local_keys = collect_keys::<EI::Entity, EI>(matches, ValueRole::BaseTarget);
let (subcommand, submatches) = matches
.subcommand()
.ok_or(clap::Error::new(clap::error::ErrorKind::MissingSubcommand))?;
struct RelationFinder<'l, EI: EntityInterface> {
subcommand: &'l str,
submatches: &'l clap::ArgMatches,
keys: &'l mut Vec<EntityKey>,
_ghost: std::marker::PhantomData<(EI,)>,
}
impl<'l, EI: EntityInterface> EntityPartVisitor for RelationFinder<'l, EI> {
type Entity = EI::Entity;
fn visit<EP: EntityPart>(&mut self) {
if EP::part_name() != self.subcommand {
return;
}
EP::Datum::accept_entity_visitor(self);
}
}
impl<'l, EI: EntityInterface> EntityVisitor for RelationFinder<'l, EI> {
fn visit<E: Entity>(&mut self) {
*self.keys = collect_keys::<E, EI>(self.submatches, ValueRole::AttachmentTarget);
}
}
let mut remote_keys = vec![];
EI::Entity::accept_part_visitor(&mut RelationFinder::<EI> {
subcommand,
submatches,
keys: &mut remote_keys,
_ghost: Default::default(),
});
Ok((local_keys, subcommand.into(), remote_keys))
}
fn from_matches(parent_matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
let (subcommand, matches) = parent_matches
.subcommand()
.ok_or(clap::Error::new(clap::error::ErrorKind::MissingSubcommand))?;
Ok(match subcommand {
"attach" => {
let (local_keys, relation, remote_keys) = Self::parse_attachment(matches)?;
Self::Attach {
local_keys,
relation,
remote_keys,
}
},
"delete" => Self::Delete(collect_keys::<EI::Entity, EI>(
matches,
ValueRole::BaseTarget,
)),
"detach" => {
let (local_keys, relation, remote_keys) = Self::parse_attachment(matches)?;
Self::Detach {
local_keys,
relation,
remote_keys,
}
},
"list" => Verb::ListAll,
"inspect" => Self::Inspect(collect_keys::<EI::Entity, EI>(
matches,
ValueRole::BaseTarget,
)),
cmd => {
if EI::CustomCommand::has_subcommand(cmd) {
Self::Custom(EI::CustomCommand::from_arg_matches(parent_matches)?)
} else {
unreachable!()
}
},
})
}
}
impl<EI: EntityInterface> Autogenerate<EI> {
fn make_relation_subcommands() -> impl Iterator<Item = clap::Command> {
let mut out = vec![];
struct PartVisitor<'l, EI: EntityInterface>(
&'l mut Vec<clap::Command>,
std::marker::PhantomData<(EI,)>,
);
impl<'l, EI: EntityInterface> EntityPartVisitor for PartVisitor<'l, EI> {
type Entity = EI::Entity;
fn visit<EP: EntityPart>(&mut self) {
struct Discriminator<'l, EI: EntityInterface>(
&'l mut Vec<clap::Command>,
&'static str,
std::marker::PhantomData<(EI,)>,
);
impl<'l, EI: EntityInterface> DatumDiscriminator for Discriminator<'l, EI> {
fn visit_entity_id<E: Entity>(&mut self) {}
fn visit_serialized<T: serde::Serialize + serde::de::DeserializeOwned>(
&mut self,
) {
}
fn visit_bare_field<T: Datum>(&mut self) {}
fn visit_relation_map<E: Entity>(&mut self) {
self.0.push(add_keys::<E, EI>(
clap::Command::new(self.1),
ValueRole::AttachmentTarget,
));
}
fn visit_relation_domain<R: Relation>(&mut self) {
self.0.push(add_keys::<R::Range, EI>(
clap::Command::new(self.1),
ValueRole::AttachmentTarget,
));
}
fn visit_relation_range<R: Relation>(&mut self) {
self.0.push(add_keys::<R::Domain, EI>(
clap::Command::new(self.1),
ValueRole::AttachmentTarget,
));
}
}
<EP::Datum as Datum>::accept_discriminator(&mut Discriminator::<EI>(
self.0,
EP::part_name(),
Default::default(),
));
}
}
EI::Entity::accept_part_visitor(&mut PartVisitor::<EI>(&mut out, Default::default()));
out.into_iter()
}
}
impl<EI: EntityInterface> clap::FromArgMatches for Autogenerate<EI> {
fn from_arg_matches(matches: &clap::ArgMatches) -> Result<Self, clap::Error> {
let verb = Verb::from_matches(matches);
Ok(Self {
verb: verb?,
_ghost: Default::default(),
})
}
fn update_from_arg_matches(&mut self, _matches: &clap::ArgMatches) -> Result<(), clap::Error> {
todo!()
}
}
impl<EI: EntityInterface> clap::Subcommand for Autogenerate<EI> {
fn has_subcommand(_name: &str) -> bool {
todo!()
}
fn augment_subcommands(cmd: clap::Command) -> clap::Command {
let cmd = cmd
.subcommand(
add_keys::<EI::Entity, EI>(clap::Command::new("attach"), ValueRole::BaseTarget)
.subcommands(Self::make_relation_subcommands())
.subcommand_required(true),
)
.subcommand(
add_keys::<EI::Entity, EI>(clap::Command::new("detach"), ValueRole::BaseTarget)
.subcommands(Self::make_relation_subcommands())
.subcommand_required(true),
)
.subcommand(add_keys::<EI::Entity, EI>(
clap::Command::new("delete"),
ValueRole::BaseTarget,
))
.subcommand(add_keys::<EI::Entity, EI>(
clap::Command::new("inspect"),
ValueRole::BaseTarget,
))
.subcommand(clap::Command::new("list"));
EI::CustomCommand::augment_subcommands(cmd)
}
fn augment_subcommands_for_update(_cmd: clap::Command) -> clap::Command {
todo!()
}
}