ab814962ad0ea83368caec4546a5f2f7195f8a18
[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 use storage::Storage;
26
27 pub struct Shell<'a> {
28     tx_buf: &'a mut Buffer,
29     command_offset: usize,
30     command_buffer: [u8; 32],
31 }
32
33 extern {
34     fn usb_serial_read(c: *mut u8) -> bool;
35 }
36
37 struct ArgumentIter<'a> {
38     command: &'a [u8],
39     offset: usize,
40 }
41
42 impl<'a> Iterator for ArgumentIter<'a> {
43     type Item = &'a [u8];
44
45     fn next(&mut self) -> Option<&'a [u8]> {
46         let delimiter = b' ';
47         let mut start : Option<usize> = None;
48
49         // Find the start of the substring.
50         for o in self.offset..self.command.len() {
51             if self.command[o] != delimiter {
52                 start = Some(o);
53                 break
54             }
55         }
56
57         start.and_then(|start2| {
58             let mut end : Option<usize> = None;
59
60             // Find the end of the substring.
61             for o in start2..self.command.len() {
62                 if self.command[o] == delimiter {
63                     break
64                 }
65
66                 end = Some(o);
67             }
68
69             end.and_then(|end2| {
70                 let end3 = end2 + 1;
71
72                 self.offset = end3;
73
74                 Some(&self.command[start2..end3])
75             })
76         })
77     }
78 }
79
80 fn read_char() -> Option<u8> {
81     unsafe {
82         let mut c = b'\0';
83
84         if usb_serial_read(&mut c) {
85             Some(c)
86         } else {
87             None
88         }
89     }
90 }
91
92 struct Context<'a> {
93     storage: &'a mut dyn Storage,
94 }
95
96 impl<'a> Shell<'a> {
97     pub fn new(tx_buf: &mut Buffer) -> Shell {
98         Shell {
99             tx_buf: tx_buf,
100             command_offset: 0,
101             command_buffer: [0; 32],
102         }
103     }
104
105     pub fn update(&mut self, storage: &mut dyn Storage) {
106         let mut context = Context {
107             storage: storage,
108         };
109
110         while let Some(c) = read_char() {
111             if c != b'\n' {
112                 if self.command_offset != self.command_buffer.len() {
113                     self.command_buffer[self.command_offset] = c;
114                     self.command_offset += 1;
115                 }
116             } else {
117                 let command_length = self.command_offset;
118                 self.command_offset = 0;
119
120                 if command_length != self.command_buffer.len() {
121                     self.command_buffer[command_length] = b'\0';
122
123                     if command_length != 0 {
124                         self.dispatch(command_length, &mut context);
125                     }
126                 } else {
127                     self.command_buffer[self.command_offset] = b'\0';
128
129                     self.tx_buf.write(b"maximum command length exceeded.\n");
130                 }
131
132                 self.tx_buf.write(b"$ ");
133                 self.tx_buf.flush();
134             }
135         }
136     }
137
138     fn dispatch(&mut self, command_length: usize, mut context: &mut Context) {
139         let command : [u8; 32] = self.command_buffer;
140
141         let mut args_iter = ArgumentIter {
142             command: &command[0..command_length],
143             offset: 0,
144         };
145
146         match args_iter.next() {
147             Some(b"help") => {
148                 let usage = b"\
149 Supported commands:
150     help           Show this help message.
151     clear_storage  Fully erase the flash's contents.
152 ";
153
154                 self.tx_buf.write(usage);
155             },
156
157             Some(b"clear_storage") => self.run_clear_storage(&mut context),
158
159             Some(ref other) => {
160                 self.tx_buf.write(b"unknown_command: ");
161                 self.tx_buf.write(other);
162                 self.tx_buf.write(b"\n");
163             },
164
165             None => {
166             }
167         }
168     }
169
170     fn run_clear_storage(&self, context: &mut Context) {
171         context.storage.clear();
172     }
173 }