The main program
You are now able to parse skippable frames. Let’s write a program which displays, for each skippable frame, its magic number and content. Also, the program will check that the file is a well-formed zstd file.
Command-line arguments
We want our program to act like this:
$ cargo run -- --help
Usage: net7212 [OPTIONS] <FILE_NAME>
Arguments:
<FILE_NAME> File name to decompress
Options:
-i, --info Dump information about frames instead of outputting the result
-h, --help Print help
-V, --version Print version
$ cargo run -- --info skippables.zst
SkippableFrame(
SkippableFrame {
magic: 0x184d2a53,
data: [
0x10,
0x20,
0x30,
],
},
)
SkippableFrame(
SkippableFrame {
magic: 0x184d2a51,
data: [
0x42,
],
},
)
(you can download skippables.zst)
Create the main program
✅ Create the
src/main.rsfile containing a main function.
✅ Use
clapwith itsderivefeature to implement command-line arguments handling: declare a struct describing the arguments, deriveclap::Parseron it, and callArgs::parse()inmain().
You can read a file using std::fs::read(). Do not print anything yet, we will do it step by step.
Decoding a frame
As previously explained, a decoding phase must happen after the parsing of a frame when --info is not used as an argument to the main program.
✅ Implement a
decode(self)method on aFramereturning the decoded data in aVec<u8>.
Note that self means that the Frame object no longer exists after decode() has been called. This is expected, as some decoding operations will consume frame data.
So, at this time:
- You can decode a
SkippableFrameas it decodes to nothing (it is skipped after all). - You may use
todo!()while decoding aZstandardFrameas not even the parsing is implemented.
Iterating over the frames
Since all frames must be parsed in sequence, one can use an iterator to transform a forward byte parser into a succession of Frame objects.
✅ Add a
FrameIteratorin theframemodule, which encapsulates aForwardByteParser.
✅ Implement the
Iteratortrait forFrameIterator, theItembeingResult<Frame>.
The iterator’s next() method will:
- Return
Noneif the parser has no content left to indicate the end of the iterator. - Return
Some(Frame::parse(&mut self.parser))otherwise.
Note that this will automatically signal an error if extra characters are present at the end of an otherwise valid file, as the next iteration will attempt to parse another frame which will be invalid.
Writing the main program body
✅ Complete the main program so that it can either print information or decode frames from a zstd file in sequence.
Of course you must stop if the frame iterator returns an error.
Do not forget that:
- You can derive the
Debugtrait automatically on many types. - You can display something implementing the
Debugtrait with the{:?}construct. - You can use an alternate representation with the
{:#?}construct (for example indented structures). - You can use an alternate representation and print numbers in hexadecimal with
{:#x?}.
Also, when --info is not used, the decoded data (even though there is nothing to display yet) must be sent to the standard output. You can use the
write_all() method to write on std::io::stdout() after locking it.
If you use the color_eyre crate as suggested in the general principles, make your main() function return a color_eyre::eyre::Result<()>, call color_eyre::install()? at its beginning, and let color_eyre display a pretty error report if there is one.
Adding tracing information
Since your program will use the standard output when decoding a file, you must not use it yourself to print debugging information. Use the tracing crate to add traces where you need them (tracing::debug!(), tracing::trace!(), …), and install a tracing_subscriber in main() with tracing_subscriber::fmt::init(): traces go to the standard error, and their verbosity is controlled at run time through the RUST_LOG environment variable (e.g., RUST_LOG=trace cargo run -- file.zst).
✅ Check that you can decode a file made only of skippable frames, as well as an empty file (which is a valid zstd file with zero frames). Check that you signal an error if you try to decode an invalid file.