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;