From b7ea1908bd3250641e25fa44a923e548c62b2868 Mon Sep 17 00:00:00 2001 From: Yaroslav Bolyukin Date: Thu, 12 Mar 2026 03:41:06 +0000 Subject: [PATCH] feat: prodash --- --- /dev/null +++ b/crates/prodash-layer/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "prodash-layer" +version.workspace = true +edition.workspace = true +rust-version.workspace = true + +[dependencies] +futures.workspace = true +futures-core = "0.3.32" +prodash = { version = "31.0.0", features = ["render-tui", "render-tui-crossterm"] } +tokio.workspace = true +tracing.workspace = true +tracing-subscriber.workspace = true --- /dev/null +++ b/crates/prodash-layer/src/lib.rs @@ -0,0 +1,161 @@ +use std::collections::HashMap; +use std::marker::PhantomData; +use std::mem::forget; +use std::sync::{Arc, RwLock}; +use std::time::Duration; + +use futures::StreamExt as _; +use futures::stream::empty; +use futures_core::stream; +use prodash::messages::MessageLevel; +use prodash::render::tui::{self, ticker}; +use prodash::tree::{Item, Root}; +use prodash::unit::{Kind, label}; +use prodash::{Progress, Unit}; +use tokio::join; +use tokio::time::sleep; +use tracing::{Event, Instrument, Level, Span, Subscriber, info, info_span, span, trace}; +use tracing_subscriber::EnvFilter; +use tracing_subscriber::layer::{self, SubscriberExt as _}; +use tracing_subscriber::registry::LookupSpan; +use tracing_subscriber::util::SubscriberInitExt; + +pub(crate) struct WithContext(fn(&tracing::Dispatch, &span::Id, f: &mut dyn FnMut(&mut Item))); + +pub struct ProdashLayer { + root: Arc, + root_logs: Item, + with_context: WithContext, + + _marker: PhantomData, +} + +impl ProdashLayer +where + S: Subscriber + for<'a> LookupSpan<'a>, +{ + pub fn new(root: Arc) -> Self { + let root_logs = root.add_child("root"); + Self { + root, + root_logs, + with_context: WithContext(Self::get_context), + _marker: PhantomData, + } + } + + fn get_context(dispatch: &tracing::Dispatch, id: &span::Id, f: &mut dyn FnMut(&mut Item)) { + let subscriber = dispatch + .downcast_ref::() + .expect("subscriber should downcast to expected type"); + let span = subscriber.span(id).expect("span should be in context"); + let span = span.as_ref().map(|s| s.extensions()); + let span = span + .as_ref() + .map(|e| e.get::().expect("existence checked")); + } +} + +impl layer::Layer for ProdashLayer +where + S: Subscriber + for<'a> LookupSpan<'a>, +{ + fn on_event(&self, event: &Event<'_>, ctx: layer::Context<'_, S>) { + let span = ctx.current_span(); + let cur_span = span + .id() + .and_then(|s| ctx.span_scope(s)) + .and_then(|mut scope| { + scope.find(|span| { + let ext = span.extensions(); + ext.get::().is_some() + }) + }); + let cur_span = cur_span.as_ref().map(|s| s.extensions()); + let cur_span = cur_span + .as_ref() + .map(|e| e.get::().expect("existence checked")); + + let cur_span = cur_span.unwrap_or(&self.root_logs); + cur_span.info("hello".to_owned()); + } + fn on_new_span(&self, attrs: &span::Attributes<'_>, id: &span::Id, ctx: layer::Context<'_, S>) { + let span = ctx.span(id).expect("span should be in context"); + let mut ext = span.extensions_mut(); + ext.insert(self.with_context); + + let mut spans = self.spans.write().expect("not poisoned"); + let name = attrs.metadata().name(); + if let Some(parent) = attrs + .parent() + .cloned() + .or_else(|| { + if attrs.is_contextual() { + ctx.current_span().id().cloned() + } else { + None + } + }) + .and_then(|v| spans.get_mut(&v)) + { + let child = parent.add_child(name); + spans.insert(id.clone(), child); + } else { + let child = self.root.add_child(name); + spans.insert(id.clone(), child); + }; + } + fn on_close(&self, id: span::Id, _ctx: layer::Context<'_, S>) { + let mut spans = self.spans.write().expect("not poisoned"); + spans.remove(&id); + } +} + +// #[tokio::test] +// async fn test() { +// let root: Arc = prodash::tree::root::Options { +// message_buffer_capacity: 1000, +// ..Default::default() +// } +// .create() +// .into(); +// +// let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); +// let reg = tracing_subscriber::registry() +// .with(filter) +// .with(ProdashLayer::new(root.clone())); +// reg.init(); +// +// let render = tui::render( +// std::io::stdout(), +// Arc::downgrade(&root), +// tui::Options { +// frames_per_second: 10.0, +// ..tui::Options::default() +// }, +// ) +// .expect("render"); +// +// let render = tokio::task::spawn(render); +// +// loop { +// info!("Hello, world!"); +// sleep(Duration::from_secs(3)) +// .instrument(info_span!("sleeping root")) +// .await; +// async { +// sleep(Duration::from_secs(3)) +// .instrument(info_span!("sleeping 3")) +// .await; +// sleep(Duration::from_secs(2)) +// .instrument(info_span!("sleeping 2")) +// .await; +// } +// .instrument(info_span!("sleep parent")) +// .await; +// } +// +// // loop { +// // sleep(Duration::from_secs(3)); +// render.await; +// } -- gitstuff