33type MenuCallbackFn < T > = fn ( menu : & Menu < T > ) ;
44type ItemCallbackFn < T > = fn ( menu : & Menu < T > , item : & Item < T > , args : & str , context : & mut T ) ;
55
6+ /// Describes a parameter to the command
7+ pub enum Parameter < ' a > {
8+ Mandatory ( & ' a str ) ,
9+ Optional ( & ' a str ) ,
10+ Named {
11+ parameter_name : & ' a str ,
12+ argument_name : & ' a str ,
13+ } ,
14+ }
15+
16+ /// Do we enter a sub-menu when this command is entered, or call a specific
17+ /// function?
618pub enum ItemType < ' a , T >
719where
820 T : ' a ,
921{
10- Callback ( ItemCallbackFn < T > ) ,
22+ Callback {
23+ function : ItemCallbackFn < T > ,
24+ parameters : & ' a [ Parameter < ' a > ] ,
25+ } ,
1126 Menu ( & ' a Menu < ' a , T > ) ,
1227}
1328
7186
7287 pub fn prompt ( & mut self , newline : bool ) {
7388 if newline {
74- write ! ( self . context, " \n " ) . unwrap ( ) ;
89+ writeln ! ( self . context) . unwrap ( ) ;
7590 }
7691 if self . depth != 0 {
7792 let mut depth = 1 ;
@@ -92,64 +107,10 @@ where
92107 return ;
93108 }
94109 let outcome = if input == 0x0D {
95- write ! ( self . context, "\n " ) . unwrap ( ) ;
96- if let Ok ( s) = core:: str:: from_utf8 ( & self . buffer [ 0 ..self . used ] ) {
97- if s == "help" {
98- let menu = self . menus [ self . depth ] . unwrap ( ) ;
99- for item in menu. items {
100- if let Some ( help) = item. help {
101- writeln ! ( self . context, "{} - {}" , item. command, help) . unwrap ( ) ;
102- } else {
103- writeln ! ( self . context, "{}" , item. command) . unwrap ( ) ;
104- }
105- }
106- if self . depth != 0 {
107- writeln ! ( self . context, "exit - leave this menu." ) . unwrap ( ) ;
108- }
109- writeln ! ( self . context, "help - print this help text." ) . unwrap ( ) ;
110- Outcome :: CommandProcessed
111- } else if s == "exit" && self . depth != 0 {
112- if self . depth == self . menus . len ( ) {
113- writeln ! ( self . context, "Can't enter menu - structure too deep." ) . unwrap ( ) ;
114- } else {
115- self . menus [ self . depth ] = None ;
116- self . depth -= 1 ;
117- }
118- Outcome :: CommandProcessed
119- } else {
120- let mut parts = s. split ( ' ' ) ;
121- if let Some ( cmd) = parts. next ( ) {
122- let mut found = false ;
123- let menu = self . menus [ self . depth ] . unwrap ( ) ;
124- for item in menu. items {
125- if cmd == item. command {
126- match item. item_type {
127- ItemType :: Callback ( f) => f ( menu, item, s, & mut self . context ) ,
128- ItemType :: Menu ( m) => {
129- self . depth += 1 ;
130- self . menus [ self . depth ] = Some ( m) ;
131- }
132- }
133- found = true ;
134- break ;
135- }
136- }
137- if !found {
138- writeln ! ( self . context, "Command {:?} not found. Try 'help'." , cmd)
139- . unwrap ( ) ;
140- }
141- Outcome :: CommandProcessed
142- } else {
143- writeln ! ( self . context, "Input empty" ) . unwrap ( ) ;
144- Outcome :: CommandProcessed
145- }
146- }
147- } else {
148- writeln ! ( self . context, "Input not valid UTF8" ) . unwrap ( ) ;
149- Outcome :: CommandProcessed
150- }
151- } else if input == 0x08 {
152- // Handling backspace
110+ writeln ! ( self . context) . unwrap ( ) ;
111+ self . process_command ( )
112+ } else if ( input == 0x08 ) || ( input == 0x7F ) {
113+ // Handling backspace or delete
153114 if self . used > 0 {
154115 write ! ( self . context, "\u{0008} \u{0008} " ) . unwrap ( ) ;
155116 self . used -= 1 ;
@@ -159,15 +120,17 @@ where
159120 self . buffer [ self . used ] = input;
160121 self . used += 1 ;
161122
162- let valid = if let Ok ( _) = core:: str:: from_utf8 ( & self . buffer [ 0 ..self . used ] ) {
163- true
164- } else {
165- false
166- } ;
123+ // We have to do this song and dance because `self.prompt()` needs
124+ // a mutable reference to self, and we can't have that while
125+ // holding a reference to the buffer at the same time.
126+ // This line grabs the buffer, checks it's OK, then releases it again
127+ let valid = core:: str:: from_utf8 ( & self . buffer [ 0 ..self . used ] ) . is_ok ( ) ;
128+ // Now we've released the buffer, we can draw the prompt
167129 if valid {
168130 write ! ( self . context, "\r " ) . unwrap ( ) ;
169131 self . prompt ( false ) ;
170132 }
133+ // Grab the buffer again to render it to the screen
171134 if let Ok ( s) = core:: str:: from_utf8 ( & self . buffer [ 0 ..self . used ] ) {
172135 write ! ( self . context, "{}" , s) . unwrap ( ) ;
173136 }
@@ -184,6 +147,115 @@ where
184147 Outcome :: NeedMore => { }
185148 }
186149 }
150+
151+ fn process_command ( & mut self ) -> Outcome {
152+ if let Ok ( command_line) = core:: str:: from_utf8 ( & self . buffer [ 0 ..self . used ] ) {
153+ if command_line == "help" {
154+ let menu = self . menus [ self . depth ] . unwrap ( ) ;
155+ for item in menu. items {
156+ self . print_help ( & item) ;
157+ }
158+ if self . depth != 0 {
159+ writeln ! ( self . context, "* exit - leave this menu." ) . unwrap ( ) ;
160+ }
161+ writeln ! ( self . context, "* help - print this help text" ) . unwrap ( ) ;
162+ Outcome :: CommandProcessed
163+ } else if command_line == "exit" && self . depth != 0 {
164+ if self . depth == self . menus . len ( ) {
165+ writeln ! ( self . context, "Can't enter menu - structure too deep." ) . unwrap ( ) ;
166+ } else {
167+ self . menus [ self . depth ] = None ;
168+ self . depth -= 1 ;
169+ }
170+ Outcome :: CommandProcessed
171+ } else {
172+ let mut parts = command_line. split ( ' ' ) ;
173+ if let Some ( cmd) = parts. next ( ) {
174+ let mut found = false ;
175+ let menu = self . menus [ self . depth ] . unwrap ( ) ;
176+ for item in menu. items {
177+ if cmd == item. command {
178+ match item. item_type {
179+ ItemType :: Callback {
180+ function,
181+ parameters,
182+ } => self . call_function (
183+ function,
184+ parameters,
185+ menu,
186+ item,
187+ command_line,
188+ ) ,
189+ ItemType :: Menu ( m) => {
190+ self . depth += 1 ;
191+ self . menus [ self . depth ] = Some ( m) ;
192+ }
193+ }
194+ found = true ;
195+ break ;
196+ }
197+ }
198+ if !found {
199+ writeln ! ( self . context, "Command {:?} not found. Try 'help'." , cmd) . unwrap ( ) ;
200+ }
201+ Outcome :: CommandProcessed
202+ } else {
203+ writeln ! ( self . context, "Input empty" ) . unwrap ( ) ;
204+ Outcome :: CommandProcessed
205+ }
206+ }
207+ } else {
208+ writeln ! ( self . context, "Input not valid UTF8" ) . unwrap ( ) ;
209+ Outcome :: CommandProcessed
210+ }
211+ }
212+
213+ fn print_help ( & mut self , item : & Item < T > ) {
214+ match item. item_type {
215+ ItemType :: Callback { parameters, .. } => {
216+ if !parameters. is_empty ( ) {
217+ write ! ( self . context, "* {}" , item. command) . unwrap ( ) ;
218+ for param in parameters. iter ( ) {
219+ match param {
220+ Parameter :: Mandatory ( name) => {
221+ write ! ( self . context, " <{}>" , name) . unwrap ( ) ;
222+ }
223+ Parameter :: Optional ( name) => {
224+ write ! ( self . context, " [ <{}> ]" , name) . unwrap ( ) ;
225+ }
226+ Parameter :: Named {
227+ parameter_name,
228+ argument_name,
229+ } => {
230+ write ! ( self . context, " [ --{}={} ]" , parameter_name, argument_name)
231+ . unwrap ( ) ;
232+ }
233+ }
234+ }
235+ } else {
236+ write ! ( self . context, "* {}" , item. command) . unwrap ( ) ;
237+ }
238+ }
239+ ItemType :: Menu ( _menu) => {
240+ write ! ( self . context, "* {}" , item. command) . unwrap ( ) ;
241+ }
242+ }
243+ if let Some ( help) = item. help {
244+ write ! ( self . context, " - {}" , help) . unwrap ( ) ;
245+ }
246+ writeln ! ( self . context) . unwrap ( ) ;
247+ }
248+
249+ fn call_function (
250+ & self ,
251+ _function : ItemCallbackFn < T > ,
252+ _parameters : & [ Parameter ] ,
253+ _parent_menu : & Menu < T > ,
254+ _item : & Item < T > ,
255+ _command : & str ,
256+ ) {
257+
258+ }
187259}
188260
189261#[ cfg( test) ]
0 commit comments