cursive/cursive_runnable.rs
1use crate::{backend, backends, Cursive, CursiveRunner};
2
3type Initializer = dyn FnMut() -> Result<Box<dyn backend::Backend>, Box<dyn std::error::Error>>;
4
5/// A runnable wrapper around `Cursive`, bundling the backend initializer.
6///
7/// This struct embeds both `Cursive` and a backend-initializer
8/// (`FnMut() -> Result<dyn Backend>`), to provide a simple `.run()` method.
9///
10/// This lets you pick the backend when creating the Cursive root, rather than
11/// when running it.
12///
13/// It implements `DerefMut<Target=Cursive>`, so you can use it just like a
14/// regular `Cursive` object.
15pub struct CursiveRunnable {
16 siv: Cursive,
17 backend_init: Box<Initializer>,
18}
19
20impl Default for CursiveRunnable {
21 /// Creates a new Cursive wrapper using one of the available backends.
22 ///
23 /// Picks the first backend enabled from the list:
24 /// * BearLibTerminal
25 /// * Termion
26 /// * Crossterm
27 /// * Pancurses
28 /// * Ncurses
29 /// * Dummy
30 fn default() -> Self {
31 Self::with_initializer(Box::new(backends::try_default))
32 }
33}
34
35impl std::ops::Deref for CursiveRunnable {
36 type Target = Cursive;
37
38 fn deref(&self) -> &Cursive {
39 &self.siv
40 }
41}
42
43impl std::ops::DerefMut for CursiveRunnable {
44 fn deref_mut(&mut self) -> &mut Cursive {
45 &mut self.siv
46 }
47}
48
49impl std::borrow::Borrow<Cursive> for CursiveRunnable {
50 fn borrow(&self) -> &Cursive {
51 self
52 }
53}
54
55impl std::borrow::BorrowMut<Cursive> for CursiveRunnable {
56 fn borrow_mut(&mut self) -> &mut Cursive {
57 self
58 }
59}
60
61/// Helper function to help type inference when `Box::new` would not work.
62fn boxed(e: impl std::error::Error + 'static) -> Box<dyn std::error::Error> {
63 Box::new(e)
64}
65
66impl CursiveRunnable {
67 /// Creates a new Cursive wrapper using the given boxed backend initializer.
68 fn with_initializer(backend_init: Box<Initializer>) -> Self {
69 let siv = Cursive::new();
70 Self { siv, backend_init }
71 }
72
73 /// Creates a new Cursive wrapper, using the given backend.
74 pub fn new<E, F>(mut backend_init: F) -> Self
75 where
76 E: std::error::Error + 'static,
77 F: FnMut() -> Result<Box<dyn backend::Backend>, E> + 'static,
78 {
79 Self::with_initializer(Box::new(move || backend_init().map_err(boxed)))
80 }
81
82 /// Runs the event loop with the registered backend initializer.
83 ///
84 /// # Panics
85 ///
86 /// If the backend initialization fails.
87 pub fn run(&mut self) {
88 self.try_run().unwrap();
89 }
90
91 /// Runs the event loop with the registered backend initializer.
92 pub fn try_run(&mut self) -> Result<(), Box<dyn std::error::Error>> {
93 self.siv.try_run_with(&mut self.backend_init)
94 }
95
96 /// Gets a runner with the registered backend.
97 ///
98 /// Used to manually control the event loop. In most cases, running
99 /// `run()` will be easier.
100 ///
101 /// The runner will borrow `self`; when dropped, it will clear out the
102 /// terminal, and the cursive instance will be ready for another run if
103 /// needed.
104 pub fn try_runner(
105 &mut self,
106 ) -> Result<CursiveRunner<&mut Cursive>, Box<dyn std::error::Error>> {
107 Ok(self.siv.runner((self.backend_init)()?))
108 }
109
110 /// Gets a runner with the registered backend.
111 ///
112 /// # Panics
113 ///
114 /// If the backend initialization fails.
115 pub fn runner(&mut self) -> CursiveRunner<&mut Cursive> {
116 self.try_runner().unwrap()
117 }
118
119 /// Returns a new runner on the registered backend.
120 ///
121 /// Used to manually control the event loop. In most cases, running
122 /// `run()` will be easier.
123 ///
124 /// The runner will embed `self`; when dropped, it will clear out the
125 /// terminal, and the cursive instance will be dropped as well.
126 pub fn try_into_runner(mut self) -> Result<CursiveRunner<Self>, Box<dyn std::error::Error>> {
127 let backend = (self.backend_init)()?;
128 Ok(CursiveRunner::new(self, backend))
129 }
130
131 /// Returns a new runner on the registered backend.
132 ///
133 /// Used to manually control the event loop. In most cases, running
134 /// `run()` will be easier.
135 ///
136 /// The runner will embed `self`; when dropped, it will clear out the
137 /// terminal, and the cursive instance will be dropped as well.
138 ///
139 /// # Panics
140 ///
141 /// If the backend initialization fails.
142 pub fn into_runner(self) -> CursiveRunner<Self> {
143 self.try_into_runner().unwrap()
144 }
145
146 /// Creates a new Cursive wrapper using the dummy backend.
147 ///
148 /// Nothing will actually be output when calling `.run()`.
149 pub fn dummy() -> Self {
150 Self::new::<std::convert::Infallible, _>(|| Ok(cursive_core::backend::Dummy::init()))
151 }
152
153 /// Creates a new Cursive wrapper using the ncurses backend.
154 ///
155 /// _Requires the `ncurses-backend` feature._
156 #[cfg(feature = "ncurses-backend")]
157 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "ncurses-backend")))]
158 pub fn ncurses() -> Self {
159 Self::new(backends::curses::n::Backend::init)
160 }
161
162 /// Creates a new Cursive wrapper using the panncurses backend.
163 ///
164 /// _Requires the `panncurses-backend` feature._
165 #[cfg(feature = "pancurses-backend")]
166 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "pancurses-backend")))]
167 pub fn pancurses() -> Self {
168 Self::new(backends::curses::pan::Backend::init)
169 }
170
171 /// Creates a new Cursive wrapper using the termion backend.
172 ///
173 /// _Requires the `termion-backend` feature._
174 #[cfg(feature = "termion-backend")]
175 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "termion-backend")))]
176 pub fn termion() -> Self {
177 Self::new(backends::termion::Backend::init)
178 }
179
180 /// Creates a new Cursive wrapper using the crossterm backend.
181 ///
182 /// _Requires the `crossterm-backend` feature._
183 #[cfg(feature = "crossterm-backend")]
184 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "crossterm-backend")))]
185 pub fn crossterm() -> Self {
186 Self::new(backends::crossterm::Backend::init)
187 }
188
189 /// Creates a new Cursive wrapper using the bear-lib-terminal backend.
190 ///
191 /// _Requires the `blt-backend` feature._
192 #[cfg(feature = "blt-backend")]
193 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "blt-backend")))]
194 pub fn blt() -> Self {
195 Self::new::<std::convert::Infallible, _>(|| Ok(backends::blt::Backend::init()))
196 }
197}