1use crate::auth::AccessLevel;
7
8pub mod completion;
10pub mod path;
11
12#[derive(Debug, Copy, Clone, PartialEq, Eq)]
14pub enum CommandKind {
15 Sync,
17
18 #[cfg(feature = "async")]
20 Async,
21}
22
23#[derive(Debug, Clone)]
27pub struct CommandMeta<L: AccessLevel> {
28 pub id: &'static str,
31
32 pub name: &'static str,
34
35 pub description: &'static str,
37
38 pub access_level: L,
40
41 pub kind: CommandKind,
43
44 pub min_args: usize,
46
47 pub max_args: usize,
49}
50
51#[derive(Debug, Clone)]
54pub struct Directory<L: AccessLevel> {
55 pub name: &'static str,
57
58 pub children: &'static [Node<L>],
60
61 pub access_level: L,
63}
64
65#[derive(Debug, Clone)]
69pub enum Node<L: AccessLevel> {
70 Command(&'static CommandMeta<L>),
72
73 Directory(&'static Directory<L>),
75}
76
77impl<L: AccessLevel> Node<L> {
78 pub fn is_command(&self) -> bool {
80 matches!(self, Node::Command(_))
81 }
82
83 pub fn is_directory(&self) -> bool {
85 matches!(self, Node::Directory(_))
86 }
87
88 pub fn name(&self) -> &'static str {
90 match self {
91 Node::Command(cmd) => cmd.name,
92 Node::Directory(dir) => dir.name,
93 }
94 }
95
96 pub fn access_level(&self) -> L {
98 match self {
99 Node::Command(cmd) => cmd.access_level,
100 Node::Directory(dir) => dir.access_level,
101 }
102 }
103}
104
105impl<L: AccessLevel> Directory<L> {
106 pub fn find_child(&self, name: &str) -> Option<&Node<L>> {
108 self.children.iter().find(|child| child.name() == name)
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use crate::auth::AccessLevel;
116
117 #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
119 enum TestAccessLevel {
120 Guest = 0,
121 User = 1,
122 }
123
124 impl AccessLevel for TestAccessLevel {
125 fn from_str(s: &str) -> Option<Self> {
126 match s {
127 "Guest" => Some(Self::Guest),
128 "User" => Some(Self::User),
129 _ => None,
130 }
131 }
132
133 fn as_str(&self) -> &'static str {
134 match self {
135 Self::Guest => "Guest",
136 Self::User => "User",
137 }
138 }
139 }
140
141 #[test]
142 fn test_command_kind() {
143 assert_eq!(CommandKind::Sync, CommandKind::Sync);
144
145 #[cfg(feature = "async")]
146 assert_ne!(CommandKind::Sync, CommandKind::Async);
147 }
148
149 #[test]
150 fn test_node_type_checking() {
151 const CMD: CommandMeta<TestAccessLevel> = CommandMeta {
152 id: "test",
153 name: "test",
154 description: "Test command",
155 access_level: TestAccessLevel::User,
156 kind: CommandKind::Sync,
157 min_args: 0,
158 max_args: 0,
159 };
160
161 let node = Node::Command(&CMD);
162 assert!(node.is_command());
163 assert!(!node.is_directory());
164 assert_eq!(node.name(), "test");
165 }
166}