katana_document_viewer/
forge.rs1use crate::artifact::{ArtifactBytes, ArtifactFactory};
2use crate::export_payload::ExportPayloadFactory;
3pub use crate::forge_types::{
4 BuildGraph, BuildProfile, BuildRequest, ExportFormat, ExportOutput, ExportRequest,
5 ForgeBackend, ForgeDiagnostics, ForgeError, MarkdownEvaluationTarget, RenderedDiagram,
6 TransformStep,
7};
8
9pub struct ForgePipeline<B> {
10 backend: B,
11}
12
13impl BuildProfile {
14 pub fn markdown_export() -> Self {
15 Self {
16 evaluation_targets: vec![
17 MarkdownEvaluationTarget::CommonMark,
18 MarkdownEvaluationTarget::Gfm,
19 MarkdownEvaluationTarget::Math,
20 MarkdownEvaluationTarget::GitHubAlert,
21 MarkdownEvaluationTarget::KatanaCompatibility,
22 MarkdownEvaluationTarget::ExternalRendering,
23 ],
24 transform_steps: vec![
25 TransformStep::EvaluateMarkdown,
26 TransformStep::RenderDiagrams,
27 TransformStep::BuildArtifactManifest,
28 ],
29 }
30 }
31}
32
33impl BuildGraph {
34 pub fn from_request(request: &BuildRequest) -> Self {
35 Self {
36 snapshot: request.snapshot.clone(),
37 profile: request.profile.clone(),
38 theme: request.theme.clone(),
39 diagnostics: ForgeDiagnostics {
40 messages: Vec::new(),
41 },
42 rendered_diagrams: Vec::new(),
43 }
44 }
45
46 pub fn with_rendered_diagrams(mut self, rendered_diagrams: Vec<RenderedDiagram>) -> Self {
47 self.rendered_diagrams = rendered_diagrams;
48 self
49 }
50}
51
52impl<B: ForgeBackend> ForgePipeline<B> {
53 pub fn new(backend: B) -> Self {
54 Self { backend }
55 }
56
57 pub fn build(&self, request: &BuildRequest) -> Result<BuildGraph, ForgeError> {
58 self.backend.build(request)
59 }
60
61 pub fn export(&self, request: &ExportRequest) -> Result<ExportOutput, ForgeError> {
62 validate_export_output(self.backend.export(request), request.format)
63 }
64}
65
66fn validate_export_output(
67 result: Result<ExportOutput, ForgeError>,
68 format: ExportFormat,
69) -> Result<ExportOutput, ForgeError> {
70 match result {
71 Ok(output) => reject_empty_export_output(output, format),
72 Err(error) => Err(error),
73 }
74}
75
76fn reject_empty_export_output(
77 output: ExportOutput,
78 format: ExportFormat,
79) -> Result<ExportOutput, ForgeError> {
80 if output.artifact.bytes.bytes.is_empty() {
81 Err(ForgeError::EmptyExportArtifact(format))
82 } else {
83 Ok(output)
84 }
85}
86pub struct ManifestOnlyBackend;
87
88impl ForgeBackend for ManifestOnlyBackend {
89 fn build(&self, request: &BuildRequest) -> Result<BuildGraph, ForgeError> {
90 Ok(BuildGraph::from_request(request))
91 }
92
93 fn export(&self, request: &ExportRequest) -> Result<ExportOutput, ForgeError> {
94 let snapshot = &request.graph.snapshot;
95 ExportPayloadFactory::create(&request.graph, request.format, &request.theme).map(|bytes| {
96 let artifact = ArtifactFactory::export(
97 request.format.artifact_format(),
98 snapshot.id.clone(),
99 snapshot.revision.clone(),
100 ArtifactBytes { bytes },
101 );
102 ExportOutput {
103 artifact,
104 diagnostics: request.graph.diagnostics.clone(),
105 }
106 })
107 }
108}
109
110#[cfg(test)]
111#[path = "forge_tests.rs"]
112mod tests;