Skip to content

Commit d3bfe52

Browse files
authored
Merge pull request #24 from eivindbergem/noline-support
Noline support MVP
2 parents be2b623 + c101737 commit d3bfe52

File tree

5 files changed

+512
-147
lines changed

5 files changed

+512
-147
lines changed

.github/workflows/rust.yml

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,18 @@ jobs:
1818
runs-on: ubuntu-latest
1919
steps:
2020
- uses: actions/checkout@v4
21-
- name: Build
21+
- name: Build (default features)
2222
run: cargo build
23-
- name: Run Tests
23+
- name: Build (no features)
24+
run: cargo build --no-default-features
25+
- name: Build (all features)
26+
run: cargo build --all-features
27+
- name: Run Tests (default features)
2428
run: cargo test
29+
- name: Run Tests (no features)
30+
run: cargo test --no-default-features
31+
- name: Run Tests (all features)
32+
run: cargo test --all-features
2533

2634
clippy:
2735
runs-on: ubuntu-latest

Cargo.toml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,15 @@ repository = "https://github.com/rust-embedded-community/menu"
99
readme = "README.md"
1010

1111
[dependencies]
12-
12+
embedded-io = "0.6.1"
13+
noline = { version = "0.5.0", optional = true }
1314

1415
[features]
1516
default = ["echo"]
1617
echo = []
1718

1819
[dev-dependencies]
20+
noline = { version = "0.5.0", features = ["std"] }
1921
pancurses = "0.16"
22+
termion = "4.0.2"
23+
menu = {path = ".", features = ["noline"]}

examples/noline.rs

Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
extern crate menu;
2+
3+
use embedded_io::{ErrorType, Read as EmbRead, Write as EmbWrite};
4+
use menu::*;
5+
use noline::builder::EditorBuilder;
6+
use std::io::{self, Read as _, Stdin, Stdout, Write as _};
7+
use termion::raw::IntoRawMode;
8+
9+
pub struct IOWrapper {
10+
stdin: Stdin,
11+
stdout: Stdout,
12+
}
13+
14+
impl IOWrapper {
15+
pub fn new() -> Self {
16+
Self {
17+
stdin: std::io::stdin(),
18+
stdout: std::io::stdout(),
19+
}
20+
}
21+
}
22+
23+
impl Default for IOWrapper {
24+
fn default() -> Self {
25+
Self::new()
26+
}
27+
}
28+
29+
impl ErrorType for IOWrapper {
30+
type Error = embedded_io::ErrorKind;
31+
}
32+
33+
impl EmbRead for IOWrapper {
34+
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Self::Error> {
35+
Ok(self.stdin.read(buf).map_err(|e| e.kind())?)
36+
}
37+
}
38+
39+
impl EmbWrite for IOWrapper {
40+
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
41+
let mut written = 0;
42+
let parts = buf.split(|b| *b == b'\n').collect::<Vec<_>>();
43+
44+
for (i, part) in parts.iter().enumerate() {
45+
written += self.stdout.write(part).map_err(|e| e.kind())?;
46+
47+
if i != parts.len() - 1 {
48+
let _ = self.stdout.write(b"\r\n").map_err(|e| e.kind())?;
49+
written += 1;
50+
}
51+
}
52+
53+
Ok(written)
54+
}
55+
56+
fn flush(&mut self) -> Result<(), Self::Error> {
57+
Ok(self.stdout.flush().map_err(|e| e.kind())?)
58+
}
59+
}
60+
61+
#[derive(Default)]
62+
struct Context {
63+
_inner: u32,
64+
}
65+
66+
const ROOT_MENU: Menu<IOWrapper, Context> = Menu {
67+
label: "root",
68+
items: &[
69+
&Item {
70+
item_type: ItemType::Callback {
71+
function: select_foo,
72+
parameters: &[
73+
Parameter::Mandatory {
74+
parameter_name: "a",
75+
help: Some("This is the help text for 'a'"),
76+
},
77+
Parameter::Optional {
78+
parameter_name: "b",
79+
help: None,
80+
},
81+
Parameter::Named {
82+
parameter_name: "verbose",
83+
help: None,
84+
},
85+
Parameter::NamedValue {
86+
parameter_name: "level",
87+
argument_name: "INT",
88+
help: Some("Set the level of the dangle"),
89+
},
90+
],
91+
},
92+
command: "foo",
93+
help: Some(
94+
"Makes a foo appear.
95+
96+
This is some extensive help text.
97+
98+
It contains multiple paragraphs and should be preceeded by the parameter list.
99+
",
100+
),
101+
},
102+
&Item {
103+
item_type: ItemType::Callback {
104+
function: select_bar,
105+
parameters: &[],
106+
},
107+
command: "bar",
108+
help: Some("fandoggles a bar"),
109+
},
110+
&Item {
111+
item_type: ItemType::Menu(&Menu {
112+
label: "sub",
113+
items: &[
114+
&Item {
115+
item_type: ItemType::Callback {
116+
function: select_baz,
117+
parameters: &[],
118+
},
119+
command: "baz",
120+
help: Some("thingamobob a baz"),
121+
},
122+
&Item {
123+
item_type: ItemType::Callback {
124+
function: select_quux,
125+
parameters: &[],
126+
},
127+
command: "quux",
128+
help: Some("maximum quux"),
129+
},
130+
],
131+
entry: Some(enter_sub),
132+
exit: Some(exit_sub),
133+
}),
134+
command: "sub",
135+
help: Some("enter sub-menu"),
136+
},
137+
],
138+
entry: Some(enter_root),
139+
exit: Some(exit_root),
140+
};
141+
142+
fn main() {
143+
let _stdout = io::stdout().into_raw_mode().unwrap();
144+
145+
let mut io = IOWrapper::new();
146+
let mut editor = EditorBuilder::new_unbounded()
147+
.with_unbounded_history()
148+
.build_sync(&mut io)
149+
.unwrap();
150+
151+
let mut context = Context::default();
152+
let mut r = Runner::new(ROOT_MENU, &mut editor, io, &mut context);
153+
154+
while let Ok(_) = r.input_line(&mut context) {}
155+
}
156+
157+
fn enter_root(_menu: &Menu<IOWrapper, Context>, interface: &mut IOWrapper, _context: &mut Context) {
158+
writeln!(interface, "In enter_root").unwrap();
159+
}
160+
161+
fn exit_root(_menu: &Menu<IOWrapper, Context>, interface: &mut IOWrapper, _context: &mut Context) {
162+
writeln!(interface, "In exit_root").unwrap();
163+
}
164+
165+
fn select_foo(
166+
_menu: &Menu<IOWrapper, Context>,
167+
item: &Item<IOWrapper, Context>,
168+
args: &[&str],
169+
interface: &mut IOWrapper,
170+
_context: &mut Context,
171+
) {
172+
writeln!(interface, "In select_foo. Args = {:?}", args).unwrap();
173+
writeln!(
174+
interface,
175+
"a = {:?}",
176+
::menu::argument_finder(item, args, "a")
177+
)
178+
.unwrap();
179+
writeln!(
180+
interface,
181+
"b = {:?}",
182+
::menu::argument_finder(item, args, "b")
183+
)
184+
.unwrap();
185+
writeln!(
186+
interface,
187+
"verbose = {:?}",
188+
::menu::argument_finder(item, args, "verbose")
189+
)
190+
.unwrap();
191+
writeln!(
192+
interface,
193+
"level = {:?}",
194+
::menu::argument_finder(item, args, "level")
195+
)
196+
.unwrap();
197+
writeln!(
198+
interface,
199+
"no_such_arg = {:?}",
200+
::menu::argument_finder(item, args, "no_such_arg")
201+
)
202+
.unwrap();
203+
}
204+
205+
fn select_bar(
206+
_menu: &Menu<IOWrapper, Context>,
207+
_item: &Item<IOWrapper, Context>,
208+
args: &[&str],
209+
interface: &mut IOWrapper,
210+
_context: &mut Context,
211+
) {
212+
writeln!(interface, "In select_bar. Args = {:?}", args).unwrap();
213+
}
214+
215+
fn enter_sub(_menu: &Menu<IOWrapper, Context>, interface: &mut IOWrapper, _context: &mut Context) {
216+
writeln!(interface, "In enter_sub").unwrap();
217+
}
218+
219+
fn exit_sub(_menu: &Menu<IOWrapper, Context>, interface: &mut IOWrapper, _context: &mut Context) {
220+
writeln!(interface, "In exit_sub").unwrap();
221+
}
222+
223+
fn select_baz(
224+
_menu: &Menu<IOWrapper, Context>,
225+
_item: &Item<IOWrapper, Context>,
226+
args: &[&str],
227+
interface: &mut IOWrapper,
228+
_context: &mut Context,
229+
) {
230+
writeln!(interface, "In select_baz: Args = {:?}", args).unwrap();
231+
}
232+
233+
fn select_quux(
234+
_menu: &Menu<IOWrapper, Context>,
235+
_item: &Item<IOWrapper, Context>,
236+
args: &[&str],
237+
interface: &mut IOWrapper,
238+
_context: &mut Context,
239+
) {
240+
writeln!(interface, "In select_quux: Args = {:?}", args).unwrap();
241+
}

examples/simple.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
extern crate menu;
22

3+
use embedded_io::Write;
34
use menu::*;
45
use pancurses::{endwin, initscr, noecho, Input};
5-
use std::fmt::Write;
6+
use std::convert::Infallible;
67

78
#[derive(Default)]
89
struct Context {
@@ -87,9 +88,17 @@ It contains multiple paragraphs and should be preceeded by the parameter list.
8788

8889
struct Output(pancurses::Window);
8990

90-
impl std::fmt::Write for Output {
91-
fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> {
92-
self.0.printw(s);
91+
impl embedded_io::ErrorType for Output {
92+
type Error = Infallible;
93+
}
94+
95+
impl Write for Output {
96+
fn write(&mut self, buf: &[u8]) -> Result<usize, Self::Error> {
97+
self.0.printw(core::str::from_utf8(buf).unwrap());
98+
Ok(buf.len())
99+
}
100+
101+
fn flush(&mut self) -> Result<(), Self::Error> {
93102
Ok(())
94103
}
95104
}

0 commit comments

Comments
 (0)