use std::path::Path;
use serde::Serialize;
use void_core::shard::ShardWriter;
use void_core::store::{FsStore, ObjectStoreExt};
use crate::context::{open_repo, void_err_to_cli};
use crate::output::{run_command, CliError, CliOptions};
#[derive(Debug)]
pub struct CreateShardArgs {
pub files: Vec<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct CreateShardOutput {
pub cid: String,
pub file_count: usize,
pub uncompressed_size: usize,
pub encrypted_size: usize,
}
pub fn run(cwd: &Path, args: CreateShardArgs, opts: &CliOptions) -> Result<(), CliError> {
run_command("create-shard", opts, |ctx| {
if args.files.is_empty() {
return Err(CliError::invalid_args("at least one file is required"));
}
ctx.progress("Creating shard...");
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 mut writer = ShardWriter::new();
for file_path in &args.files {
let full_path = if Path::new(file_path).is_absolute() {
Path::new(file_path).to_path_buf()
} else {
cwd.join(file_path)
};
let content = std::fs::read(&full_path)
.map_err(|e| CliError::not_found(format!("cannot read {}: {}", file_path, e)))?;
writer
.add_file(file_path, &content)
.map_err(void_err_to_cli)?;
ctx.progress(format!("Added: {}", file_path));
}
let file_count = writer.file_count() as usize;
let uncompressed_size = writer.body_size();
let shard_bytes = writer.finish(3).map_err(void_err_to_cli)?;
let encrypted = vault.seal_shard(&shard_bytes)
.map_err(|e| CliError::encryption_error(format!("encryption failed: {}", e)))?;
let encrypted_size = encrypted.as_bytes().len();
let shard_cid = store.put_blob(&encrypted).map_err(void_err_to_cli)?;
let cid_str = shard_cid.to_string();
if !ctx.use_json() {
ctx.info(format!("Created shard: {}", cid_str));
ctx.info(format!("Files: {}", file_count));
ctx.info(format!("Uncompressed: {} bytes", uncompressed_size));
ctx.info(format!("Encrypted: {} bytes", encrypted_size));
}
Ok(CreateShardOutput {
cid: cid_str,
file_count,
uncompressed_size,
encrypted_size,
})
})
}