scena 1.7.2

A Rust-native scene-graph renderer with typed scene state, glTF assets, and explicit prepare/render lifecycles.
Documentation
use serde::{Deserialize, Serialize};

use crate::scene::NodeKey;

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Diagnostic {
    pub code: DiagnosticCode,
    pub severity: DiagnosticSeverity,
    pub message: String,
    pub help: Option<String>,
    #[serde(skip)]
    context: DiagnosticContext,
}

#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct DiagnosticContext {
    node: Option<NodeKey>,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DiagnosticCode {
    MissingActiveCamera,
    InvalidCameraProjection,
    ObjectsBehindCamera,
    SceneOutsideCameraFrustum,
    InvisibleScene,
    MissingLightingOrEnvironment,
    LargeScenePrecisionRisk,
    DepthPrecisionRisk,
    WebGl2DepthCompatibility,
    ForwardPbrDegraded,
    MaterialPresetFallback,
    DirectionalShadowsDegraded,
    PointShadowsDisabled,
    SpotShadowsDisabled,
    BloomDisabled,
    AmbientOcclusionDisabled,
    OrderIndependentTransparencyDisabled,
    PhysicalGlassTransmissionDegraded,
    WideGamutOutputUnavailable,
    GpuCullingDisabled,
    MaterialTextureMissingDecodedPixels,
    DestructionQueuePressure,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DiagnosticSeverity {
    Info,
    Warning,
    Error,
}

impl Diagnostic {
    pub fn code(&self) -> DiagnosticCode {
        self.code
    }

    pub fn severity(&self) -> DiagnosticSeverity {
        self.severity
    }

    pub fn message(&self) -> &str {
        &self.message
    }

    pub fn help(&self) -> Option<&str> {
        self.help.as_deref()
    }

    pub fn suggested_fix(&self) -> Option<&str> {
        self.help()
    }

    pub fn context(&self) -> DiagnosticContext {
        self.context
    }

    pub fn node(&self) -> Option<NodeKey> {
        self.context.node()
    }

    pub fn info(code: DiagnosticCode, message: impl Into<String>, help: impl Into<String>) -> Self {
        Self {
            code,
            severity: DiagnosticSeverity::Info,
            message: message.into(),
            help: Some(help.into()),
            context: DiagnosticContext::default(),
        }
    }

    pub fn warning(
        code: DiagnosticCode,
        message: impl Into<String>,
        help: impl Into<String>,
    ) -> Self {
        Self {
            code,
            severity: DiagnosticSeverity::Warning,
            message: message.into(),
            help: Some(help.into()),
            context: DiagnosticContext::default(),
        }
    }

    pub fn error(
        code: DiagnosticCode,
        message: impl Into<String>,
        help: impl Into<String>,
    ) -> Self {
        Self {
            code,
            severity: DiagnosticSeverity::Error,
            message: message.into(),
            help: Some(help.into()),
            context: DiagnosticContext::default(),
        }
    }

    pub(crate) fn warning_for_node(
        code: DiagnosticCode,
        node: NodeKey,
        message: impl Into<String>,
        help: impl Into<String>,
    ) -> Self {
        Self::warning(code, message, help).with_node(node)
    }

    pub(crate) fn error_for_node(
        code: DiagnosticCode,
        node: NodeKey,
        message: impl Into<String>,
        help: impl Into<String>,
    ) -> Self {
        Self::error(code, message, help).with_node(node)
    }

    fn with_node(mut self, node: NodeKey) -> Self {
        self.context = DiagnosticContext { node: Some(node) };
        self
    }
}

impl DiagnosticContext {
    pub const fn node(self) -> Option<NodeKey> {
        self.node
    }
}