#![doc = include_str!("../doc/version_compatibility_matrix.md")]
#![cfg_attr(docsrs, feature(doc_cfg))]
mod sync;
#[cfg(feature = "async")]
mod r#async;
use zarrs_storage::{StorageError, StoreKey, StoreKeyError, StorePrefix, StorePrefixError};
use rc_zip::parse::Entry;
use thiserror::Error;
use std::collections::HashMap;
use std::{
path::{Path, PathBuf},
sync::Arc,
};
#[derive(Debug, Clone, PartialEq, Eq)]
enum ZipEntry {
Key(StoreKey),
Prefix(StorePrefix),
}
impl ZipEntry {
fn as_str(&self) -> &str {
match self {
ZipEntry::Key(k) => k.as_str(),
ZipEntry::Prefix(p) => p.as_str(),
}
}
}
pub struct ZipStorageAdapter<TStorage: ?Sized> {
size: u64,
storage: Arc<TStorage>,
key: StoreKey,
entries: HashMap<StoreKey, Entry>,
sorted_entries: Vec<ZipEntry>,
}
impl<TStorage: ?Sized> ZipStorageAdapter<TStorage> {
fn strip_zip_path_prefix<'a>(name: &'a str, zip_path: &Path) -> Option<&'a str> {
let prefix = zip_path.to_str().unwrap_or("");
name.strip_prefix(prefix).filter(|&n| !n.is_empty())
}
fn get_entry(&self, key: &StoreKey) -> Option<&Entry> {
self.entries.get(key)
}
fn entries_with_prefix(&self, prefix: &StorePrefix) -> &[ZipEntry] {
let prefix_str = prefix.as_str();
let start = self
.sorted_entries
.partition_point(|e| e.as_str() < prefix_str);
let end = self.sorted_entries[start..]
.partition_point(|e| e.as_str().starts_with(prefix_str))
+ start;
&self.sorted_entries[start..end]
}
fn immediate_child_prefix(key: &StoreKey, prefix: &StorePrefix) -> Option<StorePrefix> {
let key_str = key.as_str();
let prefix_str = prefix.as_str();
let suffix = key_str.strip_prefix(prefix_str)?;
if let Some(slash_pos) = suffix.find('/') {
let child = &suffix[..=slash_pos];
let full_prefix = format!("{prefix_str}{child}");
StorePrefix::try_from(full_prefix.as_str()).ok()
} else {
None
}
}
}
#[derive(Debug, Error)]
pub enum ZipStorageAdapterCreateError {
#[error(transparent)]
IOError(#[from] std::io::Error),
#[error("{0} is an existing directory, not a zip file")]
ExistingDir(PathBuf),
#[error("{0}")]
ZipError(String),
#[error(transparent)]
StorageError(#[from] StorageError),
#[error(transparent)]
InvalidStoreKey(#[from] StoreKeyError),
#[error(transparent)]
InvalidStorePrefix(#[from] StorePrefixError),
}