use crate::{
array_cache::{ArrayCache, UriResolvable},
schema::{array_uri_reference::AbsoluteUri, LoggingRun},
};
use serde::{Deserialize, Deserializer};
use std::{fmt::Debug, fs::File, io::Read, path::Path};
use zip::ZipArchive;
pub type Error = String;
#[derive(Debug, Deserialize)]
pub struct BoreholeDataArchive {
#[serde(deserialize_with = "ensure_version")]
pub version: Version,
pub logging_runs: Vec<LoggingRun>,
}
#[derive(Debug, Deserialize, PartialEq)]
pub struct Version {
pub major: u64,
pub minor: u64,
}
impl BoreholeDataArchive {
const VERSION: Version = Version { major: 0, minor: 2 };
pub fn from_file<P: AsRef<Path> + Debug>(path: P) -> Result<Self, Error> {
let zip_file = File::open(&path)
.map_err(|e| format!("Failed to open BDA file {path:?} due to an error {e}"))?;
let mut archive = ZipArchive::new(zip_file)
.map_err(|e| format!("Failed to construct ZipArchive: {e}"))?;
let mut index: Self = {
let mut file = archive
.by_name("index.json")
.map_err(|e| format!("Failed to extract BDA index: {e}"))?;
let mut contents = String::new();
let _bytes_read = file
.read_to_string(&mut contents)
.map_err(|e| format!("Failed to read BDA index: {e}"))?;
serde_json::from_str(&contents)
.map_err(|e| format!("Failed to interpret BDA index file {path:?} as JSON: {e}"))?
};
let base_uri = ArrayCache::insert_archive(archive)?;
index.set_base_for_relative_uris(&base_uri);
Ok(index)
}
}
impl UriResolvable for BoreholeDataArchive {
fn set_base_for_relative_uris(&mut self, base_uri: &AbsoluteUri) {
for logging_run in &mut self.logging_runs {
logging_run.set_base_for_relative_uris(base_uri);
}
}
}
fn ensure_version<'de, D>(d: D) -> Result<Version, D::Error>
where
D: Deserializer<'de>,
{
let version = Version::deserialize(d)?;
if version != BoreholeDataArchive::VERSION {
Err(serde::de::Error::custom(format!(
"Unsupported version: {version:?}"
)))
} else {
Ok(version)
}
}
#[test]
fn caching() {
use crate::schema::axes::{ChannelAxis, Numeric, NumericAxis};
let bda = BoreholeDataArchive::from_file("QL40-OBI.bda").unwrap();
for channel in &bda.logging_runs[0].channels {
if channel.name != "Image" {
continue;
}
let composite_axis = match &channel.data.axes[0] {
ChannelAxis::Composite(axis) => axis,
_ => panic!("CompositeAxis expected"),
};
for component in &composite_axis.descriptors {
if let NumericAxis::Coordinate(axis) = component {
assert!(axis.coordinates().unwrap().len() > 0);
return;
}
}
}
panic!("Should've found a CoordinateAxis by now!");
}