use std::io::{BufRead, Write};
use std::path::PathBuf;
use serde::{Deserialize, Serialize};
use crate::output::shard::error::ShardError;
use crate::output::shard::file::SHARD_SCHEMA_VERSION;
use crate::output::shard::name::normalized_path;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ShardManifestRecord {
pub path: PathBuf,
pub content_hash: String,
pub size: u64,
pub mtime: u64,
pub shard: String,
pub schema_version: u32,
}
impl ShardManifestRecord {
pub fn new(path: PathBuf, content_hash: String, size: u64, mtime: u64, shard: String) -> Self {
Self {
path,
content_hash,
size,
mtime,
shard,
schema_version: SHARD_SCHEMA_VERSION,
}
}
#[must_use]
pub fn compute_hash(bytes: &[u8]) -> String {
blake3::hash(bytes).to_hex().to_string()
}
pub fn from_file_bytes(path: PathBuf, bytes: &[u8], mtime: u64, shard: String) -> Self {
let content_hash = Self::compute_hash(bytes);
let size = bytes.len() as u64;
Self::new(path, content_hash, size, mtime, shard)
}
}
pub fn write_manifest<W: Write>(
mut writer: W,
records: &[ShardManifestRecord],
) -> Result<(), ShardError> {
for (line_index, record) in records.iter().enumerate() {
validate_manifest_record(record, line_index + 1)?;
serde_json::to_writer(&mut writer, record).map_err(ShardError::Encode)?;
writer.write_all(b"\n")?;
}
writer.flush()?;
Ok(())
}
pub fn read_manifest<R: BufRead>(reader: R) -> Result<Vec<ShardManifestRecord>, ShardError> {
let mut records = Vec::new();
for (line_index, line) in reader.lines().enumerate() {
let line_number = line_index + 1;
let line = line?;
if line.trim().is_empty() {
continue;
}
let record: ShardManifestRecord =
serde_json::from_str(&line).map_err(|source| ShardError::Decode {
line: line_number,
source,
})?;
validate_manifest_record(&record, line_number)?;
records.push(record);
}
Ok(records)
}
fn validate_manifest_record(record: &ShardManifestRecord, line: usize) -> Result<(), ShardError> {
if record.schema_version != SHARD_SCHEMA_VERSION {
return Err(ShardError::SchemaVersion {
line,
found: record.schema_version,
expected: SHARD_SCHEMA_VERSION,
});
}
normalized_path(&record.path)?;
Ok(())
}