better root_handler handling

This commit is contained in:
Aiden McClelland
2024-11-01 15:10:33 -06:00
parent 39a872a129
commit 021379f21c
3 changed files with 657 additions and 495 deletions

879
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -43,8 +43,8 @@ impl<Context: crate::Context + Clone, Config: CommandFactory + FromArgMatches>
} }
pub fn run(self, args: impl IntoIterator<Item = OsString>) -> Result<(), RpcError> { pub fn run(self, args: impl IntoIterator<Item = OsString>) -> Result<(), RpcError> {
let mut cmd = Config::command(); let mut cmd = Config::command();
for (name, handler) in &self.root_handler.subcommands.0 { for (name, handler) in &self.root_handler.subcommands.1 {
if let (Name(Some(name)), Some(cli)) = (name, handler.cli()) { if let (Name(name), Some(cli)) = (name, handler.cli()) {
cmd = cmd.subcommand(cli.cli_command().name(name)); cmd = cmd.subcommand(cli.cli_command().name(name));
} }
} }

View File

@@ -14,32 +14,52 @@ use crate::{
}; };
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct Name(pub(crate) Option<&'static str>); pub(crate) struct Name(pub(crate) &'static str);
impl<'a> std::borrow::Borrow<Option<&'a str>> for Name { impl<'a> std::borrow::Borrow<&'a str> for Name {
fn borrow(&self) -> &Option<&'a str> { fn borrow(&self) -> &&'a str {
&self.0 &self.0
} }
} }
pub(crate) struct SubcommandMap<Context, Inherited>( pub(crate) struct SubcommandMap<Context, Params, InheritedParams>(
pub(crate) OrdMap<Name, DynHandler<Context, Inherited>>, pub(crate) Option<DynHandler<Context, InheritedParams>>,
pub(crate) OrdMap<Name, DynHandler<Context, Flat<Params, InheritedParams>>>,
); );
impl<Context, Inherited> Clone for SubcommandMap<Context, Inherited> { impl<Context, Params, InheritedParams> Clone for SubcommandMap<Context, Params, InheritedParams> {
fn clone(&self) -> Self { fn clone(&self) -> Self {
Self(self.0.clone()) Self(self.0.clone(), self.1.clone())
} }
} }
impl<Context, Inherited> Debug for SubcommandMap<Context, Inherited> { impl<Context, Params, InheritedParams> Debug for SubcommandMap<Context, Params, InheritedParams> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_map().entries(self.0.iter()).finish() let mut map = f.debug_map();
if let Some(root) = &self.0 {
#[derive(Debug)]
struct Root;
map.entry(&Root, root);
}
map.entries(self.1.iter()).finish()
} }
} }
impl<Context, Inherited> SubcommandMap<Context, Inherited> { impl<Context, Params, InheritedParams> SubcommandMap<Context, Params, InheritedParams> {
fn insert(&mut self, name: Option<&'static str>, handler: DynHandler<Context, Inherited>) { fn set_root(&mut self, handler: DynHandler<Context, InheritedParams>) {
self.0.insert(Name(name), handler); self.0 = Some(handler);
} }
fn get<'a>(&'a self, name: Option<&str>) -> Option<(Name, &'a DynHandler<Context, Inherited>)> { fn get_root<'a>(&'a self) -> Option<&'a DynHandler<Context, InheritedParams>> {
if let Some((name, handler)) = self.0.get_key_value(&name) { self.0.as_ref()
}
fn insert(
&mut self,
name: &'static str,
handler: DynHandler<Context, Flat<Params, InheritedParams>>,
) {
self.1.insert(Name(name), handler);
}
fn get<'a>(
&'a self,
name: &str,
) -> Option<(Name, &'a DynHandler<Context, Flat<Params, InheritedParams>>)> {
if let Some((name, handler)) = self.1.get_key_value(&name) {
Some((*name, handler)) Some((*name, handler))
} else { } else {
None None
@@ -49,14 +69,14 @@ impl<Context, Inherited> SubcommandMap<Context, Inherited> {
pub struct ParentHandler<Context, Params = Empty, InheritedParams = Empty> { pub struct ParentHandler<Context, Params = Empty, InheritedParams = Empty> {
_phantom: PhantomData<Context>, _phantom: PhantomData<Context>,
pub(crate) subcommands: SubcommandMap<Context, Flat<Params, InheritedParams>>, pub(crate) subcommands: SubcommandMap<Context, Params, InheritedParams>,
metadata: OrdMap<&'static str, Value>, metadata: OrdMap<&'static str, Value>,
} }
impl<Context, Params, InheritedParams> ParentHandler<Context, Params, InheritedParams> { impl<Context, Params, InheritedParams> ParentHandler<Context, Params, InheritedParams> {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
_phantom: PhantomData::new(), _phantom: PhantomData::new(),
subcommands: SubcommandMap(OrdMap::new()), subcommands: SubcommandMap(None, OrdMap::new()),
metadata: OrdMap::new(), metadata: OrdMap::new(),
} }
} }
@@ -98,12 +118,11 @@ impl<Context: crate::Context, Params, InheritedParams>
} }
pub fn root_handler<C: crate::Context, H>(mut self, handler: H) -> Self pub fn root_handler<C: crate::Context, H>(mut self, handler: H) -> Self
where where
WithContext<C, H>: Handler<Flat<Params, InheritedParams>>, WithContext<C, H>: Handler<InheritedParams>,
<WithContext<C, H> as Handler<Flat<Params, InheritedParams>>>::H: <WithContext<C, H> as Handler<InheritedParams>>::H: HandlerTypes<Params = Params>,
HandlerTypes<Params = Empty>,
{ {
if let Some(h) = DynHandler::new(handler) { if let Some(h) = DynHandler::new(handler) {
self.subcommands.insert(None, h); self.subcommands.set_root(h);
} }
self self
} }
@@ -142,17 +161,29 @@ where
let cmd = method.pop_front(); let cmd = method.pop_front();
if let Some(cmd) = cmd { if let Some(cmd) = cmd {
parent_method.push_back(cmd); parent_method.push_back(cmd);
} if let Some((_, sub_handler)) = &self.subcommands.get(cmd) {
if let Some((_, sub_handler)) = &self.subcommands.get(cmd) { sub_handler.handle_sync(HandleAnyArgs {
sub_handler.handle_sync(HandleAnyArgs { context,
context, parent_method,
parent_method, method,
method, params: raw_params,
params: raw_params, inherited: Flat(params, inherited_params),
inherited: Flat(params, inherited_params), })
}) } else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
} else { } else {
Err(yajrc::METHOD_NOT_FOUND_ERROR) if let Some(sub_handler) = &self.subcommands.get_root() {
sub_handler.handle_sync(HandleAnyArgs {
context,
parent_method,
method,
params: raw_params,
inherited: inherited_params,
})
} else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
} }
} }
async fn handle_async( async fn handle_async(
@@ -169,27 +200,49 @@ where
let cmd = method.pop_front(); let cmd = method.pop_front();
if let Some(cmd) = cmd { if let Some(cmd) = cmd {
parent_method.push_back(cmd); parent_method.push_back(cmd);
} if let Some((_, sub_handler)) = &self.subcommands.get(cmd) {
if let Some((_, sub_handler)) = self.subcommands.get(cmd) { sub_handler
sub_handler .handle_async(HandleAnyArgs {
.handle_async(HandleAnyArgs { context,
context, parent_method,
parent_method, method,
method, params: raw_params,
params: raw_params, inherited: Flat(params, inherited_params),
inherited: Flat(params, inherited_params), })
}) .await
.await } else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
} else { } else {
Err(yajrc::METHOD_NOT_FOUND_ERROR) if let Some(sub_handler) = &self.subcommands.get_root() {
sub_handler
.handle_async(HandleAnyArgs {
context,
parent_method,
method,
params: raw_params,
inherited: inherited_params,
})
.await
} else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
} }
} }
fn metadata(&self, mut method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> { fn metadata(&self, mut method: VecDeque<&'static str>) -> OrdMap<&'static str, Value> {
let metadata = self.metadata.clone(); let metadata = self.metadata.clone();
if let Some((_, handler)) = self.subcommands.get(method.pop_front()) { if let Some(cmd) = method.pop_front() {
handler.metadata(method).union(metadata) if let Some((_, handler)) = self.subcommands.get(cmd) {
handler.metadata(method).union(metadata)
} else {
metadata
}
} else { } else {
metadata if let Some(handler) = self.subcommands.get_root() {
handler.metadata(method).union(metadata)
} else {
metadata
}
} }
} }
fn method_from_dots(&self, method: &str) -> Option<VecDeque<&'static str>> { fn method_from_dots(&self, method: &str) -> Option<VecDeque<&'static str>> {
@@ -201,15 +254,22 @@ where
.map(|(head, tail)| (Some(head), Some(tail))) .map(|(head, tail)| (Some(head), Some(tail)))
.unwrap_or((Some(method), None)) .unwrap_or((Some(method), None))
}; };
let (Name(name), h) = self.subcommands.get(head)?; if let Some(head) = head {
let mut res = VecDeque::new(); let (Name(name), h) = self.subcommands.get(head)?;
if let Some(name) = name { let mut res = VecDeque::new();
res.push_back(name); res.push_back(name);
if let Some(tail) = tail {
res.append(&mut h.method_from_dots(tail)?);
}
Some(res)
} else {
let h = self.subcommands.get_root()?;
let mut res = VecDeque::new();
if let Some(tail) = tail {
res.append(&mut h.method_from_dots(tail)?);
}
Some(res)
} }
if let Some(tail) = tail {
res.append(&mut h.method_from_dots(tail)?);
}
Some(res)
} }
} }
@@ -221,15 +281,16 @@ where
InheritedParams: Serialize + Send + Sync + 'static, InheritedParams: Serialize + Send + Sync + 'static,
{ {
fn cli_command(&self) -> Command { fn cli_command(&self) -> Command {
let mut base = Params::command().subcommand_required(true); let mut base = if let Some(cli) = &self.subcommands.0.as_ref().and_then(|h| h.cli()) {
for (name, handler) in &self.subcommands.0 { cli.cli_command().subcommand_required(false)
} else {
Params::command().subcommand_required(true)
};
for (name, handler) in &self.subcommands.1 {
match (name, handler.cli()) { match (name, handler.cli()) {
(Name(Some(name)), Some(cli)) => { (Name(name), Some(cli)) => {
base = base.subcommand(cli.cli_command().name(name)); base = base.subcommand(cli.cli_command().name(name));
} }
(Name(None), Some(_)) => {
base = base.subcommand_required(false);
}
_ => (), _ => (),
} }
} }
@@ -239,27 +300,40 @@ where
&self, &self,
matches: &ArgMatches, matches: &ArgMatches,
) -> Result<(VecDeque<&'static str>, Value), clap::Error> { ) -> Result<(VecDeque<&'static str>, Value), clap::Error> {
let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?)
.map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?;
let (name, matches) = match matches.subcommand() { let (name, matches) = match matches.subcommand() {
Some((name, matches)) => (Some(name), matches), Some((name, matches)) => (Some(name), matches),
None => (None, matches), None => (None, matches),
}; };
if let Some((Name(Some(name)), cli)) = self if let Some(name) = name {
.subcommands let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?)
.get(name) .map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?;
.and_then(|(n, h)| h.cli().map(|c| (n, c))) if let Some((Name(name), cli)) = self
{ .subcommands
let (mut method, params) = cli.cli_parse(matches)?; .get(name)
method.push_front(name); .and_then(|(n, h)| h.cli().map(|c| (n, c)))
{
let (mut method, params) = cli.cli_parse(matches)?;
method.push_front(name);
Ok(( Ok((
method, method,
combine(root_params, params) combine(root_params, params).map_err(|e| {
.map_err(|e| clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e))?, clap::Error::raw(clap::error::ErrorKind::ArgumentConflict, e)
)) })?,
))
} else {
Ok((VecDeque::new(), root_params))
}
} else { } else {
Ok((VecDeque::new(), root_params)) if let Some(cli) = self.subcommands.get_root().and_then(|h| h.cli()) {
let (method, params) = cli.cli_parse(matches)?;
Ok((method, params))
} else {
let root_params = imbl_value::to_value(&Params::from_arg_matches(matches)?)
.map_err(|e| clap::Error::raw(clap::error::ErrorKind::ValueValidation, e))?;
Ok((VecDeque::new(), root_params))
}
} }
} }
fn cli_display( fn cli_display(
@@ -277,24 +351,39 @@ where
let cmd = method.pop_front(); let cmd = method.pop_front();
if let Some(cmd) = cmd { if let Some(cmd) = cmd {
parent_method.push_back(cmd); parent_method.push_back(cmd);
} if let Some((_, cli)) = self
if let Some((_, cli)) = self .subcommands
.subcommands .get(cmd)
.get(cmd) .and_then(|(n, h)| h.cli().map(|c| (n, c)))
.and_then(|(n, h)| h.cli().map(|c| (n, c))) {
{ cli.cli_display(
cli.cli_display( HandleAnyArgs {
HandleAnyArgs { context,
context, parent_method,
parent_method, method,
method, params: raw_params,
params: raw_params, inherited: Flat(params, inherited_params),
inherited: Flat(params, inherited_params), },
}, result,
result, )
) } else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
} else { } else {
Err(yajrc::METHOD_NOT_FOUND_ERROR) if let Some(cli) = self.subcommands.get_root().and_then(|h| h.cli()) {
cli.cli_display(
HandleAnyArgs {
context,
parent_method,
method,
params: raw_params,
inherited: inherited_params,
},
result,
)
} else {
Err(yajrc::METHOD_NOT_FOUND_ERROR)
}
} }
} }
} }