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.
71 FromJson {
72 /// Input JSON file
73 #[arg(value_name = "FILE")]
74 file: String,
75
76 /// Output file path (defaults to stdout)
77 #[arg(short, long)]
78 output: Option<String>,
79 },
80
81 /// Convert HEDL to YAML
82 ///
83 /// Converts a HEDL file to YAML format.
84 ToYaml {
85 /// Input HEDL file
86 #[arg(value_name = "FILE")]
87 file: String,
88
89 /// Output file path (defaults to stdout)
90 #[arg(short, long)]
91 output: Option<String>,
92 },
93
94 /// Convert YAML to HEDL
95 ///
96 /// Converts a YAML file to HEDL format.
97 FromYaml {
98 /// Input YAML file
99 #[arg(value_name = "FILE")]
100 file: String,
101
102 /// Output file path (defaults to stdout)
103 #[arg(short, long)]
104 output: Option<String>,
105 },
106
107 /// Convert HEDL to XML
108 ///
109 /// Converts a HEDL file to XML format with optional pretty printing.
110 ToXml {
111 /// Input HEDL file
112 #[arg(value_name = "FILE")]
113 file: String,
114
115 /// Output file path (defaults to stdout)
116 #[arg(short, long)]
117 output: Option<String>,
118
119 /// Pretty print XML
120 #[arg(short, long)]
121 pretty: bool,
122 },
123
124 /// Convert XML to HEDL
125 ///
126 /// Converts an XML file to HEDL format.
127 FromXml {
128 /// Input XML file
129 #[arg(value_name = "FILE")]
130 file: String,
131
132 /// Output file path (defaults to stdout)
133 #[arg(short, long)]
134 output: Option<String>,
135 },
136
137 /// Convert HEDL to CSV
138 ///
139 /// Converts a HEDL file containing tabular data to CSV format.
140 /// Works best with HEDL matrix lists.
141 ToCsv {
142 /// Input HEDL file
143 #[arg(value_name = "FILE")]
144 file: String,
145
146 /// Output file path (defaults to stdout)
147 #[arg(short, long)]
148 output: Option<String>,
149
150 /// Include header row
151 #[arg(long, default_value = "true")]
152 headers: bool,
153 },
154
155 /// Convert CSV to HEDL
156 ///
157 /// Converts a CSV file to HEDL matrix list format.
158 FromCsv {
159 /// Input CSV file
160 #[arg(value_name = "FILE")]
161 file: String,
162
163 /// Output file path (defaults to stdout)
164 #[arg(short, long)]
165 output: Option<String>,
166
167 /// Type name for the matrix list
168 #[arg(short, long, default_value = "Row")]
169 type_name: String,
170 },
171
172 /// Convert HEDL to Parquet
173 ///
174 /// Converts a HEDL file to Apache Parquet columnar format.
175 /// Requires an output file path (Parquet cannot write to stdout).
176 ToParquet {
177 /// Input HEDL file
178 #[arg(value_name = "FILE")]
179 file: String,
180
181 /// Output Parquet file path (required)
182 #[arg(short, long)]
183 output: String,
184 },
185
186 /// Convert Parquet to HEDL
187 ///
188 /// Converts an Apache Parquet file to HEDL format.
189 FromParquet {
190 /// Input Parquet file
191 #[arg(value_name = "FILE")]
192 file: String,
193
194 /// Output file path (defaults to stdout)
195 #[arg(short, long)]
196 output: Option<String>,
197 },
198
199 /// Convert HEDL to TOON
200 ///
201 /// Converts a HEDL file to TOON format.
202 ToToon {
203 /// Input HEDL file
204 #[arg(value_name = "FILE")]
205 file: String,
206
207 /// Output file path (defaults to stdout)
208 #[arg(short, long)]
209 output: Option<String>,
210 },
211}
212
213impl ConversionCommands {
214 /// Execute the conversion command.
215 ///
216 /// # Returns
217 ///
218 /// Returns `Ok(())` on success, or an error message on failure.
219 ///
220 /// # Errors
221 ///
222 /// Returns `Err` if:
223 /// - File I/O fails
224 /// - Parsing fails
225 /// - Conversion fails
226 /// - Output writing fails
227 pub fn execute(self) -> Result<(), String> {
228 match self {
229 ConversionCommands::ToJson {
230 file,
231 output,
232 metadata,
233 pretty,
234 } => commands::to_json(&file, output.as_deref(), metadata, pretty),
235 ConversionCommands::FromJson { file, output } => {
236 commands::from_json(&file, output.as_deref())
237 }
238 ConversionCommands::ToYaml { file, output } => {
239 commands::to_yaml(&file, output.as_deref())
240 }
241 ConversionCommands::FromYaml { file, output } => {
242 commands::from_yaml(&file, output.as_deref())
243 }
244 ConversionCommands::ToXml {
245 file,
246 output,
247 pretty,
248 } => commands::to_xml(&file, output.as_deref(), pretty),
249 ConversionCommands::FromXml { file, output } => {
250 commands::from_xml(&file, output.as_deref())
251 }
252 ConversionCommands::ToCsv {
253 file,
254 output,
255 headers,
256 } => commands::to_csv(&file, output.as_deref(), headers),
257 ConversionCommands::FromCsv {
258 file,
259 output,
260 type_name,
261 } => commands::from_csv(&file, output.as_deref(), &type_name),
262 ConversionCommands::ToParquet { file, output } => commands::to_parquet(&file, &output),
263 ConversionCommands::FromParquet { file, output } => {
264 commands::from_parquet(&file, output.as_deref())
265 }
266 ConversionCommands::ToToon { file, output } => {
267 commands::to_toon(&file, output.as_deref())
268 }
269 }
270 }
271}