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}