use std::collections::BTreeSet;
use std::ffi::OsStr;
use std::fs::File;
use std::io::BufReader;
use std::iter::FromIterator;
use std::path::{Path, PathBuf};
use std::str::FromStr;
use walkdir::WalkDir;
use ion_rs::result::{decoding_error, IonResult};
use ion_rs::{BinaryIonCursor, IonType, Reader};
const GOOD_TEST_FILES_PATH: &str = "ion-tests/iontestdata/good/";
const GOOD_TEST_FILES_SKIP_LIST: &[&str] = &[
"ion-tests/iontestdata/good/emptyThreeByteNopPad.10n",
"ion-tests/iontestdata/good/equivs/nopPadEmptyStruct.10n",
"ion-tests/iontestdata/good/equivs/nopPadNonEmptyStruct.10n",
"ion-tests/iontestdata/good/nopPad16Bytes.10n",
"ion-tests/iontestdata/good/nopPadInsideEmptyStructNonZeroSymbolId.10n",
"ion-tests/iontestdata/good/nopPadInsideEmptyStructZeroSymbolId.10n",
"ion-tests/iontestdata/good/nopPadInsideStructWithNopPadThenValueNonZeroSymbolId.10n",
"ion-tests/iontestdata/good/nopPadInsideStructWithNopPadThenValueZeroSymbolId.10n",
"ion-tests/iontestdata/good/nopPadInsideStructWithValueThenNopPad.10n",
"ion-tests/iontestdata/good/nopPadOneByte.10n",
"ion-tests/iontestdata/good/valueFollowedByNopPad.10n",
"ion-tests/iontestdata/good/valuePrecededByNopPad.10n",
"ion-tests/iontestdata/good/valueBetweenNopPads.10n",
"ion-tests/iontestdata/good/equivs/timestampSuperfluousOffset.10n",
"ion-tests/iontestdata/good/timestamp/timestamp2011-02.10n",
"ion-tests/iontestdata/good/timestamp/timestamp2011.10n",
"ion-tests/iontestdata/good/equivs/intsLargeNegative1.10n",
"ion-tests/iontestdata/good/equivs/intsLargeNegative2.10n",
"ion-tests/iontestdata/good/equivs/intsLargeNegative3.10n",
"ion-tests/iontestdata/good/equivs/intsLargePositive1.10n",
"ion-tests/iontestdata/good/equivs/intsLargePositive2.10n",
"ion-tests/iontestdata/good/equivs/intsLargePositive3.10n",
"ion-tests/iontestdata/good/intBigSize1201.10n",
"ion-tests/iontestdata/good/intBigSize13.10n",
"ion-tests/iontestdata/good/intBigSize14.10n",
"ion-tests/iontestdata/good/intBigSize16.10n",
"ion-tests/iontestdata/good/intBigSize256.10n",
"ion-tests/iontestdata/good/intLongMaxValuePlusOne.10n",
"ion-tests/iontestdata/good/intLongMinValue.10n",
"ion-tests/iontestdata/good/equivs/paddedInts.10n",
"ion-tests/iontestdata/good/item1.10n",
"ion-tests/iontestdata/good/typecodes/T0.10n",
"ion-tests/iontestdata/good/typecodes/T11.10n",
"ion-tests/iontestdata/good/typecodes/T12.10n",
"ion-tests/iontestdata/good/typecodes/T2.10n",
"ion-tests/iontestdata/good/typecodes/T3.10n",
"ion-tests/iontestdata/good/typecodes/T5.10n",
"ion-tests/iontestdata/good/typecodes/T6-small.10n",
"ion-tests/iontestdata/good/typecodes/T7-large.10n",
];
#[test]
fn read_good_files() -> IonResult<()> {
let binary_file_extension: &OsStr = OsStr::new("10n");
let good_files = all_files_in(GOOD_TEST_FILES_PATH);
let paths_to_skip = skip_list_as_set(GOOD_TEST_FILES_SKIP_LIST);
let binary_good_files: Vec<_> = good_files
.iter()
.filter(|f| f.extension() == Some(binary_file_extension))
.filter(|f| !paths_to_skip.contains(<&&PathBuf as AsRef<Path>>::as_ref(&f)))
.collect();
let mut failure_count: usize = 0;
println!();
for entry in &binary_good_files {
print!("Reading {}... ", entry.display());
if let Err(error) = read_file(entry.as_ref()) {
print!("ERROR: {:?}", error);
failure_count += 1;
} else {
print!("OK");
}
println!();
}
if failure_count > 0 {
return decoding_error(&format!(
"{} good test files could not be read successfully.",
failure_count
));
}
Ok(())
}
fn skip_list_as_set(files_to_skip: &[&str]) -> BTreeSet<PathBuf> {
let mut skip_set = BTreeSet::new();
for file in files_to_skip {
skip_set.insert(PathBuf::from_str(file).unwrap());
}
skip_set
}
fn all_files_in(path: &str) -> BTreeSet<PathBuf> {
let binary_file_iterator = WalkDir::new(path)
.into_iter()
.map(|entry| {
entry.unwrap_or_else(|error| panic!("Failure during dir traversal: {:?}", error))
})
.filter(|entry| entry.path().is_file())
.map(|entry| entry.path().to_owned());
BTreeSet::from_iter(binary_file_iterator)
}
fn read_file(path: &Path) -> IonResult<()> {
let file = File::open(path).unwrap_or_else(|error| panic!("Failed to open file: {:?}", error));
let file_reader = BufReader::new(file);
let mut reader = Reader::new(BinaryIonCursor::new(file_reader));
read_all_values(&mut reader)?;
Ok(())
}
fn read_all_values(reader: &mut Reader<BinaryIonCursor<BufReader<File>>>) -> IonResult<()> {
use IonType::*;
while let Some((ion_type, is_null)) = reader.next()? {
if is_null {
continue;
}
match ion_type {
Struct | List | SExpression => {
reader.step_in()?;
read_all_values(reader)?;
reader.step_out()?;
}
String => {
let _text = reader.string_ref_map(|_s| ())?.unwrap();
}
Symbol => {
let _symbol_id = reader.read_symbol_id()?.unwrap();
}
Integer => {
let _int = reader.read_i64()?.unwrap();
}
Float => {
let _float = reader.read_f64()?.unwrap();
}
Decimal => {
let _decimal = reader.read_big_decimal()?.unwrap();
}
Timestamp => {
let _timestamp = reader.read_datetime()?.unwrap();
}
Boolean => {
let _boolean = reader.read_bool()?.unwrap();
}
Blob => {
let _blob = reader.read_blob_bytes()?.unwrap();
}
Clob => {
let _clob = reader.read_clob_bytes()?.unwrap();
}
Null => {
unreachable!("Value with IonType::Null returned is_null=false.");
}
}
}
Ok(())
}