use clap::Args; use hff_std::*; use std::path::PathBuf; /// Dump out the structure of an hff file. #[derive(Debug, Args)] pub struct Dump { /// The input file. pub input: PathBuf, /// Indent the information by depth? #[arg(long, overrides_with = "_no_indent")] pub indent: bool, /// No indention. #[arg(long = "no-indent")] pub _no_indent: bool, /// Max length to indent. #[arg(long, default_value = "20")] pub max_indent: usize, /// Number of spaces per indent level to use. #[arg(long, default_value = "2")] pub indent_size: usize, /// Dump chunk type, sub-type? #[arg(long, default_value = "false")] pub chunk_types: bool, /// Dump metadata? #[arg(long, default_value = "false")] pub metadata: bool, /// Interpret the metadata as a key string vector table. #[arg(long, default_value = "false", conflicts_with = "as_string_vec")] pub as_ksv: bool, /// Interpret the metadata as a string vector. #[arg(long, conflicts_with = "as_ksv")] pub as_string_vec: bool, } impl Dump { /// Execute the dump subcommand. pub fn execute(self) -> Result<()> { use std::fs::File; // The input must exist and be a single file. if self.input.exists() && self.input.metadata()?.is_file() { // Open the hff and check it is valid. let hff = open(File::open(&self.input)?)?; // Iterate through the content. println!(); println!("----------"); for (depth, table) in hff.depth_first() { self.dump(&hff, depth, &table)?; } println!("----------"); Ok(()) } else { Err(Error::Invalid(format!( "Invalid input: {}", self.input.display().to_string() ))) } } /// Dump information about the provided table. fn dump( &self, hff: &Hff, depth: usize, table: &TableView<'_, StdReader>, ) -> Result<()> { // Always print out the table information. println!( "{} ({} : children: {} chunks: {})", self.indent(depth), table.identifier().to_string(hff.id_type()), table.child_count(), table.chunk_count() ); // Print out the metadata if desired. if self.metadata { self.dump_metadata(hff, depth, table)?; } // Print out chunk types if desired. if self.chunk_types { self.dump_chunk_types(hff.id_type(), depth, table)?; } Ok(()) } /// Dump out the metadata if any. fn dump_metadata( &self, hff: &Hff, depth: usize, table: &TableView<'_, StdReader>, ) -> Result<()> { let metadata = hff.get(table)?; if self.as_ksv { match hff_std::hff_core::utilities::Ksv::from_bytes(metadata.as_slice()) { Ok(ksv) => { println!(" {} {:#?}", self.indent(depth), ksv); } Err(_) => { println!(" {} ", self.indent(depth)); } } } else if self.as_string_vec { match hff_std::hff_core::utilities::StringVec::from_bytes(metadata.as_slice()) { Ok(sv) => { println!(" {} {:#?}", self.indent(depth), sv); } Err(_) => { println!(" {} ", self.indent(depth)); } } } else { match std::str::from_utf8(&metadata) { Ok(s) => { println!(" {}{}", self.indent(depth), s); } Err(_) => { println!( " {} ({:<8} {:<8})", self.indent(depth), metadata.len(), table.offset() ); } } } Ok(()) } /// Dump out the chunk types if any. fn dump_chunk_types( &self, id_type: IdType, depth: usize, table: &TableView<'_, StdReader>, ) -> Result<()> { for chunk in table.chunks() { println!( " {} [{} Len: {}]", self.indent(depth), chunk.identifier().to_string(id_type), chunk.len() ); } Ok(()) } /// Get a string of spaces representing the indent level desired. fn indent(&self, depth: usize) -> String { if self.indent { if self.indent_size * depth < self.max_indent { std::iter::repeat(' ') .take(self.indent_size * depth) .collect::() } else { std::iter::repeat(' ') .take((self.indent_size * self.max_indent) - 3) .collect::() + "-> " } } else { String::new() } } }