use crate::command::Command;
use clap::{Parser, ValueHint};
use pna::prelude::*;
use std::{fs, path::PathBuf};
use tabled::{builder::Builder as TableBuilder, settings::Style as TableStyle};
#[derive(Parser, Clone, Eq, PartialEq, Hash, Debug)]
#[command(args_conflicts_with_subcommands = true, arg_required_else_help = true)]
pub(crate) struct ChunkCommand {
#[command(subcommand)]
command: ChunkCommands,
}
impl Command for ChunkCommand {
#[inline]
fn execute(self, ctx: &crate::cli::GlobalContext) -> anyhow::Result<()> {
match self.command {
ChunkCommands::List(cmd) => cmd.execute(ctx),
}
}
}
#[derive(Parser, Clone, Eq, PartialEq, Hash, Debug)]
pub(crate) enum ChunkCommands {
#[command(about = "List chunks")]
List(ListCommand),
}
#[derive(Parser, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
#[clap(disable_help_flag = true)]
pub(crate) struct ListCommand {
#[arg(short, long, help = "Display chunk body")]
pub(crate) long: bool,
#[arg(short, long, help = "Add a header row to each column")]
pub(crate) header: bool,
#[arg(short = 'f', long = "file", value_hint = ValueHint::FilePath)]
archive: PathBuf,
#[arg(long, action = clap::ArgAction::Help, help = "Print help")]
help: (),
}
impl Command for ListCommand {
#[inline]
fn execute(self, _ctx: &crate::cli::GlobalContext) -> anyhow::Result<()> {
list_archive_chunks(self)
}
}
#[hooq::hooq(anyhow)]
fn list_archive_chunks(args: ListCommand) -> anyhow::Result<()> {
let archive = fs::File::open(args.archive)?;
let mut builder = TableBuilder::new();
if args.header {
builder.push_record(
["Index", "Type", "Size", "Offset"]
.into_iter()
.chain(args.long.then_some("Body")),
)
}
let mut offset = pna::PNA_HEADER.len();
let mut idx = 0;
for chunk in pna::read_as_chunks(archive)? {
let chunk = chunk?;
idx += 1;
builder.push_record(
[
idx.to_string(),
chunk.ty().to_string(),
chunk.length().to_string(),
format!("{offset:#06x}"),
]
.into_iter()
.chain(args.long.then(|| {
let data = chunk.data();
match std::str::from_utf8(data) {
Ok(s) => s.to_string(),
Err(_) => format!("{:#x}", const_hex::display(data)),
}
})),
);
offset += chunk.length() as usize + std::mem::size_of::<u32>() * 3;
}
let mut table = builder.build();
table.with(TableStyle::empty());
println!("{table}");
Ok(())
}