next.js/turbopack/crates/turbopack-core/src/diagnostics/mod.rs
mod.rs119 lines3.5 KB
use std::cmp::Ordering;

use anyhow::Result;
use async_trait::async_trait;
use auto_hash_map::AutoSet;
use turbo_rcstr::RcStr;
use turbo_tasks::{CollectiblesSource, FxIndexMap, ResolvedVc, Upcast, Vc, emit};

#[turbo_tasks::value(serialization = "skip")]
#[derive(Clone, Debug)]
pub struct PlainDiagnostic {
    pub category: RcStr,
    pub name: RcStr,
    pub payload: FxIndexMap<RcStr, RcStr>,
}

impl Ord for PlainDiagnostic {
    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
        self.name
            .cmp(&other.name)
            .then_with(|| self.category.cmp(&other.category))
            .then_with(|| self.payload.len().cmp(&other.payload.len()))
            .then_with(|| {
                for ((a_key, a_value), (b_key, b_value)) in
                    self.payload.iter().zip(other.payload.iter())
                {
                    match a_key.cmp(b_key) {
                        Ordering::Equal => {}
                        other => return other,
                    }
                    match a_value.cmp(b_value) {
                        Ordering::Equal => {}
                        other => return other,
                    }
                }
                Ordering::Equal
            })
    }
}

impl PartialOrd for PlainDiagnostic {
    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
        Some(self.cmp(other))
    }
}

#[turbo_tasks::value(transparent)]
pub struct DiagnosticPayload(
    #[bincode(with = "turbo_bincode::indexmap")] pub FxIndexMap<RcStr, RcStr>,
);

/// An arbitrary payload can be used to analyze, diagnose Turbopack's behavior.
#[turbo_tasks::value_trait]
pub trait Diagnostic {
    /// **NOTE:** Pseudo-reserved; this is not being used currently. The `type` of the diagnostics
    /// that can be used selectively filtered by consumers. For example, this could be `telemetry`,
    /// or `slow_perf_event`, or something else. This is not strongly typed though; since consumer
    /// or implementation may need to define own category.
    #[turbo_tasks::function]
    fn category(&self) -> Vc<RcStr>;
    /// Name of the specific diagnostic event.
    #[turbo_tasks::function]
    fn name(&self) -> Vc<RcStr>;
    /// Arbitrary payload included in the diagnostic event.
    #[turbo_tasks::function]
    fn payload(&self) -> Vc<DiagnosticPayload>;

    #[turbo_tasks::function]
    async fn into_plain(self: Vc<Self>) -> Result<Vc<PlainDiagnostic>> {
        Ok(PlainDiagnostic {
            category: self.category().owned().await?,
            name: self.name().owned().await?,
            payload: self.payload().owned().await?,
        }
        .cell())
    }
}

pub trait DiagnosticExt {
    fn emit(self);
}

impl<T> DiagnosticExt for ResolvedVc<T>
where
    T: Upcast<Box<dyn Diagnostic>>,
{
    fn emit(self) {
        let diagnostic = ResolvedVc::upcast_non_strict::<Box<dyn Diagnostic>>(self);
        emit(diagnostic);
    }
}

#[async_trait]
pub trait DiagnosticContextExt
where
    Self: Sized,
{
    async fn peek_diagnostics(self) -> Result<CapturedDiagnostics>;
}

#[async_trait]
impl<T> DiagnosticContextExt for T
where
    T: CollectiblesSource + Copy + Send,
{
    async fn peek_diagnostics(self) -> Result<CapturedDiagnostics> {
        Ok(CapturedDiagnostics {
            diagnostics: self.peek_collectibles(),
        })
    }
}

/// A list of diagnostics captured with [`DiagnosticContextExt::peek_diagnostics`] and
#[derive(Debug)]
#[turbo_tasks::value]
pub struct CapturedDiagnostics {
    pub diagnostics: AutoSet<ResolvedVc<Box<dyn Diagnostic>>>,
}
Quest for Codev2.0.0
/
SIGN IN