use hackshell::{
AsyncCommand, Command, CommandResult, Hackshell, TaskOptions, async_trait,
error::HackshellError,
};
use std::sync::{Arc, Mutex};
use std::time::Duration;
use tokio::time::sleep;
struct AppContext {
messages: Arc<Mutex<Vec<String>>>,
}
impl AppContext {
fn new() -> Self {
Self {
messages: Arc::new(Mutex::new(Vec::new())),
}
}
}
struct AsyncTaskCommand {
ctx: Arc<AppContext>,
}
impl Command for AsyncTaskCommand {
fn commands(&self) -> &'static [&'static str] {
&["async-task", "at"]
}
fn help(&self) -> &'static str {
"async-task <name> [count] - Spawn an async task using task management"
}
fn run(&self, shell: &Hackshell, args: &[&str]) -> CommandResult {
if args.len() < 2 {
println!("Usage: async-task <name> [count]");
return Ok(None);
}
let task_name = args[1].to_string();
let count = args
.get(2)
.and_then(|s| s.parse::<u32>().ok())
.unwrap_or(10);
let messages = self.ctx.messages.clone();
let task_name_clone = task_name.clone();
shell.spawn_async(&task_name, TaskOptions::default(), async move {
println!(
"Async task '{}' started (counting to {})",
task_name_clone, count
);
for i in 1..=count {
sleep(Duration::from_millis(500)).await;
let mut msgs = messages.lock().unwrap();
msgs.push(format!("Task '{}': {}/{}", task_name_clone, i, count));
if i % 5 == 0 {
println!("Task '{}': reached {}/{}", task_name_clone, i, count);
}
}
println!("Task '{}' finished counting to {}!", task_name_clone, count);
None
});
println!("Spawned async task: '{}'", task_name);
Ok(None)
}
}
struct DelayCommand;
#[async_trait]
impl AsyncCommand for DelayCommand {
fn commands(&self) -> &'static [&'static str] {
&["delay", "d"]
}
fn help(&self) -> &'static str {
"delay <seconds> - Async command that waits for the specified duration"
}
async fn run(&self, _shell: &Hackshell, args: &[&str]) -> CommandResult {
let seconds: f64 = args.get(1).and_then(|s| s.parse().ok()).unwrap_or(1.0);
println!("Waiting for {} seconds...", seconds);
sleep(Duration::from_secs_f64(seconds)).await;
Ok(Some(format!("Done waiting {} seconds!", seconds)))
}
}
struct CheckProgressCommand {
ctx: Arc<AppContext>,
}
impl Command for CheckProgressCommand {
fn commands(&self) -> &'static [&'static str] {
&["progress", "p"]
}
fn help(&self) -> &'static str {
"progress - Check progress of async tasks"
}
fn run(&self, shell: &Hackshell, _args: &[&str]) -> CommandResult {
let messages = self.ctx.messages.lock().unwrap();
println!("Current Status:");
println!(" Active tasks: {}", shell.get_tasks().len());
if !messages.is_empty() {
println!("\nRecent messages:");
for (i, msg) in messages.iter().rev().take(5).enumerate() {
println!(" {}. {}", i + 1, msg);
}
}
Ok(None)
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
println!("Hackshell Async Tasks Example");
println!("Type 'help' to see available commands\n");
let context = Arc::new(AppContext::new());
let shell = Hackshell::new("async> ")?;
shell.set_history_file("history.txt")?;
shell.add_command(AsyncTaskCommand {
ctx: context.clone(),
});
shell.add_command(CheckProgressCommand {
ctx: context.clone(),
});
shell.add_async_command(DelayCommand);
loop {
match shell.run_async().await {
Ok(Some(output)) => println!("{}", output),
Ok(None) => {}
Err(e) => {
if matches!(e, HackshellError::Eof)
|| matches!(e, HackshellError::Interrupted)
|| matches!(e, HackshellError::Exit)
{
println!("\nGoodbye!");
break;
}
println!("Error: {}", e);
}
}
}
Ok(())
}