hedl_cli/cli/conversion.rs
1// Dweve HEDL - Hierarchical Entity Data Language
2//
3// Copyright (c) 2025 Dweve IP B.V. and individual contributors.
4//
5// SPDX-License-Identifier: Apache-2.0
6//
7// Licensed under the Apache License, Version 2.0 (the "License");
8// you may not use this file except in compliance with the License.
9// You may obtain a copy of the License in the LICENSE file at the
10// root of this repository or at: http://www.apache.org/licenses/LICENSE-2.0
11//
12// Unless required by applicable law or agreed to in writing, software
13// distributed under the License is distributed on an "AS IS" BASIS,
14// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15// See the License for the specific language governing permissions and
16// limitations under the License.
17
18//! Format conversion commands for HEDL.
19//!
20//! This module provides bidirectional conversion between HEDL and various
21//! popular data formats including JSON, YAML, XML, CSV, and Parquet.
22
23use crate::commands;
24use clap::Subcommand;
25
26/// Format conversion commands.
27///
28/// These commands enable bidirectional conversion between HEDL and various
29/// data formats, making HEDL interoperable with existing tools and workflows.
30///
31/// # Supported Formats
32///
33/// - **JSON**: Compact and pretty printing, optional metadata
34/// - **YAML**: Standard YAML format
35/// - **XML**: Compact and pretty printing
36/// - **CSV**: Tabular data with optional headers
37/// - **Parquet**: Apache Parquet columnar format
38///
39/// # Design
40///
41/// All conversion commands follow a consistent pattern:
42/// - `to-<format>`: Convert HEDL to target format
43/// - `from-<format>`: Convert target format to HEDL
44#[derive(Subcommand)]
45pub enum ConversionCommands {
46 /// Convert HEDL to JSON
47 ///
48 /// Converts a HEDL file to JSON format with optional pretty printing
49 /// and metadata inclusion.
50 ToJson {
51 /// Input HEDL file
52 #[arg(value_name = "FILE")]
53 file: String,
54
55 /// Output file path (defaults to stdout)
56 #[arg(short, long)]
57 output: Option<String>,
58
59 /// Include HEDL metadata in JSON
60 #[arg(long)]
61 metadata: bool,
62
63 /// Pretty print JSON
64 #[arg(short, long)]
65 pretty: bool,
66 },
67
68 /// Convert JSON to HEDL
69 ///
70 /// Converts a JSON file to HEDL format. Foreign key relationships are
71 /// automatically detected and converted to nested hierarchies.
72 FromJson {
73 /// Input JSON file
74 #[arg(value_name = "FILE")]
75 file: String,
76
77 /// Output file path (defaults to stdout)
78 #[arg(short, long)]
79 output: Option<String>,
80 },
81
82 /// Convert HEDL to YAML
83 ///
84 /// Converts a HEDL file to YAML format.
85 ToYaml {
86 /// Input HEDL file
87 #[arg(value_name = "FILE")]
88 file: String,
89
90 /// Output file path (defaults to stdout)
91 #[arg(short, long)]
92 output: Option<String>,
93 },
94
95 /// Convert YAML to HEDL
96 ///
97 /// Converts a YAML file to HEDL format.
98 FromYaml {
99 /// Input YAML file
100 #[arg(value_name = "FILE")]
101 file: String,
102
103 /// Output file path (defaults to stdout)
104 #[arg(short, long)]
105 output: Option<String>,
106 },
107
108 /// Convert HEDL to XML
109 ///
110 /// Converts a HEDL file to XML format with optional pretty printing.
111 ToXml {
112 /// Input HEDL file
113 #[arg(value_name = "FILE")]
114 file: String,
115
116 /// Output file path (defaults to stdout)
117 #[arg(short, long)]
118 output: Option<String>,
119
120 /// Pretty print XML
121 #[arg(short, long)]
122 pretty: bool,
123 },
124
125 /// Convert XML to HEDL
126 ///
127 /// Converts an XML file to HEDL format.
128 FromXml {
129 /// Input XML file
130 #[arg(value_name = "FILE")]
131 file: String,
132
133 /// Output file path (defaults to stdout)
134 #[arg(short, long)]
135 output: Option<String>,
136 },
137
138 /// Convert HEDL to CSV
139 ///
140 /// Converts a HEDL file containing tabular data to CSV format.
141 /// Works best with HEDL matrix lists.
142 ToCsv {
143 /// Input HEDL file
144 #[arg(value_name = "FILE")]
145 file: String,
146
147 /// Output file path (defaults to stdout)
148 #[arg(short, long)]
149 output: Option<String>,
150
151 /// Include header row
152 #[arg(long, default_value = "true")]
153 headers: bool,
154 },
155
156 /// Convert CSV to HEDL
157 ///
158 /// Converts a CSV file to HEDL matrix list format.
159 FromCsv {
160 /// Input CSV file
161 #[arg(value_name = "FILE")]
162 file: String,
163
164 /// Output file path (defaults to stdout)
165 #[arg(short, long)]
166 output: Option<String>,
167
168 /// Type name for the matrix list
169 #[arg(short, long, default_value = "Row")]
170 type_name: String,
171 },
172
173 /// Convert HEDL to Parquet
174 ///
175 /// Converts a HEDL file to Apache Parquet columnar format.
176 /// Requires an output file path (Parquet cannot write to stdout).
177 ToParquet {
178 /// Input HEDL file
179 #[arg(value_name = "FILE")]
180 file: String,
181
182 /// Output Parquet file path (required)
183 #[arg(short, long)]
184 output: String,
185 },
186
187 /// Convert Parquet to HEDL
188 ///
189 /// Converts an Apache Parquet file to HEDL format.
190 FromParquet {
191 /// Input Parquet file
192 #[arg(value_name = "FILE")]
193 file: String,
194
195 /// Output file path (defaults to stdout)
196 #[arg(short, long)]
197 output: Option<String>,
198 },
199
200 /// Convert HEDL to TOON
201 ///
202 /// Converts a HEDL file to TOON format.
203 ToToon {
204 /// Input HEDL file
205 #[arg(value_name = "FILE")]
206 file: String,
207
208 /// Output file path (defaults to stdout)
209 #[arg(short, long)]
210 output: Option<String>,
211 },
212
213 /// Convert TOON to HEDL
214 ///
215 /// Converts a TOON file to HEDL format.
216 FromToon {
217 /// Input TOON file
218 #[arg(value_name = "FILE")]
219 file: String,
220
221 /// Output file path (defaults to stdout)
222 #[arg(short, long)]
223 output: Option<String>,
224 },
225}
226
227impl ConversionCommands {
228 /// Execute the conversion command.
229 ///
230 /// # Returns
231 ///
232 /// Returns `Ok(())` on success, or an error message on failure.
233 ///
234 /// # Errors
235 ///
236 /// Returns `Err` if:
237 /// - File I/O fails
238 /// - Parsing fails
239 /// - Conversion fails
240 /// - Output writing fails
241 pub fn execute(self) -> Result<(), crate::error::CliError> {
242 match self {
243 ConversionCommands::ToJson {
244 file,
245 output,
246 metadata,
247 pretty,
248 } => commands::to_json(&file, output.as_deref(), metadata, pretty),
249 ConversionCommands::FromJson { file, output } => {
250 commands::from_json(&file, output.as_deref())
251 }
252 ConversionCommands::ToYaml { file, output } => {
253 commands::to_yaml(&file, output.as_deref())
254 }
255 ConversionCommands::FromYaml { file, output } => {
256 commands::from_yaml(&file, output.as_deref())
257 }
258 ConversionCommands::ToXml {
259 file,
260 output,
261 pretty,
262 } => commands::to_xml(&file, output.as_deref(), pretty),
263 ConversionCommands::FromXml { file, output } => {
264 commands::from_xml(&file, output.as_deref())
265 }
266 ConversionCommands::ToCsv {
267 file,
268 output,
269 headers,
270 } => commands::to_csv(&file, output.as_deref(), headers),
271 ConversionCommands::FromCsv {
272 file,
273 output,
274 type_name,
275 } => commands::from_csv(&file, output.as_deref(), &type_name),
276 ConversionCommands::ToParquet { file, output } => commands::to_parquet(&file, &output),
277 ConversionCommands::FromParquet { file, output } => {
278 commands::from_parquet(&file, output.as_deref())
279 }
280 ConversionCommands::ToToon { file, output } => {
281 commands::to_toon(&file, output.as_deref())
282 }
283 ConversionCommands::FromToon { file, output } => {
284 commands::from_toon(&file, output.as_deref())
285 }
286 }
287 }
288}