Skip to content

feat: Added initial support for inspecting and converting a trace#39

Open
Salmony wants to merge 1 commit intomainfrom
feature/salmony/add-converter
Open

feat: Added initial support for inspecting and converting a trace#39
Salmony wants to merge 1 commit intomainfrom
feature/salmony/add-converter

Conversation

@Salmony
Copy link
Copy Markdown
Member

@Salmony Salmony commented Mar 26, 2026

No description provided.

@Salmony Salmony requested a review from Copilot March 26, 2026 22:32
@Salmony Salmony marked this pull request as ready for review March 26, 2026 22:35
@Salmony Salmony requested a review from miline as a code owner March 26, 2026 22:35
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds initial CLI support for inspecting Z-Wave ZLF traces and converting them to JSON, alongside expanding ZLF record classification to recognize attachment records.

Changes:

  • Add an Inspect CLI subcommand to summarize ZLF contents (attachments/data/other).
  • Add a Convert CLI path for converting .zlf traces to pretty-printed JSON (via serde_json), including serde derives on core frame types.
  • Extend ZLF parsing with an Attachment API type / record variant.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
crates/core/src/zlf/types.rs Adds Attachment API type; changes API type decoding behavior.
crates/core/src/zlf/reader.rs Introduces ZlfRecord::Attachment and routes attachment records during parsing.
crates/core/src/types.rs Adds Serialize/Deserialize derives to enable JSON export of Frame and Region.
crates/cli/src/main.rs Adds inspect command and implements .zlf.json conversion.
crates/cli/src/generator/generator.rs Skips non-frame records (e.g., attachments).
crates/cli/Cargo.toml Adds serde_json dependency for JSON output.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/cli/src/main.rs
Comment on lines +492 to +496
let input_extension = std::path::Path::new(input)
.extension()
.and_then(std::ffi::OsStr::to_str)
.unwrap_or("")
.to_lowercase();
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path::new takes a reference; input here is a String, so Path::new(input) won’t compile. Use Path::new(&input) (or Path::new(input.as_str())) when extracting the extension.

Copilot uses AI. Check for mistakes.
Comment thread crates/cli/src/main.rs
Comment on lines +504 to +508
let output_extension = std::path::Path::new(output)
.extension()
.and_then(std::ffi::OsStr::to_str)
.unwrap_or("")
.to_lowercase();
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as above for output: Path::new(output) expects a reference, so this won’t compile as written. Use Path::new(&output) (or output.as_str()) before calling .extension().

Copilot uses AI. Check for mistakes.
Comment thread crates/cli/src/main.rs
Comment on lines +472 to +475
zlf::ZlfRecord::Data(data_frame) => {
data_frame_counter += 1;
},
zlf::ZlfRecord::Other(raw_frame) => {
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data_frame and raw_frame are bound but never used. If they’re intentionally unused, rename them to _data_frame/_raw_frame (or use _) to avoid unused-variable warnings.

Suggested change
zlf::ZlfRecord::Data(data_frame) => {
data_frame_counter += 1;
},
zlf::ZlfRecord::Other(raw_frame) => {
zlf::ZlfRecord::Data(_data_frame) => {
data_frame_counter += 1;
},
zlf::ZlfRecord::Other(_raw_frame) => {

Copilot uses AI. Check for mistakes.
Comment thread crates/cli/src/main.rs
match result {
zniffer_parser::ParserResult::ValidFrame { frame } => {

raw_frames.push(frame.clone());
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ParserResult::ValidFrame { frame } already yields an owned Frame (the parser clones internally before returning), so raw_frames.push(frame.clone()) does an extra clone. Push frame directly to avoid unnecessary allocations/copies when converting large traces.

Suggested change
raw_frames.push(frame.clone());
raw_frames.push(frame);

Copilot uses AI. Check for mistakes.
Comment on lines 12 to 20
impl From<u8> for ApiType {
fn from(value: u8) -> Self {
match value {
0xF5 => ApiType::Pti,
0xF8 => ApiType::Attachment,
0xFE => ApiType::Zniffer,
_ => ApiType::Unknown(value),
_ => panic!("Unknown API type: 0x{:02X}", value),
}
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ApiType::from(u8) panics on unknown values. Since this parser reads potentially malformed/untrusted trace files, panicking will crash callers and also prevents returning ZlfError::InvalidApiTypeField from the reader. Consider making this a fallible conversion (TryFrom<u8> returning a custom error) or reintroducing an Unknown(u8) variant so unknown values can be handled without aborting the process.

Copilot uses AI. Check for mistakes.
Comment on lines 179 to 189
let mut api_type = [0u8; 1];
self.r.read_exact(&mut api_type)?;
match ApiType::from(api_type[0]) {
ApiType::Unknown(_) => { return Err(ZlfError::InvalidApiTypeField(api_type[0])); },
_ => { },
ApiType::Attachment => { return Ok(Some(ZlfRecord::Attachment)); },
ApiType::Pti | ApiType::Zniffer => {
// TODO: Do we need the frame type?
let frame_type = FrameType::Data;
Ok(Some(ZlfRecord::Other(RawFrame { timestamp: 0, sof: 0, frame_type, payload })))
},
_ => { return Err(ZlfError::InvalidApiTypeField(api_type[0])); },
}
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This match includes an _ arm returning InvalidApiTypeField, but ApiType::from(api_type[0]) currently panics for unknown values, so this error path is unreachable (and the _ arm is also unreachable because all enum variants are already covered). Use a fallible API type decode (e.g., TryFrom<u8>) and map failures to ZlfError::InvalidApiTypeField so invalid inputs don’t crash the process.

Copilot uses AI. Check for mistakes.
Comment on lines +177 to 181
self.frame_counter += 1;

let mut api_type = [0u8; 1];
self.r.read_exact(&mut api_type)?;
match ApiType::from(api_type[0]) {
Copy link

Copilot AI Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

frame_counter is incremented before reading/validating the api_type byte. If read_exact(&mut api_type) fails (e.g., EOF/corruption), the counter will still advance even though no record was successfully parsed. Increment the counter only after the full record (including api_type) has been read and classified.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants