r3bl_terminal_async/
lib.rs

1/*
2 *   Copyright (c) 2024 R3BL LLC
3 *   All rights reserved.
4 *
5 *   Licensed under the Apache License, Version 2.0 (the "License");
6 *   you may not use this file except in compliance with the License.
7 *   You may obtain a copy of the License at
8 *
9 *   http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *   Unless required by applicable law or agreed to in writing, software
12 *   distributed under the License is distributed on an "AS IS" BASIS,
13 *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *   See the License for the specific language governing permissions and
15 *   limitations under the License.
16 */
17
18//! # Why R3BL?
19//!
20//! <img
21//! src="https://raw.githubusercontent.com/r3bl-org/r3bl-open-core/main/r3bl-term.svg?raw=true"
22//! height="256px">
23//!
24//! <!-- R3BL TUI library & suite of apps focused on developer productivity -->
25//!
26//! <span style="color:#FD2F53">R</span><span style="color:#FC2C57">3</span><span
27//! style="color:#FB295B">B</span><span style="color:#FA265F">L</span><span
28//! style="color:#F92363"> </span><span style="color:#F82067">T</span><span
29//! style="color:#F61D6B">U</span><span style="color:#F51A6F">I</span><span
30//! style="color:#F31874"> </span><span style="color:#F11678">l</span><span
31//! style="color:#EF137C">i</span><span style="color:#ED1180">b</span><span
32//! style="color:#EB0F84">r</span><span style="color:#E90D89">a</span><span
33//! style="color:#E60B8D">r</span><span style="color:#E40A91">y</span><span
34//! style="color:#E10895"> </span><span style="color:#DE0799">&amp;</span><span
35//! style="color:#DB069E"> </span><span style="color:#D804A2">s</span><span
36//! style="color:#D503A6">u</span><span style="color:#D203AA">i</span><span
37//! style="color:#CF02AE">t</span><span style="color:#CB01B2">e</span><span
38//! style="color:#C801B6"> </span><span style="color:#C501B9">o</span><span
39//! style="color:#C101BD">f</span><span style="color:#BD01C1"> </span><span
40//! style="color:#BA01C4">a</span><span style="color:#B601C8">p</span><span
41//! style="color:#B201CB">p</span><span style="color:#AE02CF">s</span><span
42//! style="color:#AA03D2"> </span><span style="color:#A603D5">f</span><span
43//! style="color:#A204D8">o</span><span style="color:#9E06DB">c</span><span
44//! style="color:#9A07DE">u</span><span style="color:#9608E1">s</span><span
45//! style="color:#910AE3">e</span><span style="color:#8D0BE6">d</span><span
46//! style="color:#890DE8"> </span><span style="color:#850FEB">o</span><span
47//! style="color:#8111ED">n</span><span style="color:#7C13EF"> </span><span
48//! style="color:#7815F1">d</span><span style="color:#7418F3">e</span><span
49//! style="color:#701AF5">v</span><span style="color:#6B1DF6">e</span><span
50//! style="color:#6720F8">l</span><span style="color:#6322F9">o</span><span
51//! style="color:#5F25FA">p</span><span style="color:#5B28FB">e</span><span
52//! style="color:#572CFC">r</span><span style="color:#532FFD"> </span><span
53//! style="color:#4F32FD">p</span><span style="color:#4B36FE">r</span><span
54//! style="color:#4739FE">o</span><span style="color:#443DFE">d</span><span
55//! style="color:#4040FE">u</span><span style="color:#3C44FE">c</span><span
56//! style="color:#3948FE">t</span><span style="color:#354CFE">i</span><span
57//! style="color:#324FFD">v</span><span style="color:#2E53FD">i</span><span
58//! style="color:#2B57FC">t</span><span style="color:#285BFB">y</span>
59//!
60//! We are working on building command line apps in Rust which have rich text user
61//! interfaces (TUI). We want to lean into the terminal as a place of productivity, and
62//! build all kinds of awesome apps for it.
63//!
64//! 1. 🔮 Instead of just building one app, we are building a library to enable any kind
65//!    of rich TUI development w/ a twist: taking concepts that work really well for the
66//!    frontend mobile and web development world and re-imagining them for TUI & Rust.
67//!
68//!    - Taking inspiration from things like [React](https://react.dev/),
69//!      [SolidJS](https://www.solidjs.com/),
70//!      [Elm](https://guide.elm-lang.org/architecture/),
71//!      [iced-rs](https://docs.rs/iced/latest/iced/), [Jetpack
72//!      Compose](https://developer.android.com/compose),
73//!      [JSX](https://ui.dev/imperative-vs-declarative-programming),
74//!      [CSS](https://www.w3.org/TR/CSS/#css), but making everything async (so they can
75//!      be run in parallel & concurrent via [Tokio](https://crates.io/crates/tokio)).
76//!    - Even the thread running the main event loop doesn't block since it is async.
77//!    - Using proc macros to create DSLs to implement something inspired by
78//!      [CSS](https://www.w3.org/TR/CSS/#css) &
79//!      [JSX](https://ui.dev/imperative-vs-declarative-programming).
80//!
81//! 2. 🌎 We are building apps to enhance developer productivity & workflows.
82//!
83//!    - The idea here is not to rebuild `tmux` in Rust (separate processes mux'd onto a
84//!      single terminal window). Rather it is to build a set of integrated "apps" (or
85//!      "tasks") that run in the same process that renders to one terminal window.
86//!    - Inside of this terminal window, we can implement things like "app" switching,
87//!      routing, tiling layout, stacking layout, etc. so that we can manage a lot of TUI
88//!      apps (which are tightly integrated) that are running in the same process, in the
89//!      same window. So you can imagine that all these "app"s have shared application
90//!      state. Each "app" may also have its own local application state.
91//!    - Here are some examples of the types of "app"s we plan to build (for which this
92//!      infrastructure acts as the open source engine):
93//!      1. Multi user text editors w/ syntax highlighting.
94//!      2. Integrations w/ github issues.
95//!      3. Integrations w/ calendar, email, contacts APIs.
96//!
97//! All the crates in the `r3bl-open-core`
98//! [repo](https://github.com/r3bl-org/r3bl-open-core/) provide lots of useful
99//! functionality to help you build TUI (text user interface) apps, along w/ general
100//! niceties & ergonomics that all Rustaceans 🦀 can enjoy 🎉.
101//!
102//! # Table of contents
103//!
104//! <!-- cspell:disable -->
105//! <!-- TOC -->
106//!
107//! - [Introduction](#introduction)
108//! - [Changelog](#changelog)
109//! - [Learn how these crates are built, provide
110//!   feedback](#learn-how-these-crates-are-built-provide-feedback)
111//! - [Features](#features)
112//!   - [Pause and resume support](#pause-and-resume-support)
113//!   - [Input Editing Behavior](#input-editing-behavior)
114//! - [Examples](#examples)
115//! - [How to use this crate](#how-to-use-this-crate)
116//!   - [TerminalAsync::try_new, which is the main entry point for most use
117//!     cases](#terminalasynctry_new-which-is-the-main-entry-point-for-most-use-cases)
118//!   - [Readline overview please see the docs for this struct for
119//!     details](#readline-overview-please-see-the-docs-for-this-struct-for-details)
120//!   - [Spinner::try_start](#spinnertry_start)
121//! - [Build this crate with Naz on YouTube](#build-this-crate-with-naz-on-youtube)
122//! - [Why another async readline crate?](#why-another-async-readline-crate)
123//!   - [References for blocking and thread cancellation in
124//!     Rust](#references-for-blocking-and-thread-cancellation-in-rust)
125//!   - [Educational references for Linux TTY and async
126//!     Rust](#educational-references-for-linux-tty-and-async-rust)
127//!
128//! <!-- /TOC -->
129//! <!-- cspell:enable -->
130//!
131//! # Introduction
132//!
133//! The `r3bl_terminal_async` library lets your CLI program be asynchronous and
134//! interactive without blocking the main thread. Your spawned tasks can use it to
135//! concurrently write to the display output, pause and resume it. You can also display of
136//! colorful animated spinners ⌛🌈 for long running tasks. With it, you can create
137//! beautiful, powerful, and interactive REPLs (read execute print loops) with ease.
138//!
139//! 1. Because
140//!    [`read_line()`](https://doc.rust-lang.org/std/io/struct.Stdin.html#method.read_line)
141//!    is blocking. And there is no way to terminate an OS thread that is blocking in
142//!    Rust. To do this you have to exit the process (who's thread is blocked in
143//!    `read_line()`).
144//!
145//!     - There is no way to get `read_line()` unblocked once it is blocked.
146//!     - You can use
147//!       [`process::exit()`](https://doc.rust-lang.org/std/process/fn.exit.html) or
148//!       [`panic!()`](https://doc.rust-lang.org/std/panic/index.html) to kill the entire
149//!       process. This is not appealing.
150//!     - Even if that task is wrapped in a [`thread::spawn()` or
151//!       `thread::spawn_blocking()`](https://tokio.rs/tokio/tutorial/spawning), it isn't
152//!       possible to cancel or abort that thread, without cooperatively asking it to
153//!       exit. To see what this type of code looks like, take a look at
154//!       [this](https://github.com/nazmulidris/rust-scratch/blob/fcd730c4b17ed0b09ff2c1a7ac4dd5b4a0c66e49/tcp-api-server/src/client_task.rs#L275).
155//!
156//! 2. Another problem is that when a thread is blocked in `read_line()`, and you have to
157//!    display output to `stdout` concurrently, this poses some challenges.
158//!
159//!     - This is because the caret is moved by `read_line()` and it blocks.
160//!     - When another thread / task writes to `stdout` concurrently, it assumes that the
161//!       caret is at row 0 of a new line.
162//!     - This results in output that doesn't look good since it clobbers the
163//!       `read_line()` output, which assumes that no other output will be produced, while
164//!       is blocking for user input, resulting in a bad user experience.
165//!
166//! Here is a video of the `terminal_async` and `spinner` examples in this crate, in
167//! action:
168//!
169//! ![terminal_async_video](https://github.com/r3bl-org/r3bl-open-core/blob/main/terminal_async/docs/r3bl_terminal_async_clip_ffmpeg.gif?raw=true)
170//!
171//! # Changelog
172//!
173//! Please check out the
174//! [changelog](https://github.com/r3bl-org/r3bl-open-core/blob/main/CHANGELOG.md#r3bl_terminal_async)
175//! to see how the library has evolved over time.
176//!
177//! # Learn how these crates are built, provide feedback
178//!
179//! To learn how we built this crate, please take a look at the following resources.
180//! - If you like consuming video content, here's our [YT
181//!   channel](https://www.youtube.com/@developerlifecom). Please consider
182//!   [subscribing](https://www.youtube.com/channel/CHANNEL_ID?sub_confirmation=1).
183//! - If you like consuming written content, here's our developer
184//!   [site](https://developerlife.com/). Please consider subscribing to our
185//!   [newsletter](https://developerlife.com/subscribe.html).
186//! - If you have questions, please join our [discord
187//!   server](https://discord.gg/8M2ePAevaM).
188//!
189//! # Features
190//!
191//! 1. Read user input from the terminal line by line, while your program concurrently
192//!    writes lines to the same terminal. One [`Readline`] instance can be used to spawn
193//!    many async `stdout` writers ([r3bl_core::SharedWriter]) that can write to
194//!    the terminal concurrently. For most users the [`TerminalAsync`] struct is the
195//!    simplest way to use this crate. You rarely have to access the underlying
196//!    [`Readline`] or [`r3bl_core::SharedWriter`] directly. But you can if you
197//!    need to. [`r3bl_core::SharedWriter`] can be cloned and is thread-safe.
198//!    However, there is only one instance of [`Readline`] per [`TerminalAsync`] instance.
199//!
200//! 2. Generate a spinner (indeterminate progress indicator). This spinner works
201//!    concurrently with the rest of your program. When the [`Spinner`] is active it
202//!    automatically pauses output from all the [`r3bl_core::SharedWriter`]
203//!    instances that are associated with one [`Readline`] instance. Typically a spawned
204//!    task clones its own [`r3bl_core::SharedWriter`] to generate its output.
205//!    This is useful when you want to show a spinner while waiting for a long-running
206//!    task to complete. Please look at the example to see this in action, by running
207//!    `cargo run --example terminal_async`. Then type `starttask1`, press Enter. Then
208//!    type `spinner`, press Enter.
209//!
210//! 3. Use tokio tracing with support for concurrent `stout` writes. If you choose to log
211//!    to `stdout` then the concurrent version ([`r3bl_core::SharedWriter`]) from
212//!    this crate will be used. This ensures that the concurrent output is supported even
213//!    for your tracing logs to `stdout`.
214//!
215//! 4. You can also plug in your own terminal, like `stdout`, or `stderr`, or any other
216//!    terminal that implements [`SendRawTerminal`] trait for more details.
217//!
218//! This crate can detect when your terminal is not in interactive mode. Eg: when you pipe
219//! the output of your program to another program. In this case, the `readline` feature is
220//! disabled. Both the [`TerminalAsync`] and [`Spinner`] support this functionality. So if
221//! you run the examples in this crate, and pipe something into them, they won't do
222//! anything.
223//!
224//! Here's an example:
225//!
226//! ```bash
227//! # This will work.
228//! cargo run --examples terminal_async
229//!
230//! # This won't do anything. Just exits with no error.
231//! echo "hello" | cargo run --examples terminal_async
232//! ```
233//!
234//! ## Pause and resume support
235//!
236//! The pause and resume functionality is implemented using:
237//! - [LineState::is_paused] - Used to check if the line state is paused and affects
238//!   rendering and input.
239//! - [LineState::set_paused] - Use to set the paused state via the
240//!   [r3bl_core::SharedWriter] below. This can't be called directly (outside the
241//!   crate itself).
242//! - [r3bl_core::SharedWriter::line_state_control_channel_sender] - Mechanism
243//!   used to manipulate the paused state.
244//!
245//! The [Readline::new] or [TerminalAsync::try_new] create a `line_channel` to send and
246//! receive [r3bl_core::LineStateControlSignal]:
247//! 1. The sender end of this channel is moved to the [r3bl_core::SharedWriter].
248//!    So any [r3bl_core::SharedWriter] can be used to send
249//!    [r3bl_core::LineStateControlSignal]s to the channel, which will be
250//!    processed in the task started, just for this, in [Readline::new]. This is the
251//!    primary mechanism to switch between pause and resume. Some helper functions are
252//!    provided in [TerminalAsync::pause] and [TerminalAsync::resume], though you can just
253//!    send the signals directly to the channel's sender via the
254//!    [r3bl_core::SharedWriter::line_state_control_channel_sender].
255//! 2. The receiver end of this [tokio::sync::mpsc::channel] is moved to the task that is
256//!    spawned by [Readline::new]. This is where the actual work is done when signals are
257//!    sent via the sender (described above).
258//!
259//! While the [Readline] is suspended, no input is possible, and only <kbd>Ctrl+C</kbd>
260//! and <kbd>Ctrl+D</kbd> are allowed to make it through, the rest of the keypresses are
261//! ignored.
262//!
263//! See [Readline] module docs for more implementation details on this.
264//!
265//! ## Input Editing Behavior
266//!
267//! While entering text, the user can edit and navigate through the current input line
268//! with the following key bindings:
269//!
270//! - Works on all platforms supported by `crossterm`.
271//! - Full Unicode Support (Including Grapheme Clusters).
272//! - Multiline Editing.
273//! - In-memory History.
274//! - Left, Right: Move cursor left/right.
275//! - Up, Down: Scroll through input history.
276//! - Ctrl-W: Erase the input from the cursor to the previous whitespace.
277//! - Ctrl-U: Erase the input before the cursor.
278//! - Ctrl-L: Clear the screen.
279//! - Ctrl-Left / Ctrl-Right: Move to previous/next whitespace.
280//! - Home: Jump to the start of the line.
281//!     - When the "emacs" feature (on by default) is enabled, Ctrl-A has the same effect.
282//! - End: Jump to the end of the line.
283//!     - When the "emacs" feature (on by default) is enabled, Ctrl-E has the same effect.
284//! - Ctrl-C, Ctrl-D: Send an `Eof` event.
285//! - Ctrl-C: Send an `Interrupt` event.
286//! - Extensible design based on `crossterm`'s `event-stream` feature.
287//!
288//! # Examples
289//!
290//! ```bash
291//! cargo run --example terminal_async
292//! cargo run --example spinner
293//! cargo run --example shell_async
294//! ```
295//!
296//! # How to use this crate
297//!
298//! ## [`TerminalAsync::try_new()`], which is the main entry point for most use cases
299//!
300//! 1. To read user input, call [`TerminalAsync::get_readline_event()`].
301//! 2. You can call [`TerminalAsync::clone_shared_writer()`] to get a
302//!    [`r3bl_core::SharedWriter`] instance that you can use to write to `stdout`
303//!    concurrently, using [`std::write!`] or [`std::writeln!`].
304//! 3. If you use [`std::writeln!`] then there's no need to [`TerminalAsync::flush()`]
305//!    because the `\n` will flush the buffer. When there's no `\n` in the buffer, or you
306//!    are using [`std::write!`] then you might need to call [`TerminalAsync::flush()`].
307//! 4. You can use the [`TerminalAsync::println`] and [`TerminalAsync::println_prefixed`]
308//!    methods to easily write concurrent output to the `stdout`
309//!    ([`r3bl_core::SharedWriter`]).
310//! 5. You can also get access to the underlying [`Readline`] via the
311//!    [`Readline::readline`] field. Details on this struct are listed below. For most use
312//!    cases you won't need to do this.
313//!
314//! ## [`Readline`] overview (please see the docs for this struct for details)
315//!
316//! - Structure for reading lines of input from a terminal while lines are output to the
317//!   terminal concurrently. It uses dependency injection, allowing you to supply
318//!   resources that can be used to:
319//!   1. Read input from the user, typically
320//!      [`crossterm::event::EventStream`](https://docs.rs/crossterm/latest/crossterm/event/struct.EventStream.html).
321//!   2. Generate output to the raw terminal, typically [`std::io::Stdout`].
322//!
323//! - Terminal input is retrieved by calling [`Readline::readline()`], which returns each
324//!   complete line of input once the user presses Enter.
325//!
326//! - Each [`Readline`] instance is associated with one or more
327//!   [`r3bl_core::SharedWriter`] instances. Lines written to an associated
328//!   [`r3bl_core::SharedWriter`] are output to the raw terminal.
329//!
330//! - Call [`Readline::new()`] to create a [`Readline`] instance and associated
331//!   [`r3bl_core::SharedWriter`].
332//!
333//! - Call [`Readline::readline()`] (most likely in a loop) to receive a line of input
334//!   from the terminal.  The user entering the line can edit their input using the key
335//!   bindings listed under "Input Editing" below.
336//!
337//! - After receiving a line from the user, if you wish to add it to the history (so that
338//!   the user can retrieve it while editing a later line), call
339//!   [`Readline::add_history_entry()`].
340//!
341//! - Lines written to the associated [`r3bl_core::SharedWriter`] while
342//!   `readline()` is in progress will be output to the screen above the input line.
343//!
344//! - When done, call [`crate::manage_shared_writer_output::flush_internal()`] to ensure
345//!   that all lines written to the [`r3bl_core::SharedWriter`] are output.
346//!
347//! ## [`Spinner::try_start()`]
348//!
349//! This displays an indeterminate spinner while waiting for a long-running task to
350//! complete. The intention with displaying this spinner is to give the user an indication
351//! that the program is still running and hasn't hung up or become unresponsive. When
352//! other tasks produce output concurrently, this spinner's output will not be clobbered.
353//! Neither will the spinner output clobber the output from other tasks. It suspends the
354//! output from all the [`r3bl_core::SharedWriter`] instances that are associated
355//! with one [`Readline`] instance. Both the `terminal_async.rs` and `spinner.rs` examples
356//! shows this (`cargo run --example terminal_async` and `cargo run --example spinner`).
357//!
358//! [`Spinner`]s also has cancellation support. Once a spinner is started,
359//! <kbd>Ctrl+C</kbd> and <kbd>Ctrl+D</kbd> are directed to the spinner, to cancel it.
360//! Spinners can also be checked for completion or cancellation by long running tasks, to
361//! ensure that they exit as a response to user cancellation. Take a look at the
362//! `examples/terminal_async.rs` file to get an understanding of how to use this API.
363//!
364//! The third change is that [`TerminalAsync::try_new()`] now accepts prompts that can
365//! have ANSI escape sequences in them. Here's an example of this.
366//!
367//! ```
368//! # use r3bl_terminal_async::TerminalAsync;
369//! # use crossterm::style::Stylize;
370//! # pub async fn sample() -> Result<(), Box<dyn std::error::Error>> {
371//!     let prompt = {
372//!         let user = "naz";
373//!         let prompt_seg_1 = "â•­".magenta().on_dark_grey().to_string();
374//!         let prompt_seg_2 = format!("┤{user}├").magenta().on_dark_grey().to_string();
375//!         let prompt_seg_3 = "â•®".magenta().on_dark_grey().to_string();
376//!         format!("{}{}{} ", prompt_seg_1, prompt_seg_2, prompt_seg_3)
377//!     };
378//!     let maybe_terminal_async = TerminalAsync::try_new(prompt.as_str()).await?;
379//!     let Some(mut terminal_async) = maybe_terminal_async else {
380//!         return Err(miette::miette!("Failed to create terminal").into());
381//!     };
382//!     Ok(())
383//! # }
384//! ```
385//!
386//! # Build this crate with Naz on YouTube
387//!
388//! Watch the following videos to learn more about how this crate was built:
389//!
390//! - [Part 1: Why?](https://youtu.be/6LhVx0xM86c)
391//! - [Part 2: What?](https://youtu.be/3vQJguti02I)
392//! - [Part 3: Do the refactor and rename the crate](https://youtu.be/uxgyZzOmVIw)
393//! - [Part 4: Build the spinner](https://www.youtube.com/watch?v=fcb6rstRniI)
394//! - [Part 5: Add color gradient animation to
395//!   spinner](https://www.youtube.com/watch?v=_QjsGDds270)
396//! - [Part 6: Publish the crate and overview](https://youtu.be/X5wDVaZENOo)
397//! - [Testing
398//!   playlist](https://www.youtube.com/watch?v=Xt495QLrFFk&list=PLofhE49PEwmwLR_4Noa0dFOSPmSpIg_l8)
399//!   - [Part 1: Intro](https://www.youtube.com/watch?v=Xt495QLrFFk)
400//!   - [Part 2: Deep dive](https://www.youtube.com/watch?v=4iM9t5dgvU4)
401//!
402//! The following playlists are relevant to this crate:
403//!
404//! - [Build with Naz, async readline and spinner for CLI in
405//!   Rust](https://www.youtube.com/watch?v=3vQJguti02I&list=PLofhE49PEwmwelPkhfiqdFQ9IXnmGdnSE)
406//! - [Build with Naz : Explore Linux TTY, process, signals w/
407//!   Rust](https://www.youtube.com/playlist?list=PLofhE49PEwmw3MKOU1Kn3xbP4FRQR4Mb3)
408//! - [Build with Naz, testing in
409//!   Rust](https://www.youtube.com/watch?v=Xt495QLrFFk&list=PLofhE49PEwmwLR_4Noa0dFOSPmSpIg_l8)
410//!
411//! # Why another async readline crate?
412//!
413//! This crate & repo is forked from
414//! [rustyline-async](https://github.com/zyansheep/rustyline-async). However it has mostly
415//! been rewritten and re-architected. Here are some changes made to the code:
416//!
417//! - Rearchitect the entire crate from the ground up to operate in a totally different
418//!   manner than the original. All the underlying mental models are different, and
419//!   simpler. The main event loop is redone. And a task is used to monitor the line
420//!   channel for communication between multiple [`r3bl_core::SharedWriter`]s and
421//!   the [`Readline`], to properly support pause and resume, and other control functions.
422//! - Drop support for all async runtimes other than `tokio`. Rewrite all the code for
423//!   this.
424//! - Drop crates like `pin-project`, `thingbuf` in favor of `tokio`. Rewrite all the code
425//!   for this.
426//! - Drop `simplelog` and `log` dependencies. Add support for `tokio-tracing`. Rewrite
427//!   all the code for this, and add `tracing_setup.rs`.
428//! - Remove all examples and create new ones to mimic a real world CLI application.
429//! - Add `spinner_impl`, `readline_impl`, and `public_api` modules.
430//! - Add tests.
431//!
432//! ## References for blocking and thread cancellation in Rust
433//! <a href="markdown-references-for-blocking-and-thread-cancellation-in-rust"
434//! name="references-for-blocking-and-thread-cancellation-in-rust"></a>
435//!
436//! - [Docs: tokio's `stdin`](https://docs.rs/tokio/latest/tokio/io/struct.Stdin.html)
437//! - [Discussion: Stopping a thread in
438//!   Rust](https://users.rust-lang.org/t/stopping-a-thread/6328/7)
439//! - [Discussion: Support for
440//!   `Thread::cancel()`](https://internals.rust-lang.org/t/thread-cancel-support/3056/16)
441//! - [Discussion: stdin, stdout redirection for spawned
442//!   processes](https://stackoverflow.com/questions/34611742/how-do-i-read-the-output-of-a-child-process-without-blocking-in-rust)
443//!
444//! ## Educational references for Linux TTY and async Rust
445//! <a href="markdown-educational-references-for-linux-tty-and-async-rust"
446//! name="educational-references-for-linux-tty-and-async-rust"></a>
447//!
448//! - [Linux TTY and async Rust - Article on
449//!   developerlife.com](https://developerlife.com/2024/08/20/tty-linux-async-rust/)
450//! - [Linux TTY and async Rust - Playlist on developerlife.com YT
451//!   channel](https://www.youtube.com/watch?v=bolScvh4x7I&list=PLofhE49PEwmw3MKOU1Kn3xbP4FRQR4Mb3)
452
453// Skip rustfmt for rest of file.
454// https://stackoverflow.com/a/75910283/2085356
455#![cfg_attr(rustfmt, rustfmt_skip)]
456
457// Attach sources.
458pub mod public_api;
459pub mod readline_impl;
460pub mod spinner_impl;
461
462// Re-export the public API.
463pub use public_api::*;
464pub use readline_impl::*;
465pub use spinner_impl::*;
466
467// External crates.
468use std::{collections::VecDeque, sync::Arc};
469
470// r3bl-open-core crates.
471use r3bl_core::{StdMutex, SendRawTerminal, SafeRawTerminal};
472
473// Type aliases.
474pub type SafeLineState = Arc<StdMutex<LineState>>;
475pub type SafeHistory = Arc<StdMutex<History>>;
476
477pub type SafeBool = Arc<StdMutex<bool>>;
478
479pub type PauseBuffer = VecDeque<r3bl_core::Text>;
480pub type SafePauseBuffer = Arc<StdMutex<PauseBuffer>>;
481
482// Constants.
483pub const CHANNEL_CAPACITY: usize = 1_000;
484pub const HISTORY_SIZE_MAX: usize = 1_000;