rm-lisa 0.3.2

A logging library for rem-verse, with support for inputs, tasks, and more.
Documentation
use parking_lot::RwLock;
use rm_lisa::{
	initialize_logging,
	tasks::{LisaTaskId, LisaTaskStatus, TaskEvent, TaskEventLogProvider, ThreadOrTokioTaskId},
};
use std::{
	sync::{
		Arc,
		atomic::{AtomicU8, Ordering},
	},
	time::Duration,
};
use tokio::{signal::ctrl_c, time::sleep};
use tracing::info;

#[tokio::main]
pub async fn main() {
	let task_provider = GlobalTaskList::spawn_tasks();
	let console = initialize_logging("task-display").expect("Failed to initialize logging!");
	console.set_task_provider(task_provider.clone());

	loop {
		if task_provider.tasks_running() {
			info!("[/] Waiting for tasks to complete!");
		} else {
			info!("[+] Done!");
			break;
		}

		tokio::select! {
			_ = sleep(Duration::from_secs(1)) => {}
			_ = ctrl_c() => {
				break;
			}
		}
	}

	console.flush().await;
}

/// A simple global task list of events:
///
/// 1. Events to share with lisa.
/// 2. The # of tasks actively running.
#[derive(Clone)]
struct GlobalTaskList(Arc<RwLock<Vec<TaskEvent>>>, Arc<AtomicU8>);

impl GlobalTaskList {
	pub fn spawn_tasks() -> Self {
		let events = Arc::new(RwLock::new(Vec::new()));
		let tasks_left = Arc::new(AtomicU8::new(5));

		Self::simple_bg_task(
			(1, 1),
			"smol computation".to_owned(),
			vec![(
				Duration::from_secs(1),
				LisaTaskStatus::Waiting(Some("cleanup".to_owned())),
			)],
			events.clone(),
			tasks_left.clone(),
		);
		Self::simple_bg_task(
			(1, 2),
			"fetch".to_owned(),
			vec![
				(
					Duration::from_secs(1),
					LisaTaskStatus::Waiting(Some("network response".to_owned())),
				),
				(
					Duration::from_secs(1),
					LisaTaskStatus::Running(Some("parsing network response".to_owned())),
				),
				(
					Duration::from_secs(3),
					LisaTaskStatus::Waiting(Some("cleanup".to_owned())),
				),
			],
			events.clone(),
			tasks_left.clone(),
		);
		Self::simple_bg_task(
			(1, 3),
			"processing".to_owned(),
			vec![(
				Duration::from_secs(5),
				LisaTaskStatus::Waiting(Some("cleanup".to_owned())),
			)],
			events.clone(),
			tasks_left.clone(),
		);
		Self::simple_bg_task(
			(2, 1),
			"smol 2".to_owned(),
			vec![(
				Duration::from_secs(1),
				LisaTaskStatus::Waiting(Some("cleanup".to_owned())),
			)],
			events.clone(),
			tasks_left.clone(),
		);
		Self::simple_bg_task(
			(3, 1),
			"smol 3".to_owned(),
			vec![(
				Duration::from_secs(1),
				LisaTaskStatus::Waiting(Some("cleanup".to_owned())),
			)],
			events.clone(),
			tasks_left.clone(),
		);

		Self(events, tasks_left)
	}

	#[must_use]
	pub fn tasks_running(&self) -> bool {
		self.1.load(Ordering::SeqCst) != 0
	}

	fn simple_bg_task(
		ids: (u64, u64),
		name: String,
		task_waits: Vec<(Duration, LisaTaskStatus)>,
		events: Arc<RwLock<Vec<TaskEvent>>>,
		tasks: Arc<AtomicU8>,
	) {
		_ = tokio::task::spawn(async move {
			sleep(Duration::from_secs(1)).await;

			{
				let mut event_lock = events.write();
				event_lock.push(TaskEvent::TaskStart(
					ThreadOrTokioTaskId(ids.0),
					LisaTaskId(ids.1),
					name,
					LisaTaskStatus::Running(None),
				));
			}

			for (wait_before, status_after) in task_waits {
				sleep(wait_before).await;
				{
					let mut event_lock = events.write();
					event_lock.push(TaskEvent::TaskStatusUpdate(
						ThreadOrTokioTaskId(ids.0),
						LisaTaskId(ids.1),
						status_after,
					));
				}
			}

			sleep(Duration::from_secs(1)).await;

			{
				let mut event_lock = events.write();
				event_lock.push(TaskEvent::TaskEnd(
					ThreadOrTokioTaskId(ids.0),
					LisaTaskId(ids.1),
				));
			}
			_ = tasks.fetch_sub(1, Ordering::SeqCst);
		});
	}
}

impl TaskEventLogProvider for GlobalTaskList {
	fn new_events(&self) -> Vec<TaskEvent> {
		let events = {
			let mut locked = self.0.write();
			locked.drain(..).collect::<Vec<_>>()
		};

		events
	}
}