command_handler/
command_handler.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2024 Taylan Gökkaya
3
4use std::io::{
5	self,
6	BufRead,
7	Write,
8};
9
10use malachi::{
11	Args,
12	Command,
13};
14
15type Callback = Box<dyn for<'a> Fn(Args<'a, 'a>)>;
16
17struct Handler {
18	cmd: Command,
19	callback: Callback,
20}
21
22struct Handlers(Vec<Handler>);
23
24impl Handlers {
25	fn execute(&self, msg: &str) {
26		// Try matching every command sequentially.
27		for cmd in &self.0 {
28			// If this command matches, execute it and return.
29			if let Some(args) = cmd.cmd.get_matches(msg) {
30				(cmd.callback)(args);
31				return;
32			}
33		}
34
35		println!("No command matched the input.");
36	}
37
38	fn add<F>(&mut self, cmd: &str, callback: F) -> Result<&mut Self, malachi::Error>
39	where
40		F: for<'a> Fn(Args<'a, 'a>) + 'static,
41	{
42		println!("adding new command: {}", cmd);
43		self.0.push(Handler {
44			cmd: Command::new(cmd)?,
45			callback: Box::new(callback),
46		});
47
48		Ok(self)
49	}
50}
51
52fn cmd_join(args: Args) {
53	// Separator is optional.
54	let sep = args.get_once("separator").unwrap_or("-");
55	// Tokens are not optional so unwrapping is fine.
56	let tokens = args.get_many("words").unwrap();
57
58	println!("{}", tokens.join(sep));
59}
60
61fn cmd_sort(args: Args) {
62	let descending = args.is_present("descending");
63	// We defined `words` with the `+` quantifier. We'll always get `Match::Many`.
64	let mut words = args.get_many("words").unwrap().clone();
65
66	words.sort_by(|a, b| if descending { b.cmp(a) } else { a.cmp(b) });
67
68	println!("sorted:");
69	for s in words {
70		println!("{}", s);
71	}
72}
73
74fn main() -> Result<(), malachi::Error> {
75	let mut cmds = Handlers(vec![]);
76	cmds.add(
77		".sort [
78	<descending?: nocase(), `-desc`, `-descending`, `-reverse`>
79	<words+>
80]",
81		cmd_sort,
82	)?
83	.add(
84		".join [
85	<separator?: nocase(), starts(`sep=`, `separator=`)>
86	<words+>
87]",
88		cmd_join,
89	)?;
90
91	println!(
92		"Available commands:
93.sort [-desc] <words>
94	Sorts the given input lexicographically.
95	examples:
96	.sort foo banana wow
97	.sort -desc house tree hi sup
98
99.join [sep=?] <words>
100	Join the given words with the separator given. The default separator is `-`.
101	examples:
102	.join sep=_ snake case
103	.join kebab case
104	.join sep=:: std io stdin"
105	);
106
107	let stdin = io::stdin();
108	let stdin = stdin.lock();
109	print!("> ");
110	io::stdout().flush().unwrap();
111
112	for msg in stdin.lines().filter_map(Result::ok) {
113		cmds.execute(&msg);
114		print!("> ");
115		io::stdout().flush().unwrap();
116	}
117
118	Ok(())
119}