use std::path::Path;
use serde::Serialize;
use void_core::crypto::{AAD_SHARD, EncryptedShard};
use void_core::shard::read_padding_info;
use void_core::store::{FsStore, ObjectStoreExt};
use void_core::cid;
use crate::context::{open_repo, void_err_to_cli};
use crate::output::{run_command, CliError, CliOptions};
#[derive(Debug)]
pub struct ReadShardArgs {
pub cid: String,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct ReadShardOutput {
pub cid: String,
pub encrypted_size: usize,
pub decrypted_size: usize,
pub compressed_size: usize,
pub padding_size: usize,
}
pub fn run(cwd: &Path, args: ReadShardArgs, opts: &CliOptions) -> Result<(), CliError> {
run_command("read-shard", opts, |ctx| {
ctx.progress(format!("Reading shard {}...", args.cid));
let repo = open_repo(cwd)?;
let vault = repo.vault();
let objects_dir = repo.void_dir().join("objects");
let store = FsStore::new(objects_dir).map_err(void_err_to_cli)?;
let parsed_cid = cid::parse(&args.cid)
.map_err(|e| CliError::invalid_args(format!("invalid CID: {}", e)))?;
let encrypted: EncryptedShard = store.get_blob(&parsed_cid).map_err(void_err_to_cli)?;
let encrypted_size = encrypted.as_bytes().len();
let decrypted = vault.decrypt_blob(encrypted.as_bytes(), AAD_SHARD)
.map_err(|e| CliError::encryption_error(format!("decryption failed: {}", e)))?;
let decrypted_size = decrypted.len();
let padding_size = read_padding_info(&decrypted).unwrap_or(0);
let padding_footer = if padding_size > 0 { 16 } else { 0 };
let compressed_size = decrypted_size.saturating_sub(padding_size + padding_footer);
if !ctx.use_json() {
ctx.info(format!("Shard: {}", args.cid));
ctx.info(format!("Encrypted size: {} bytes", encrypted_size));
ctx.info(format!("Compressed size: {} bytes", compressed_size));
if padding_size > 0 {
ctx.info(format!("Padding: {} bytes", padding_size));
}
ctx.info("(Use TreeManifest for file index details)".to_string());
}
Ok(ReadShardOutput {
cid: args.cid,
encrypted_size,
decrypted_size,
compressed_size,
padding_size,
})
})
}