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    /// Convert TOON to HEDL
213    ///
214    /// Converts a TOON file to HEDL format.
215    FromToon {
216        /// Input TOON file
217        #[arg(value_name = "FILE")]
218        file: String,
219
220        /// Output file path (defaults to stdout)
221        #[arg(short, long)]
222        output: Option<String>,
223    },
224}
225
226impl ConversionCommands {
227    /// Execute the conversion command.
228    ///
229    /// # Returns
230    ///
231    /// Returns `Ok(())` on success, or an error message on failure.
232    ///
233    /// # Errors
234    ///
235    /// Returns `Err` if:
236    /// - File I/O fails
237    /// - Parsing fails
238    /// - Conversion fails
239    /// - Output writing fails
240    pub fn execute(self) -> Result<(), String> {
241        match self {
242            ConversionCommands::ToJson {
243                file,
244                output,
245                metadata,
246                pretty,
247            } => commands::to_json(&file, output.as_deref(), metadata, pretty),
248            ConversionCommands::FromJson { file, output } => {
249                commands::from_json(&file, output.as_deref())
250            }
251            ConversionCommands::ToYaml { file, output } => {
252                commands::to_yaml(&file, output.as_deref())
253            }
254            ConversionCommands::FromYaml { file, output } => {
255                commands::from_yaml(&file, output.as_deref())
256            }
257            ConversionCommands::ToXml {
258                file,
259                output,
260                pretty,
261            } => commands::to_xml(&file, output.as_deref(), pretty),
262            ConversionCommands::FromXml { file, output } => {
263                commands::from_xml(&file, output.as_deref())
264            }
265            ConversionCommands::ToCsv {
266                file,
267                output,
268                headers,
269            } => commands::to_csv(&file, output.as_deref(), headers),
270            ConversionCommands::FromCsv {
271                file,
272                output,
273                type_name,
274            } => commands::from_csv(&file, output.as_deref(), &type_name),
275            ConversionCommands::ToParquet { file, output } => commands::to_parquet(&file, &output),
276            ConversionCommands::FromParquet { file, output } => {
277                commands::from_parquet(&file, output.as_deref())
278            }
279            ConversionCommands::ToToon { file, output } => {
280                commands::to_toon(&file, output.as_deref())
281            }
282            ConversionCommands::FromToon { file, output } => {
283                commands::from_toon(&file, output.as_deref())
284            }
285        }
286    }
287}