Skip to content

fix: replace panic!() in tree.rs with proper error propagation - #62

Merged
BryanFRD merged 1 commit into
mainfrom
fix/tree-panic-to-result
Apr 8, 2026
Merged

BryanFRD merged 1 commit into
mainfrom
fix/tree-panic-to-result

Conversation

@BryanFRD

@BryanFRD BryanFRD commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Change TreeNode::insert_blob to return Result<()> instead of panicking on path conflicts
  • Use anyhow::bail!() with a descriptive error message including the conflicting path
  • Update all call sites (BulkRepoBuilder::set_file, append_dummy) to propagate errors
  • Update tests to use unwrap() on insert_blob calls and test error case with unwrap_err()

Closes #54

Copilot AI review requested due to automatic review settings April 8, 2026 12:36
@BryanFRD
BryanFRD merged commit 8e7b0bd into main Apr 8, 2026
3 checks passed
@BryanFRD
BryanFRD deleted the fix/tree-panic-to-result branch April 8, 2026 12:38

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR replaces a panic!() in the tree-building logic with structured error propagation so path conflicts surface as actionable Result errors instead of crashing the generator.

Changes:

  • Changed TreeNode::insert_blob to return Result<()> and use anyhow::bail!() on path conflicts
  • Updated BulkRepoBuilder::{set_file,append_dummy} to propagate insert_blob errors
  • Updated unit tests to reflect the new fallible API and added an error-case assertion

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

Comment thread generator/src/tree.rs
Comment on lines 43 to 44
self.entries
.insert(path.to_string(), TreeEntry::Blob(blob_oid));

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

insert_blob still allows a tree/blob conflict in the opposite direction: if a directory already exists at path (i.e., entries contains TreeEntry::Tree for that key) and you later call insert_blob("a", ...) after inserting something under a/, the else branch will silently replace the tree with a blob and effectively discard the subtree. Consider detecting TreeEntry::Tree(_) here and returning a conflict error instead of overwriting.

Suggested change
self.entries
.insert(path.to_string(), TreeEntry::Blob(blob_oid));
match self.entries.get(path) {
Some(TreeEntry::Tree(_)) => {
bail!("path conflict: '{path}' is a tree, not a blob")
}
Some(TreeEntry::Blob(_)) | None => {
self.entries
.insert(path.to_string(), TreeEntry::Blob(blob_oid));
}
}

Copilot uses AI. Check for mistakes.
Comment thread generator/src/tree.rs
Comment on lines +37 to +40
TreeEntry::Tree(node) => node.insert_blob(rest, blob_oid)?,
_ => {
bail!("path conflict: '{dir}' is a blob, not a tree (while inserting '{path}')")
}

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

The conflict error message can lose the full path once recursion descends (e.g., a conflict under a/b/... will report inserting b/... because path is the per-node relative path). If this error is intended for end users, consider threading the original full path (or an accumulated prefix) through recursion so the message consistently includes the full conflicting path.

Copilot uses AI. Check for mistakes.
Comment thread generator/src/tree.rs
Comment on lines +27 to 30
pub fn insert_blob(&mut self, path: &str, blob_oid: Oid) -> Result<()> {
self.invalidate();
if let Some(slash) = path.find('/') {
let dir = &path[..slash];

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

self.invalidate() runs before any conflict checks. On a conflict this leaves the tree logically unchanged but clears cached_oid, causing unnecessary recomputation if the caller recovers and continues. Consider invalidating only after a successful insertion (or only on the branch where a mutation actually happens).

Copilot uses AI. Check for mistakes.
Comment thread generator/src/tree.rs
// Try to insert a nested path where "f" is already a blob
root.insert_blob("f/g.txt", blob);
let err = root.insert_blob("f/g.txt", blob).unwrap_err();
assert!(err.to_string().contains("path conflict"));

Copilot AI Apr 8, 2026

Copy link

Choose a reason for hiding this comment

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

The new test only asserts the error contains the generic substring "path conflict". Since the PR goal is a descriptive message including the conflicting path, consider asserting that the error string also contains the conflicting segment (e.g. "f") and/or the attempted path (e.g. "f/g.txt") so regressions in the message content are caught.

Suggested change
assert!(err.to_string().contains("path conflict"));
let err_str = err.to_string();
assert!(err_str.contains("path conflict"));
assert!(err_str.contains("f"));
assert!(err_str.contains("f/g.txt"));

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.

fix: replace panic!() in tree.rs with proper error handling

2 participants