fix trailing body passed to commands

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-07-28 00:24:09 +00:00 committed by Jason Volk
parent ddc8c3b46c
commit 49033db2b8

View file

@ -1,7 +1,7 @@
use std::{panic::AssertUnwindSafe, sync::Arc, time::Instant};
use clap::{CommandFactory, Parser};
use conduit::{error, trace, utils::string::common_prefix, Error, Result};
use conduit::{checked, error, trace, utils::string::common_prefix, Error, Result};
use futures_util::future::FutureExt;
use ruma::{
events::{
@ -60,8 +60,11 @@ fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -
// Parse and process a message from the admin room
async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
let command = lines.next().expect("each string has at least one line");
let lines = msg.lines().filter(|l| !l.trim().is_empty());
let command = lines
.clone()
.next()
.expect("each string has at least one line");
let (parsed, body) = match parse_command(command) {
Ok(parsed) => parsed,
Err(error) => {
@ -71,12 +74,12 @@ async fn process(services: Arc<Services>, msg: &str) -> CommandOutput {
},
};
let timer = Instant::now();
let body: Vec<&str> = body.iter().map(String::as_str).collect();
let body = parse_body(AdminCommand::command(), &body, lines.skip(1).collect()).expect("trailing body parsed");
let context = Command {
services: &services,
body: &body,
};
let timer = Instant::now();
let result = Box::pin(admin::process(parsed, &context)).await;
let elapsed = timer.elapsed();
conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}");
@ -95,6 +98,32 @@ fn parse_command(command_line: &str) -> Result<(AdminCommand, Vec<String>), Stri
Ok((com, argv))
}
fn parse_body<'a>(mut cmd: clap::Command, body: &'a [String], lines: Vec<&'a str>) -> Result<Vec<&'a str>> {
let mut start = 1;
'token: for token in body.iter().skip(1) {
let cmd_ = cmd.clone();
for sub in cmd_.get_subcommands() {
if sub.get_name() == *token {
start = checked!(start + 1)?;
cmd = sub.clone();
continue 'token;
}
}
// positional arguments have to be skipped too
let num_posargs = cmd_.get_positionals().count();
start = checked!(start + num_posargs)?;
break;
}
Ok(body
.iter()
.skip(start)
.map(String::as_str)
.chain(lines)
.collect::<Vec<&'a str>>())
}
fn complete_command(mut cmd: clap::Command, line: &str) -> String {
let argv = parse_line(line);
let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1));