#![warn(missing_docs, clippy::all, clippy::pedantic)]
#![deny(rustdoc::broken_intra_doc_links, missing_debug_implementations)]
#![allow(clippy::module_name_repetitions)]
#![feature(box_into_inner)]
use std::{
convert::Infallible,
fmt,
fmt::{Display, Formatter},
str::FromStr,
};
use thiserror::Error;
mod fold_error;
mod tokenize;
pub use fold_error::{Downcast, FoldError};
pub use tokenize::tokenize_str_simple;
#[derive(Error, Debug)]
pub enum IdParseError {
#[error("no ID match for {0:?}")]
NoMatch(String, &'static [&'static str]),
#[error("ambiguous ID {0:?}, could be any of {}", .0.join(", "))]
Ambiguous(&'static [&'static str], String),
}
#[derive(Clone, Copy, Debug)]
pub struct ArgumentName {
pub cmd: &'static str,
pub arg: &'static str,
}
impl Display for ArgumentName {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
f.write_fmt(format_args!("{:?} of {:?}", self.arg, self.cmd))
}
}
#[derive(Error, Debug)]
pub enum CommandParseError {
#[error("no values in command parse input")]
NoInput,
#[error("failed to parse command ID")]
BadId(#[from] IdParseError),
#[error("missing required argument {0}")]
MissingRequired(ArgumentName),
#[error("failed to convert argument {0} from a string")]
BadConvert(ArgumentName, anyhow::Error),
#[error("trailing argument {1:?} of {0:?}")]
Trailing(&'static str, String),
#[error("failed to parse subcommand {0:?}")]
Subcommand(&'static str, Box<CommandParseError>),
}
impl From<Infallible> for CommandParseError {
fn from(_: Infallible) -> CommandParseError { unreachable!() }
}
pub use anyhow::Error as Anyhow;
pub use docbot_derive::*;
pub trait Command: Sized {
type Id;
fn parse<I: IntoIterator<Item = S>, S: AsRef<str>>(iter: I) -> Result<Self, CommandParseError>;
fn id(&self) -> Self::Id;
}
pub trait CommandId: FromStr + Display {
fn names() -> &'static [&'static str];
fn to_str(&self) -> &'static str;
}
#[derive(Debug, Clone)]
pub struct ArgumentUsage {
pub name: &'static str,
pub is_required: bool,
pub is_rest: bool,
}
#[derive(Debug, Clone)]
pub struct CommandUsage {
pub ids: &'static [&'static str],
pub args: &'static [ArgumentUsage],
pub desc: &'static str,
}
#[derive(Debug, Clone)]
pub struct ArgumentDesc {
pub name: &'static str,
pub is_required: bool,
pub desc: &'static str,
}
#[derive(Debug, Clone)]
pub struct CommandDesc {
pub summary: Option<&'static str>,
pub args: &'static [ArgumentDesc],
pub examples: Option<&'static str>,
}
#[derive(Debug, Clone)]
pub enum HelpTopic {
Command(CommandUsage, CommandDesc),
CommandSet(Option<&'static str>, &'static [CommandUsage]),
Custom(&'static str),
}
pub trait Help: Command {
fn help(topic: Option<Self::Id>) -> &'static HelpTopic;
}
pub mod prelude {
pub use super::{Command, CommandId, Docbot, FoldError, Help};
}