use crate::io::{LittleEndian, WriteBytesExt};
use crate::{
encryption::EncryptionProvider,
file::{CURRENT_VERSION_FILE, fsync_directory, rewrite_atomic},
fs::{Fs, SyncMode},
manifest_blocks::{current_digest, writer::ManifestArchiveWriter},
runtime_config::RuntimeConfig,
version::{Version, edit::BootstrapEdit, edit_log},
};
use alloc::sync::Arc;
#[cfg(not(feature = "std"))]
use alloc::vec::Vec;
use crate::path::Path;
pub fn persist_version(
folder: &Path,
version: &Version,
comparator_name: &str,
fs: &dyn Fs,
runtime: Arc<RuntimeConfig>,
encryption: Option<Arc<dyn EncryptionProvider>>,
sync_mode: SyncMode,
) -> crate::Result<u64> {
if comparator_name.len() > crate::comparator::MAX_COMPARATOR_NAME_BYTES {
return Err(crate::Error::from(crate::io::Error::new(
crate::io::ErrorKind::InvalidInput,
format!(
"comparator name is {} bytes (max {})",
comparator_name.len(),
crate::comparator::MAX_COMPARATOR_NAME_BYTES,
),
)));
}
log::trace!(
"Persisting version {} in {}",
version.id(),
folder.display(),
);
let path = folder.join(format!("v{}", version.id()));
let base = (!version.fits_snapshot()).then(|| version.snapshot_base());
let bootstrap = match &base {
Some(base) => {
let mut payload = Vec::new();
version.diff(base)?.encode(&mut payload)?;
let record = BootstrapEdit::of(&payload)?;
Some((payload, record))
}
None => None,
};
let mut writer = ManifestArchiveWriter::create(&path, fs, runtime, encryption, sync_mode)?;
base.as_ref()
.unwrap_or(version)
.encode_into(&mut writer, comparator_name)?;
if let Some((_, record)) = &bootstrap {
writer.start("bootstrap_edit")?;
writer.write_u32::<LittleEndian>(record.len)?;
writer.write_u64::<LittleEndian>(record.digest)?;
}
let footer = writer.finish()?;
let log_path = folder.join(format!("edits-{}", version.id()));
let log_bytes = if let Some((payload, _)) = &bootstrap {
edit_log::write_log(fs, &log_path, payload, sync_mode)?
} else {
match fs.remove_file(&log_path) {
Ok(()) => {}
Err(e) if e.kind() == crate::io::ErrorKind::NotFound => {}
Err(e) => return Err(e.into()),
}
0
};
fsync_directory(folder, fs, sync_mode)?;
let checksum = current_digest::compute(version.id(), &footer)?;
let mut current_file_content = vec![];
current_file_content.write_u64::<LittleEndian>(version.id())?;
current_file_content.write_u128::<LittleEndian>(checksum)?;
current_file_content.write_u8(0)?;
rewrite_atomic(
&folder.join(CURRENT_VERSION_FILE),
¤t_file_content,
fs,
sync_mode,
)?;
Ok(log_bytes)
}