use std::collections::BTreeMap;
use super::cf::CfTransform;
use crate::catalog::{
ArrayWriteMeta, CatalogError, CoordAxisV1, StreamTileJob, StreamWriteProgress,
write_multi_raw_array_streaming,
};
use crate::utils::dtype::ElementDtype;
use super::ConvertError;
#[derive(Clone)]
pub(crate) struct ImportPlan {
pub name: String,
pub dtype: ElementDtype,
pub shape: Vec<u64>,
pub chunk_shape: Vec<u64>,
pub cf: Option<CfTransform>,
pub zarr_array_rel: Option<String>,
pub zarr_zstd: bool,
pub import_attrs: BTreeMap<String, String>,
pub import_dim_names: Option<Vec<String>>,
pub import_coords: Option<BTreeMap<String, CoordAxisV1>>,
}
pub(crate) fn join_catalog_path(prefix: &str, name: &str) -> String {
if prefix.is_empty() {
name.to_owned()
} else {
format!("{prefix}/{name}")
}
}
#[derive(Clone, Copy)]
pub(crate) struct ImportTileRead<'a> {
pub dtype: ElementDtype,
pub shape: &'a [u64],
pub chunk_shape: &'a [u64],
pub chunk_coord: &'a [u64],
pub ndim: usize,
pub cf: Option<CfTransform>,
}
impl ImportPlan {
pub(crate) fn new(
name: String,
dtype: ElementDtype,
shape: Vec<u64>,
chunk_shape: Vec<u64>,
cf: Option<CfTransform>,
) -> Self {
Self {
name,
dtype,
shape,
chunk_shape,
cf,
zarr_array_rel: None,
zarr_zstd: false,
import_attrs: BTreeMap::new(),
import_dim_names: None,
import_coords: None,
}
}
pub(crate) fn with_zarr(mut self, array_rel: String, zstd: bool) -> Self {
self.zarr_array_rel = Some(array_rel);
self.zarr_zstd = zstd;
self
}
pub(crate) fn with_import(
mut self,
attrs: BTreeMap<String, String>,
dim_names: Option<Vec<String>>,
coords: Option<BTreeMap<String, CoordAxisV1>>,
) -> Self {
self.import_attrs = attrs;
self.import_dim_names = dim_names;
self.import_coords = coords;
self
}
pub(crate) fn tile_read<'a>(&'a self, job: &'a StreamTileJob<'_>) -> ImportTileRead<'a> {
ImportTileRead {
dtype: self.dtype,
shape: &self.shape,
chunk_shape: &self.chunk_shape,
chunk_coord: &job.chunk_coord[..job.ndim],
ndim: job.ndim,
cf: self.cf,
}
}
}
pub(crate) fn chunk_shape_for_import(shape: &[u64], source_chunks: Option<Vec<usize>>) -> Vec<u64> {
if let Some(chunks) = source_chunks
&& chunks.len() == shape.len()
&& chunks.iter().all(|&c| c > 0)
{
let mut out = Vec::with_capacity(chunks.len());
for (&dim, &chunk) in shape.iter().zip(chunks.iter()) {
let c = u64::try_from(chunk).unwrap_or(dim);
out.push(c.min(dim).max(1));
}
return out;
}
shape.to_vec()
}
pub(crate) fn write_plans_streaming(
output: &std::path::Path,
plans: &[ImportPlan],
parallel_jobs: usize,
fill_tile: impl Fn(&StreamTileJob<'_>, &mut [u8]) -> Result<(), ConvertError> + Sync + Send,
progress_hook: Option<&mut StreamWriteProgress<'_>>,
) -> Result<(), ConvertError> {
let mut metas: Vec<ArrayWriteMeta<'_>> = Vec::with_capacity(plans.len());
for plan in plans {
metas.push(ArrayWriteMeta::row_major(
&plan.name,
plan.dtype.wire_tag(),
&plan.shape,
&plan.chunk_shape,
None,
));
}
write_multi_raw_array_streaming(
output,
&metas,
parallel_jobs,
|job, buf| match fill_tile(job, buf) {
Ok(()) => Ok(()),
Err(ConvertError::Catalog(c)) => Err(c),
Err(e) => Err(CatalogError::Io(std::io::Error::other(e.to_string()))),
},
progress_hook,
)
.map_err(ConvertError::from)
}
pub(crate) fn ensure_non_empty(
path: &std::path::Path,
names: &[String],
) -> Result<(), ConvertError> {
if names.is_empty() {
return Err(ConvertError::NoDatasets {
path: path.display().to_string(),
});
}
Ok(())
}