common: Add the shell module.
[gps-watch.git] / src / common / shell.rs
1 /*
2  * Copyright (c) 2020 Tilman Sauerbeck (tilman at code-monkey de)
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining
5  * a copy of this software and associated documentation files (the
6  * "Software"), to deal in the Software without restriction, including
7  * without limitation the rights to use, copy, modify, merge, publish,
8  * distribute, sublicense, and/or sell copies of the Software, and to
9  * permit persons to whom the Software is furnished to do so, subject to
10  * the following conditions:
11  *
12  * The above copyright notice and this permission notice shall be
13  * included in all copies or substantial portions of the Software.
14  *
15  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18  * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19  * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20  * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21  * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22  */
23
24 use buffer::Buffer;
25
26 pub struct Shell<'a> {
27     tx_buf: &'a mut Buffer,
28     command_offset: usize,
29     command_buffer: [u8; 32],
30 }
31
32 extern {
33     fn usb_serial_read(c: *mut u8) -> bool;
34 }
35
36 struct ArgumentIter<'a> {
37     command: &'a [u8],
38     offset: usize,
39 }
40
41 impl<'a> Iterator for ArgumentIter<'a> {
42     type Item = &'a [u8];
43
44     fn next(&mut self) -> Option<&'a [u8]> {
45         let delimiter = b' ';
46         let mut start : Option<usize> = None;
47
48         // Find the start of the substring.
49         for o in self.offset..self.command.len() {
50             if self.command[o] != delimiter {
51                 start = Some(o);
52                 break
53             }
54         }
55
56         start.and_then(|start2| {
57             let mut end : Option<usize> = None;
58
59             // Find the end of the substring.
60             for o in start2..self.command.len() {
61                 if self.command[o] == delimiter {
62                     break
63                 }
64
65                 end = Some(o);
66             }
67
68             end.and_then(|end2| {
69                 let end3 = end2 + 1;
70
71                 self.offset = end3;
72
73                 Some(&self.command[start2..end3])
74             })
75         })
76     }
77 }
78
79 fn read_char() -> Option<u8> {
80     unsafe {
81         let mut c = b'\0';
82
83         if usb_serial_read(&mut c) {
84             Some(c)
85         } else {
86             None
87         }
88     }
89 }
90
91 impl<'a> Shell<'a> {
92     pub fn new(tx_buf: &mut Buffer) -> Shell {
93         Shell {
94             tx_buf: tx_buf,
95             command_offset: 0,
96             command_buffer: [0; 32],
97         }
98     }
99
100     pub fn update(&mut self) {
101         while let Some(c) = read_char() {
102             if c != b'\n' {
103                 if self.command_offset != self.command_buffer.len() {
104                     self.command_buffer[self.command_offset] = c;
105                     self.command_offset += 1;
106                 }
107             } else {
108                 let command_length = self.command_offset;
109                 self.command_offset = 0;
110
111                 if command_length != self.command_buffer.len() {
112                     self.command_buffer[command_length] = b'\0';
113
114                     if command_length != 0 {
115                         self.dispatch(command_length);
116                     }
117                 } else {
118                     self.command_buffer[self.command_offset] = b'\0';
119
120                     self.tx_buf.write(b"maximum command length exceeded.\n");
121                 }
122
123                 self.tx_buf.write(b"$ ");
124                 self.tx_buf.flush();
125             }
126         }
127     }
128
129     fn dispatch(&mut self, command_length: usize) {
130         let command : [u8; 32] = self.command_buffer;
131
132         let mut args_iter = ArgumentIter {
133             command: &command[0..command_length],
134             offset: 0,
135         };
136
137         match args_iter.next() {
138             Some(b"help") => {
139                 let usage = b"\
140 Supported commands:
141     help           Show this help message.
142 ";
143
144                 self.tx_buf.write(usage);
145             },
146
147             Some(ref other) => {
148                 self.tx_buf.write(b"unknown_command: ");
149                 self.tx_buf.write(other);
150                 self.tx_buf.write(b"\n");
151             },
152
153             None => {
154             }
155         }
156     }
157 }