arrow_json_integration_test/
arrow-json-integration-test.rs1#![allow(unused_crate_dependencies)]
22
23use arrow::error::{ArrowError, Result};
24use arrow::ipc::reader::FileReader;
25use arrow::ipc::writer::FileWriter;
26use arrow_integration_test::*;
27use arrow_integration_testing::{canonicalize_schema, open_json_file};
28use clap::Parser;
29use std::fs::File;
30
31#[derive(clap::ValueEnum, Debug, Clone)]
32#[clap(rename_all = "SCREAMING_SNAKE_CASE")]
33enum Mode {
34 ArrowToJson,
35 JsonToArrow,
36 Validate,
37}
38
39#[derive(Debug, Parser)]
40#[clap(author, version, about("rust arrow-json-integration-test"), long_about = None)]
41struct Args {
42 #[clap(short, long)]
43 integration: bool,
44 #[clap(short, long, help("Path to ARROW file"))]
45 arrow: String,
46 #[clap(short, long, help("Path to JSON file"))]
47 json: String,
48 #[clap(
49 value_enum,
50 short,
51 long,
52 default_value = "VALIDATE",
53 help = "Mode of integration testing tool"
54 )]
55 mode: Mode,
56 #[clap(short, long)]
57 verbose: bool,
58}
59
60fn main() -> Result<()> {
61 let args = Args::parse();
62 let arrow_file = args.arrow;
63 let json_file = args.json;
64 let verbose = args.verbose;
65 match args.mode {
66 Mode::JsonToArrow => json_to_arrow(&json_file, &arrow_file, verbose),
67 Mode::ArrowToJson => arrow_to_json(&arrow_file, &json_file, verbose),
68 Mode::Validate => validate(&arrow_file, &json_file, verbose),
69 }
70}
71
72fn json_to_arrow(json_name: &str, arrow_name: &str, verbose: bool) -> Result<()> {
73 if verbose {
74 eprintln!("Converting {json_name} to {arrow_name}");
75 }
76
77 let json_file = open_json_file(json_name)?;
78
79 let arrow_file = File::create(arrow_name)?;
80 let mut writer = FileWriter::try_new(arrow_file, &json_file.schema)?;
81
82 for b in json_file.read_batches()? {
83 writer.write(&b)?;
84 }
85
86 writer.finish()?;
87
88 Ok(())
89}
90
91fn arrow_to_json(arrow_name: &str, json_name: &str, verbose: bool) -> Result<()> {
92 if verbose {
93 eprintln!("Converting {arrow_name} to {json_name}");
94 }
95
96 let arrow_file = File::open(arrow_name)?;
97 let reader = FileReader::try_new(arrow_file, None)?;
98
99 let mut fields: Vec<ArrowJsonField> = vec![];
100 for f in reader.schema().fields() {
101 fields.push(ArrowJsonField::from(f));
102 }
103 let schema = ArrowJsonSchema {
104 fields,
105 metadata: None,
106 };
107
108 let batches = reader
109 .map(|batch| Ok(ArrowJsonBatch::from_batch(&batch?)))
110 .collect::<Result<Vec<_>>>()?;
111
112 let arrow_json = ArrowJson {
113 schema,
114 batches,
115 dictionaries: None,
116 };
117
118 let json_file = File::create(json_name)?;
119 serde_json::to_writer(&json_file, &arrow_json).unwrap();
120
121 Ok(())
122}
123
124fn validate(arrow_name: &str, json_name: &str, verbose: bool) -> Result<()> {
125 if verbose {
126 eprintln!("Validating {arrow_name} and {json_name}");
127 }
128
129 let json_file = open_json_file(json_name)?;
131
132 let arrow_file = File::open(arrow_name)?;
134 let mut arrow_reader = FileReader::try_new(arrow_file, None)?;
135 let arrow_schema = arrow_reader.schema().as_ref().to_owned();
136
137 if canonicalize_schema(&json_file.schema) != canonicalize_schema(&arrow_schema) {
139 return Err(ArrowError::ComputeError(format!(
140 "Schemas do not match. JSON: {:?}. Arrow: {:?}",
141 json_file.schema, arrow_schema
142 )));
143 }
144
145 let json_batches = json_file.read_batches()?;
146
147 assert!(
149 json_batches.len() == arrow_reader.num_batches(),
150 "JSON batches and Arrow batches are unequal"
151 );
152
153 if verbose {
154 eprintln!(
155 "Schemas match. JSON file has {} batches.",
156 json_batches.len()
157 );
158 }
159
160 for json_batch in json_batches {
161 if let Some(Ok(arrow_batch)) = arrow_reader.next() {
162 let num_columns = arrow_batch.num_columns();
164 assert!(num_columns == json_batch.num_columns());
165 assert!(arrow_batch.num_rows() == json_batch.num_rows());
166
167 for i in 0..num_columns {
168 assert_eq!(
169 arrow_batch.column(i).as_ref(),
170 json_batch.column(i).as_ref(),
171 "Arrow and JSON batch columns not the same"
172 );
173 }
174 } else {
175 return Err(ArrowError::ComputeError(
176 "no more arrow batches left".to_owned(),
177 ));
178 }
179 }
180
181 if arrow_reader.next().is_some() {
182 return Err(ArrowError::ComputeError(
183 "no more json batches left".to_owned(),
184 ));
185 }
186
187 Ok(())
188}