Skip to main content

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}