Skip to main content

yog_command/
lib.rs

1//! Command domain — the context passed to a command handler.
2//!
3//! A mod registers a command by name on the registry; when a player runs
4//! `/<name> <args>`, its handler receives a [`CommandContext`] and may return a
5//! reply string sent back to the source.
6
7mod fit;
8
9pub use fit::Fit;
10
11/// Details of a command invocation.
12#[derive(Debug, Clone)]
13pub struct CommandContext {
14    /// Command name that was run, without the leading slash.
15    pub name: String,
16    /// Its arguments: a typed command's values in order, a plain one's words.
17    pub args: Vec<String>,
18    /// Name of the player (or console) that ran the command.
19    pub source: String,
20    /// UUID of the executing entity (empty if run from the console).
21    pub uuid: String,
22    /// Dimension the source is in, empty for console-run commands (no world).
23    pub dimension: String,
24    /// The source's permission level, as the game has it (0 anyone … 4
25    /// the server's owners; the console all of them).
26    pub permission: i32,
27    /// Whether the source is an operator: it may run the game's operators'
28    /// commands (the game's own measure).
29    pub op: bool,
30}
31
32impl CommandContext {
33    /// Argument `idx`, or `None` if out of range.
34    pub fn arg_str(&self, idx: usize) -> Option<&str> {
35        self.args.get(idx).map(String::as_str)
36    }
37
38    /// Typed argument `idx` parsed as `i32`, or `None`.
39    pub fn arg_int(&self, idx: usize) -> Option<i32> {
40        self.arg_str(idx)?.parse().ok()
41    }
42
43    /// Typed argument `idx` parsed as `f32`, or `None`.
44    pub fn arg_float(&self, idx: usize) -> Option<f32> {
45        self.arg_str(idx)?.parse().ok()
46    }
47
48    /// Typed argument `idx` as a player name, or `None`.
49    pub fn arg_player(&self, idx: usize) -> Option<&str> {
50        self.arg_str(idx)
51    }
52
53    /// Typed argument `idx` as a block position (`"x,y,z"`), or `None`.
54    pub fn arg_blockpos(&self, idx: usize) -> Option<(i32, i32, i32)> {
55        let s = self.arg_str(idx)?;
56        let mut it = s.split(yog_ids::sep::LIST);
57        let x = it.next()?.parse().ok()?;
58        let y = it.next()?.parse().ok()?;
59        let z = it.next()?.parse().ok()?;
60        Some((x, y, z))
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    fn ctx(args: &[&str]) -> CommandContext {
69        CommandContext {
70            name: "hex".into(),
71            args: args.iter().map(|a| (*a).to_owned()).collect(),
72            source: "Dev".into(),
73            uuid: String::new(),
74            dimension: String::new(),
75            permission: 0,
76            op: false,
77        }
78    }
79
80    #[test]
81    fn typed_arguments() {
82        let t = ctx(&["5", "2.5", "Steve", "1,-2,3"]);
83        assert_eq!(t.arg_int(0), Some(5));
84        assert_eq!(t.arg_float(1), Some(2.5));
85        assert_eq!(t.arg_player(2), Some("Steve"));
86        assert_eq!(t.arg_blockpos(3), Some((1, -2, 3)));
87        assert_eq!(t.arg_int(2), None, "not a number");
88        assert_eq!(t.arg_str(9), None);
89        assert_eq!(ctx(&[]).arg_str(0), None);
90        assert_eq!(ctx(&["1,2"]).arg_blockpos(0), None);
91    }
92}