split admin command enum from handler

Signed-off-by: Jason Volk <jason@zemos.net>
This commit is contained in:
Jason Volk 2024-07-22 22:24:17 +00:00
parent 263e338088
commit ccfa939bd3
6 changed files with 89 additions and 84 deletions

66
src/admin/admin.rs Normal file
View file

@ -0,0 +1,66 @@
use clap::Parser;
use conduit::Result;
use ruma::events::room::message::RoomMessageEventContent;
use crate::{
appservice, appservice::AppserviceCommand, check, check::CheckCommand, debug, debug::DebugCommand, federation,
federation::FederationCommand, media, media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand,
server, server::ServerCommand, user, user::UserCommand,
};
#[derive(Parser)]
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
pub(crate) enum AdminCommand {
#[command(subcommand)]
/// - Commands for managing appservices
Appservices(AppserviceCommand),
#[command(subcommand)]
/// - Commands for managing local users
Users(UserCommand),
#[command(subcommand)]
/// - Commands for managing rooms
Rooms(RoomCommand),
#[command(subcommand)]
/// - Commands for managing federation
Federation(FederationCommand),
#[command(subcommand)]
/// - Commands for managing the server
Server(ServerCommand),
#[command(subcommand)]
/// - Commands for managing media
Media(MediaCommand),
#[command(subcommand)]
/// - Commands for checking integrity
Check(CheckCommand),
#[command(subcommand)]
/// - Commands for debugging things
Debug(DebugCommand),
#[command(subcommand)]
/// - Low-level queries for database getters and iterators
Query(QueryCommand),
}
#[tracing::instrument(skip_all, name = "command")]
pub(crate) async fn process(command: AdminCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
let reply_message_content = match command {
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
AdminCommand::Media(command) => media::process(command, body).await?,
AdminCommand::Users(command) => user::process(command, body).await?,
AdminCommand::Rooms(command) => room::process(command, body).await?,
AdminCommand::Federation(command) => federation::process(command, body).await?,
AdminCommand::Server(command) => server::process(command, body).await?,
AdminCommand::Debug(command) => debug::process(command, body).await?,
AdminCommand::Query(command) => query::process(command, body).await?,
AdminCommand::Check(command) => check::process(command, body).await?,
};
Ok(reply_message_content)
}

View file

@ -11,64 +11,16 @@ use ruma::{
OwnedEventId, OwnedEventId,
}; };
extern crate conduit_service as service;
use conduit::{utils::string::common_prefix, Result}; use conduit::{utils::string::common_prefix, Result};
pub(crate) use service::admin::Command; use service::admin::{Command, CommandOutput, CommandResult, HandlerResult};
use service::admin::{CommandOutput, CommandResult, HandlerResult};
use crate::{ use crate::{admin, admin::AdminCommand, services};
appservice, appservice::AppserviceCommand, check, check::CheckCommand, debug, debug::DebugCommand, federation,
federation::FederationCommand, media, media::MediaCommand, query, query::QueryCommand, room, room::RoomCommand,
server, server::ServerCommand, services, user, user::UserCommand,
};
pub(crate) const PAGE_SIZE: usize = 100;
#[derive(Parser)]
#[command(name = "admin", version = env!("CARGO_PKG_VERSION"))]
pub(crate) enum AdminCommand {
#[command(subcommand)]
/// - Commands for managing appservices
Appservices(AppserviceCommand),
#[command(subcommand)]
/// - Commands for managing local users
Users(UserCommand),
#[command(subcommand)]
/// - Commands for managing rooms
Rooms(RoomCommand),
#[command(subcommand)]
/// - Commands for managing federation
Federation(FederationCommand),
#[command(subcommand)]
/// - Commands for managing the server
Server(ServerCommand),
#[command(subcommand)]
/// - Commands for managing media
Media(MediaCommand),
#[command(subcommand)]
/// - Commands for checking integrity
Check(CheckCommand),
#[command(subcommand)]
/// - Commands for debugging things
Debug(DebugCommand),
#[command(subcommand)]
/// - Low-level queries for database getters and iterators
Query(QueryCommand),
}
#[must_use] #[must_use]
pub(crate) fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) } pub(super) fn handle(command: Command) -> HandlerResult { Box::pin(handle_command(command)) }
#[must_use] #[must_use]
pub(crate) fn complete(line: &str) -> String { complete_admin_command(AdminCommand::command(), line) } pub(super) fn complete(line: &str) -> String { complete_command(AdminCommand::command(), line) }
#[tracing::instrument(skip_all, name = "admin")] #[tracing::instrument(skip_all, name = "admin")]
async fn handle_command(command: Command) -> CommandResult { async fn handle_command(command: Command) -> CommandResult {
@ -80,7 +32,7 @@ async fn handle_command(command: Command) -> CommandResult {
} }
async fn process_command(command: &Command) -> CommandOutput { async fn process_command(command: &Command) -> CommandOutput {
process_admin_message(&command.command) process(&command.command)
.await .await
.and_then(|content| reply(content, command.reply_id.clone())) .and_then(|content| reply(content, command.reply_id.clone()))
} }
@ -104,11 +56,11 @@ fn reply(mut content: RoomMessageEventContent, reply_id: Option<OwnedEventId>) -
} }
// Parse and process a message from the admin room // Parse and process a message from the admin room
async fn process_admin_message(msg: &str) -> CommandOutput { async fn process(msg: &str) -> CommandOutput {
let mut lines = msg.lines().filter(|l| !l.trim().is_empty()); let mut lines = msg.lines().filter(|l| !l.trim().is_empty());
let command = lines.next().expect("each string has at least one line"); let command = lines.next().expect("each string has at least one line");
let body = lines.collect::<Vec<_>>(); let body = lines.collect::<Vec<_>>();
let parsed = match parse_admin_command(command) { let parsed = match parse_command(command) {
Ok(parsed) => parsed, Ok(parsed) => parsed,
Err(error) => { Err(error) => {
let server_name = services().globals.server_name(); let server_name = services().globals.server_name();
@ -118,7 +70,7 @@ async fn process_admin_message(msg: &str) -> CommandOutput {
}; };
let timer = Instant::now(); let timer = Instant::now();
let result = process_admin_command(parsed, body).await; let result = admin::process(parsed, body).await;
let elapsed = timer.elapsed(); let elapsed = timer.elapsed();
conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}"); conduit::debug!(?command, ok = result.is_ok(), "command processed in {elapsed:?}");
match result { match result {
@ -129,31 +81,14 @@ async fn process_admin_message(msg: &str) -> CommandOutput {
} }
} }
#[tracing::instrument(skip_all, name = "command")]
async fn process_admin_command(command: AdminCommand, body: Vec<&str>) -> Result<RoomMessageEventContent> {
let reply_message_content = match command {
AdminCommand::Appservices(command) => appservice::process(command, body).await?,
AdminCommand::Media(command) => media::process(command, body).await?,
AdminCommand::Users(command) => user::process(command, body).await?,
AdminCommand::Rooms(command) => room::process(command, body).await?,
AdminCommand::Federation(command) => federation::process(command, body).await?,
AdminCommand::Server(command) => server::process(command, body).await?,
AdminCommand::Debug(command) => debug::process(command, body).await?,
AdminCommand::Query(command) => query::process(command, body).await?,
AdminCommand::Check(command) => check::process(command, body).await?,
};
Ok(reply_message_content)
}
// Parse chat messages from the admin room into an AdminCommand object // Parse chat messages from the admin room into an AdminCommand object
fn parse_admin_command(command_line: &str) -> Result<AdminCommand, String> { fn parse_command(command_line: &str) -> Result<AdminCommand, String> {
let argv = parse_command_line(command_line); let argv = parse_line(command_line);
AdminCommand::try_parse_from(argv).map_err(|error| error.to_string()) AdminCommand::try_parse_from(argv).map_err(|error| error.to_string())
} }
fn complete_admin_command(mut cmd: clap::Command, line: &str) -> String { fn complete_command(mut cmd: clap::Command, line: &str) -> String {
let argv = parse_command_line(line); let argv = parse_line(line);
let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1)); let mut ret = Vec::<String>::with_capacity(argv.len().saturating_add(1));
'token: for token in argv.into_iter().skip(1) { 'token: for token in argv.into_iter().skip(1) {
@ -196,7 +131,7 @@ fn complete_admin_command(mut cmd: clap::Command, line: &str) -> String {
} }
// Parse chat messages from the admin room into an AdminCommand object // Parse chat messages from the admin room into an AdminCommand object
fn parse_command_line(command_line: &str) -> Vec<String> { fn parse_line(command_line: &str) -> Vec<String> {
let mut argv = command_line let mut argv = command_line
.split_whitespace() .split_whitespace()
.map(str::to_owned) .map(str::to_owned)

View file

@ -1,18 +1,20 @@
#![recursion_limit = "168"] #![recursion_limit = "168"]
#![allow(clippy::wildcard_imports)] #![allow(clippy::wildcard_imports)]
pub(crate) mod admin;
pub(crate) mod handler;
mod tests;
pub(crate) mod utils;
pub(crate) mod appservice; pub(crate) mod appservice;
pub(crate) mod check; pub(crate) mod check;
pub(crate) mod debug; pub(crate) mod debug;
pub(crate) mod federation; pub(crate) mod federation;
pub(crate) mod handler;
pub(crate) mod media; pub(crate) mod media;
pub(crate) mod query; pub(crate) mod query;
pub(crate) mod room; pub(crate) mod room;
pub(crate) mod server; pub(crate) mod server;
mod tests;
pub(crate) mod user; pub(crate) mod user;
pub(crate) mod utils;
extern crate conduit_api as api; extern crate conduit_api as api;
extern crate conduit_core as conduit; extern crate conduit_core as conduit;
@ -23,6 +25,8 @@ pub(crate) use service::services;
pub(crate) use crate::utils::{escape_html, get_room_info}; pub(crate) use crate::utils::{escape_html, get_room_info};
pub(crate) const PAGE_SIZE: usize = 100;
mod_ctor! {} mod_ctor! {}
mod_dtor! {} mod_dtor! {}

View file

@ -2,7 +2,7 @@ use std::fmt::Write;
use ruma::events::room::message::RoomMessageEventContent; use ruma::events::room::message::RoomMessageEventContent;
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result}; use crate::{escape_html, get_room_info, services, Result, PAGE_SIZE};
pub(super) async fn list( pub(super) async fn list(
_body: Vec<&str>, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool, _body: Vec<&str>, page: Option<usize>, exclude_disabled: bool, exclude_banned: bool,

View file

@ -3,7 +3,7 @@ use std::fmt::Write;
use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId}; use ruma::{events::room::message::RoomMessageEventContent, OwnedRoomId};
use super::RoomDirectoryCommand; use super::RoomDirectoryCommand;
use crate::{escape_html, get_room_info, handler::PAGE_SIZE, services, Result}; use crate::{escape_html, get_room_info, services, Result, PAGE_SIZE};
pub(super) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> { pub(super) async fn process(command: RoomDirectoryCommand, _body: Vec<&str>) -> Result<RoomMessageEventContent> {
match command { match command {

View file

@ -12,7 +12,7 @@ fn get_help_subcommand() { get_help_inner("help"); }
fn get_help_inner(input: &str) { fn get_help_inner(input: &str) {
use clap::Parser; use clap::Parser;
use crate::handler::AdminCommand; use crate::admin::AdminCommand;
let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else { let Err(error) = AdminCommand::try_parse_from(["argv[0] doesn't matter", input]) else {
panic!("no error!"); panic!("no error!");