Skip to main content

katana_document_viewer/
cli_api.rs

1use crate::backend::diagram::KrrDiagramInputFactory;
2use crate::forge::{
3    BuildGraph, BuildProfile, BuildRequest, ExportFormat, ExportOutput, ExportRequest,
4    ForgeBackend, ForgeError, ForgePipeline,
5};
6use crate::{DocumentSnapshot, KdvThemeSnapshot};
7use katana_markdown_model::DiagramKind;
8use katana_render_runtime::{RenderContext, RenderInput};
9use serde::{Deserialize, Serialize};
10
11#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
12pub struct CliDiagnostics {
13    pub messages: Vec<String>,
14}
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
17pub enum CliThemeMode {
18    Light,
19    Dark,
20}
21
22#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
23pub struct CliBuildRequest {
24    pub snapshot: DocumentSnapshot,
25    pub profile: BuildProfile,
26    pub theme_mode: Option<CliThemeMode>,
27}
28
29#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
30pub struct CliExportRequest {
31    pub graph: BuildGraph,
32    pub format: ExportFormat,
33    pub theme_mode: Option<CliThemeMode>,
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
37pub struct CliExportDebugRequest {
38    pub graph: BuildGraph,
39    pub formats: Vec<ExportFormat>,
40    pub theme_mode: Option<CliThemeMode>,
41}
42
43pub enum CliRequest {
44    Build(CliBuildRequest),
45    Export(CliExportRequest),
46    ExportDebug(CliExportDebugRequest),
47    Diagram {
48        kind: DiagramKind,
49        source: String,
50        context: RenderContext,
51    },
52}
53
54pub enum CliOutput {
55    Build {
56        graph: Box<BuildGraph>,
57        diagnostics: CliDiagnostics,
58    },
59    Export {
60        output: Box<ExportOutput>,
61        diagnostics: CliDiagnostics,
62    },
63    ExportDebug {
64        outputs: Vec<ExportOutput>,
65        diagnostics: CliDiagnostics,
66    },
67    Diagram {
68        input: Box<RenderInput>,
69        diagnostics: CliDiagnostics,
70    },
71}
72
73pub struct CliApi<B> {
74    pipeline: ForgePipeline<B>,
75}
76
77impl<B: ForgeBackend> CliApi<B> {
78    pub fn new(backend: B) -> Self {
79        Self {
80            pipeline: ForgePipeline::new(backend),
81        }
82    }
83
84    pub fn handle(&self, request: CliRequest) -> Result<CliOutput, ForgeError> {
85        match request {
86            CliRequest::Build(request) => self.handle_build(&request),
87            CliRequest::Export(request) => self.handle_export(&request),
88            CliRequest::ExportDebug(request) => self.handle_export_debug(&request),
89            CliRequest::Diagram {
90                kind,
91                source,
92                context,
93            } => self.handle_diagram(kind, source, context),
94        }
95    }
96
97    fn handle_build(&self, request: &CliBuildRequest) -> Result<CliOutput, ForgeError> {
98        let build_request = BuildRequest {
99            snapshot: request.snapshot.clone(),
100            profile: request.profile.clone(),
101            theme: Self::theme_snapshot(request.theme_mode),
102        };
103        let graph = self.pipeline.build(&build_request)?;
104        Ok(CliOutput::Build {
105            graph: Box::new(graph),
106            diagnostics: CliDiagnostics {
107                messages: Vec::new(),
108            },
109        })
110    }
111
112    fn handle_export(&self, request: &CliExportRequest) -> Result<CliOutput, ForgeError> {
113        let export_request = ExportRequest {
114            graph: request.graph.clone(),
115            format: request.format,
116            theme: Self::theme_snapshot(request.theme_mode),
117        };
118        let output = self.pipeline.export(&export_request)?;
119        Ok(CliOutput::Export {
120            output: Box::new(output),
121            diagnostics: CliDiagnostics {
122                messages: Vec::new(),
123            },
124        })
125    }
126
127    fn handle_export_debug(
128        &self,
129        request: &CliExportDebugRequest,
130    ) -> Result<CliOutput, ForgeError> {
131        let mut outputs = Vec::with_capacity(request.formats.len());
132        for format in &request.formats {
133            let export_request = ExportRequest {
134                graph: request.graph.clone(),
135                format: *format,
136                theme: Self::theme_snapshot(request.theme_mode),
137            };
138            outputs.push(self.pipeline.export(&export_request)?);
139        }
140        Ok(CliOutput::ExportDebug {
141            outputs,
142            diagnostics: CliDiagnostics {
143                messages: Vec::new(),
144            },
145        })
146    }
147
148    fn handle_diagram(
149        &self,
150        kind: DiagramKind,
151        source: String,
152        context: RenderContext,
153    ) -> Result<CliOutput, ForgeError> {
154        let input = KrrDiagramInputFactory::create(kind, source, context);
155        Ok(CliOutput::Diagram {
156            input: Box::new(input),
157            diagnostics: CliDiagnostics {
158                messages: Vec::new(),
159            },
160        })
161    }
162
163    fn theme_snapshot(mode: Option<CliThemeMode>) -> KdvThemeSnapshot {
164        match mode.unwrap_or(CliThemeMode::Light) {
165            CliThemeMode::Light => KdvThemeSnapshot::katana_light(),
166            CliThemeMode::Dark => KdvThemeSnapshot::katana_dark(),
167        }
168    }
169}
170
171#[cfg(test)]
172#[path = "cli_api_tests.rs"]
173mod tests;