1use super::BotCmdHandler;
2use super::Error;
3use super::Module;
4use super::MsgMetadata;
5use super::Reaction;
6use super::Result;
7use super::State;
8use std;
9use std::borrow::Cow;
10use std::sync::Arc;
11use util;
12use yaml_rust::Yaml;
13
14pub struct BotCommand {
15 pub name: Cow<'static, str>,
16 pub provider: Arc<Module>,
17 pub auth_lvl: BotCmdAuthLvl,
18 pub(super) handler: Arc<BotCmdHandler>,
19 pub usage_str: Cow<'static, str>,
20 pub(super) usage_yaml: Yaml,
21 pub help_msg: Cow<'static, str>,
22}
23
24#[derive(Debug)]
25pub enum BotCmdAttr {}
26
27#[derive(Debug)]
28pub enum BotCmdResult {
29 Ok(Reaction),
31
32 Unauthorized,
35
36 SyntaxErr,
39
40 ArgMissing(Cow<'static, str>),
44
45 ArgMissing1To1(Cow<'static, str>),
51
52 LibErr(Error),
54
55 UserErrMsg(Cow<'static, str>),
58
59 BotErrMsg(Cow<'static, str>),
63}
64
65impl From<Reaction> for BotCmdResult {
66 fn from(r: Reaction) -> Self {
67 BotCmdResult::Ok(r)
68 }
69}
70
71#[derive(Clone, Debug, PartialEq, Eq)]
72pub enum BotCmdAuthLvl {
73 Public,
74 Admin,
75}
76
77pub(super) fn run(
78 state: &State,
79 cmd_name: &str,
80 cmd_args: &str,
81 metadata: &MsgMetadata,
82) -> Result<Option<BotCmdResult>> {
83 let &BotCommand {
84 ref name,
85 ref provider,
86 ref auth_lvl,
87 ref handler,
88 ref usage_yaml,
89 usage_str: _,
90 help_msg: _,
91 } = match state.commands.get(cmd_name) {
92 Some(c) => c,
93 None => return Ok(None),
94 };
95
96 let user_authorized = match auth_lvl {
97 &BotCmdAuthLvl::Public => Ok(true),
98 &BotCmdAuthLvl::Admin => state.have_admin(metadata.prefix),
99 };
100
101 let arg = match parse_arg(usage_yaml, cmd_args) {
102 Ok(arg) => arg,
103 Err(res) => return Ok(Some(res)),
104 };
105
106 let result = match user_authorized {
107 Ok(true) => {
108 debug!("Running bot command {:?} with arg: {:?}", name, arg);
109 match util::run_handler("command", name.clone(), || {
110 handler.run(state, &metadata, &arg)
111 }) {
112 Ok(r) => r,
113 Err(e) => BotCmdResult::LibErr(e),
114 }
115 }
116 Ok(false) => BotCmdResult::Unauthorized,
117 Err(e) => BotCmdResult::LibErr(e),
118 };
119
120 match result {
122 BotCmdResult::Ok(Reaction::Quit(ref s)) if *auth_lvl != BotCmdAuthLvl::Admin => {
123 Ok(Some(BotCmdResult::BotErrMsg(
124 format!(
125 "Only commands at authorization level {auth_lvl_owner:?} \
126 may tell the bot to quit, but the command {cmd_name:?} \
127 from module {provider_name:?}, at authorization level \
128 {cmd_auth_lvl:?}, has told the bot to quit with quit \
129 message {quit_msg:?}.",
130 auth_lvl_owner = BotCmdAuthLvl::Admin,
131 cmd_name = name,
132 provider_name = provider.name,
133 cmd_auth_lvl = auth_lvl,
134 quit_msg = s
135 ).into(),
136 )))
137 }
138 r => Ok(Some(r)),
139 }
140}
141
142fn parse_arg<'s>(syntax: &'s Yaml, arg_str: &str) -> std::result::Result<Yaml, BotCmdResult> {
143 use util::yaml as uy;
144
145 match uy::parse_and_check_node(arg_str, syntax, "<argument>", || {
146 Yaml::Hash(Default::default())
147 }) {
148 Ok(arg) => Ok(arg),
149 Err(uy::Error(uy::ErrorKind::YamlScan(_), _)) => Err(BotCmdResult::SyntaxErr),
150 Err(err) => Err(BotCmdResult::LibErr(err.into())),
151 }
152}