Skip to content

Instantly share code, notes, and snippets.

@RandyMcMillan
Created March 12, 2025 20:50
Show Gist options
  • Save RandyMcMillan/1dad3e9e5fef385353f8c6dc07cc090e to your computer and use it in GitHub Desktop.
Save RandyMcMillan/1dad3e9e5fef385353f8c6dc07cc090e to your computer and use it in GitHub Desktop.
crossterm-lifecycle
// Cargo.toml
// [package]
// name = "crossterm-lifecycle"
// version = "0.1.0"
// edition = "2021"
//
// [dependencies]
// tokio = { version = "1.44.0", features = ["full"] }
// ratatui = "0.26"
// crossterm = "0.27"
use tokio::io::{AsyncBufReadExt, BufReader};
use tokio::process::Command;
use tokio::spawn;
use ratatui::{/*backend::Backend,*/ Terminal};
use std::io;
async fn pre() -> Result<(), Box<dyn std::error::Error>> {
let mut child = Command::new("echo")
.arg("pre")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
let stdout = child.stdout.take().expect("stdout was not configured");
let stderr = child.stderr.take().expect("stderr was not configured");
let stdout_reader = BufReader::new(stdout);
let stderr_reader = BufReader::new(stderr);
let stdout_handle = spawn(async move {
let mut lines = stdout_reader.lines();
while let Some(line) = lines.next_line().await.unwrap_or(Some("0".to_string())) {
println!("{}", line);
}
});
let stderr_handle = spawn(async move {
let mut lines = stderr_reader.lines();
while let Some(line) = lines.next_line().await.unwrap_or(Some("0".to_string())) {
eprintln!("stderr: {}", line);
}
});
let _status = child.wait().await?;
stdout_handle.await?;
stderr_handle.await?;
//println!("Exited with status: {}", _status);
let option_handle = spawn(async move {
process_options(Some(10), Some("pre-hello".to_string())).await;
});
option_handle.await?;
let option_handle = spawn(async move {
process_options(Some(20), None).await;
});
option_handle.await?;
let option_handle = spawn(async move {
process_options(None, Some("pre-world".to_string())).await;
});
option_handle.await?;
Ok(())
}
#[tokio::main]
async fn main() -> Result<(), io::Error> {
let _do_it_handle = pre().await; //print before ratatui
// Setup terminal
let backend = ratatui::backend::CrosstermBackend::new(io::stdout());
let mut terminal = Terminal::new(backend)?;
terminal.hide_cursor()?;
crossterm::execute!(
terminal.backend_mut(),
crossterm::terminal::EnterAlternateScreen,
crossterm::cursor::MoveTo(0, 0)
)?;
terminal.clear()?;
let _do_it_handle = mid().await;
// Your ratatui drawing code here (e.g., terminal.draw(...))
terminal.draw(|f| {
use ratatui::{/*prelude::*,*/ widgets::*};
let block = Block::default()
.title(" crossterm lifecycle (alternate screen) ")
.borders(Borders::ALL);
f.render_widget(block, f.size());
})?;
//let do_it_handle = do_it().await;//prints below ratatui screen
// Wait for a key press before exiting
if crossterm::event::poll(std::time::Duration::from_millis(5000))? {
crossterm::event::read()?;
}
// Restore terminal settings and exit alternate screen
crossterm::execute!(
terminal.backend_mut(),
crossterm::terminal::LeaveAlternateScreen,
crossterm::cursor::Show
)?;
let _do_it_handle = post().await; //prints below ratatui screen after exit
Ok(())
}
async fn mid() -> Result<(), Box<dyn std::error::Error>> {
println!("");
let mut child = Command::new("ls")
.arg("-l")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
let stdout = child.stdout.take().expect("stdout was not configured");
let stderr = child.stderr.take().expect("stderr was not configured");
let stdout_reader = BufReader::new(stdout);
let stderr_reader = BufReader::new(stderr);
let stdout_handle = spawn(async move {
let mut lines = stdout_reader.lines();
while let Some(line) = lines.next_line().await.unwrap_or(Some("0".to_string())) {
//spacing for ratatui border
println!(" {}", line);
}
});
let stderr_handle = spawn(async move {
let mut lines = stderr_reader.lines();
while let Some(line) = lines.next_line().await.unwrap_or(Some("0".to_string())) {
eprintln!("stderr: {}", line);
}
});
let _status = child.wait().await?;
stdout_handle.await?;
stderr_handle.await?;
//println!("Exited with status: {}", _status);
let option_handle = spawn(async move {
process_options(Some(10), Some("mid-hello".to_string())).await;
});
option_handle.await?;
let option_handle = spawn(async move {
process_options(Some(20), None).await;
});
option_handle.await?;
let option_handle = spawn(async move {
process_options(None, Some("mid-world".to_string())).await;
});
option_handle.await?;
Ok(())
}
async fn post() -> Result<(), Box<dyn std::error::Error>> {
let mut child = Command::new("echo")
.arg("post")
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()?;
let stdout = child.stdout.take().expect("stdout was not configured");
let stderr = child.stderr.take().expect("stderr was not configured");
let stdout_reader = BufReader::new(stdout);
let stderr_reader = BufReader::new(stderr);
let stdout_handle = spawn(async move {
let mut lines = stdout_reader.lines();
while let Some(line) = lines.next_line().await.unwrap_or(Some("0".to_string())) {
println!("{}", line);
}
});
let stderr_handle = spawn(async move {
let mut lines = stderr_reader.lines();
while let Some(line) = lines.next_line().await.unwrap_or(Some("0".to_string())) {
eprintln!("stderr: {}", line);
}
});
let _status = child.wait().await?;
stdout_handle.await?;
stderr_handle.await?;
//println!("Exited with status: {}", _status);
let option_handle = spawn(async move {
process_options(Some(10), Some("post-hello".to_string())).await;
});
option_handle.await?;
let option_handle = spawn(async move {
process_options(Some(20), None).await;
});
option_handle.await?;
let option_handle = spawn(async move {
process_options(None, Some("post-world".to_string())).await;
});
option_handle.await?;
Ok(())
}
#[allow(unused_assignments)]
async fn process_options(mut opt1: Option<i32>, mut opt2: Option<String>) {
let mut o1 = opt1.clone();
let mut o2 = opt2.clone();
while o1.is_some() || o2.is_some() {
match (o1.take(), o2.take()) {
(Some(val1), Some(val2)) => {
//prints twice - once for each option
println!(" Both Some: {}, {}", val1, val2);
// process both values
}
(Some(val1), None) => {
println!(" Opt1 Some: {}", val1);
// process opt1
}
(None, Some(val2)) => {
println!(" Opt2 Some: {}", val2);
// process opt2
}
(None, None) => {
//should not happen since we already checked is_some() above.
}
}
// o1 = opt1;
// o2 = opt2;
opt1 = None;
opt2 = None;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment