katana-document-viewer 0.1.4

KatanA document viewer artifact, render evaluation, and export foundation.
Documentation
use crate::artifact::{
    Artifact, ArtifactBytes, ArtifactDiagnostic, ArtifactDiagnostics, ArtifactFactory,
    ArtifactFormat, DiagnosticSeverity,
};
use crate::document::{DocumentId, SourceRevision};
use katana_markdown_model::DiagramKind as KmmDiagramKind;
use katana_render_runtime::{
    DiagramKind as KrrDiagramKind, RenderConfig, RenderContext, RenderInput,
    RenderOutput as KrrRenderOutput, RenderPolicy,
};

const KRR_BACKEND: &str = "katana-render-runtime";

pub struct KrrDiagramInputFactory;
pub struct KrrRenderOutputFactory;

impl KrrDiagramInputFactory {
    pub fn create(kind: KmmDiagramKind, source: String, context: RenderContext) -> RenderInput {
        RenderInput {
            kind: Self::diagram_kind(kind),
            source,
            config: RenderConfig::default(),
            policy: RenderPolicy::default(),
            context,
        }
    }

    fn diagram_kind(kind: KmmDiagramKind) -> KrrDiagramKind {
        match kind {
            KmmDiagramKind::Mermaid => KrrDiagramKind::Mermaid,
            KmmDiagramKind::DrawIo => KrrDiagramKind::Drawio,
            KmmDiagramKind::PlantUml => KrrDiagramKind::PlantUml,
        }
    }
}

impl KrrRenderOutputFactory {
    pub fn artifact_from_render(
        output: &KrrRenderOutput,
        document_id: DocumentId,
        source_revision: SourceRevision,
    ) -> Artifact {
        ArtifactFactory::image_with_backend(
            ArtifactFormat::Svg,
            document_id,
            source_revision,
            ArtifactBytes {
                bytes: output.svg.as_bytes().to_vec(),
            },
            KRR_BACKEND,
            Self::diagnostics(output),
        )
    }

    fn diagnostics(output: &KrrRenderOutput) -> ArtifactDiagnostics {
        let warning_entries = output
            .diagnostics
            .warnings
            .iter()
            .map(|message| Self::diagnostic(DiagnosticSeverity::Warning, message));
        let error_entries = output
            .diagnostics
            .errors
            .iter()
            .map(|message| Self::diagnostic(DiagnosticSeverity::Error, message));
        ArtifactDiagnostics {
            entries: warning_entries.chain(error_entries).collect(),
        }
    }

    fn diagnostic(severity: DiagnosticSeverity, message: &str) -> ArtifactDiagnostic {
        ArtifactDiagnostic {
            severity,
            code: "krr-render-diagnostic".to_string(),
            message: message.to_string(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::artifact::ArtifactKind;
    use katana_render_runtime::{RenderDiagnostics, RendererProfile, RuntimeVersion};

    #[test]
    fn maps_kmm_drawio_to_krr_drawio() {
        let input = KrrDiagramInputFactory::create(
            KmmDiagramKind::DrawIo,
            "<mxfile />".to_string(),
            RenderContext::default(),
        );

        assert_eq!(input.kind, KrrDiagramKind::Drawio);
    }

    #[test]
    fn maps_kmm_plantuml_to_krr_plantuml() {
        let input = KrrDiagramInputFactory::create(
            KmmDiagramKind::PlantUml,
            "@startuml".to_string(),
            RenderContext::default(),
        );

        assert_eq!(input.kind, KrrDiagramKind::PlantUml);
    }

    #[test]
    fn converts_krr_render_output_to_svg_image_artifact() {
        let artifact = KrrRenderOutputFactory::artifact_from_render(
            &render_output(),
            DocumentId("doc".to_string()),
            SourceRevision("rev".to_string()),
        );

        assert_eq!(artifact.manifest.backend, KRR_BACKEND);
        assert_eq!(artifact.manifest.kind, ArtifactKind::Image);
        assert_eq!(artifact.manifest.format, ArtifactFormat::Svg);
        assert_eq!(artifact.manifest.diagnostics.entries.len(), 2);
    }

    fn render_output() -> KrrRenderOutput {
        KrrRenderOutput {
            svg: "<svg />".to_string(),
            width: 1.0,
            height: 1.0,
            view_box: "0 0 1 1".to_string(),
            runtime: RuntimeVersion {
                name: "test".to_string(),
                version: "0".to_string(),
                checksum: None,
            },
            profile: RendererProfile {
                id: "test".to_string(),
                description: None,
            },
            diagnostics: RenderDiagnostics {
                warnings: vec!["warn".to_string()],
                errors: vec!["err".to_string()],
            },
            cache_fingerprint: "fp".to_string(),
        }
    }
}