🦀 Rust Guard Improvement Report
Improvement 1: Merge Duplicate search_issues / search_pull_requests Match Arms
Category: Duplication
File(s): guards/github-guard/rust-guard/src/labels/tool_rules.rs
Effort: Small (< 15 min)
Risk: Low
Problem
The search_issues arm (lines 223–236) and search_pull_requests arm (lines 315–328) in apply_tool_labels are byte-for-byte identical except for the desc format string prefix. Every future change to the shared logic must be applied twice, and the duplication is easy to miss.
// lines 223–236
"search_issues" => {
let (s_owner, s_repo, s_repo_id) = resolve_search_scope(tool_args, &owner, &repo);
if !s_repo_id.is_empty() {
desc = format!("search_issues:{}", s_repo_id); // ← only difference
secrecy =
apply_repo_visibility_secrecy(&s_owner, &s_repo, &s_repo_id, secrecy, ctx);
let search_repo_private = repo_private
.or_else(|| super::backend::is_repo_private(&s_owner, &s_repo));
integrity = private_writer_integrity(&s_repo_id, search_repo_private, ctx);
} else {
integrity = vec![];
}
}
// lines 315–328 (identical except for the string "search_pull_requests")
"search_pull_requests" => {
let (s_owner, s_repo, s_repo_id) = resolve_search_scope(tool_args, &owner, &repo);
if !s_repo_id.is_empty() {
desc = format!("search_pull_requests:{}", s_repo_id); // ← only difference
secrecy =
apply_repo_visibility_secrecy(&s_owner, &s_repo, &s_repo_id, secrecy, ctx);
let search_repo_private = repo_private
.or_else(|| super::backend::is_repo_private(&s_owner, &s_repo));
integrity = private_writer_integrity(&s_repo_id, search_repo_private, ctx);
} else {
integrity = vec![];
}
}
Suggested Change
Combine the two arms into one, using the already-in-scope tool_name variable for the desc prefix:
Before
// Search issues: extract repo scope from query or tool_args when available
"search_issues" => {
let (s_owner, s_repo, s_repo_id) = resolve_search_scope(tool_args, &owner, &repo);
if !s_repo_id.is_empty() {
desc = format!("search_issues:{}", s_repo_id);
secrecy =
apply_repo_visibility_secrecy(&s_owner, &s_repo, &s_repo_id, secrecy, ctx);
// Use the search query's repo for privacy check when tool_args lacks owner/repo
let search_repo_private = repo_private
.or_else(|| super::backend::is_repo_private(&s_owner, &s_repo));
integrity = private_writer_integrity(&s_repo_id, search_repo_private, ctx);
} else {
integrity = vec![];
}
}
// ... (many arms in between) ...
// Search pull requests: extract repo scope from query or tool_args when available
"search_pull_requests" => {
let (s_owner, s_repo, s_repo_id) = resolve_search_scope(tool_args, &owner, &repo);
if !s_repo_id.is_empty() {
desc = format!("search_pull_requests:{}", s_repo_id);
secrecy =
apply_repo_visibility_secrecy(&s_owner, &s_repo, &s_repo_id, secrecy, ctx);
// Use the search query's repo for privacy check when tool_args lacks owner/repo
let search_repo_private = repo_private
.or_else(|| super::backend::is_repo_private(&s_owner, &s_repo));
integrity = private_writer_integrity(&s_repo_id, search_repo_private, ctx);
} else {
integrity = vec![];
}
}
After
// Search issues / pull requests: extract repo scope from query or tool_args when available
"search_issues" | "search_pull_requests" => {
let (s_owner, s_repo, s_repo_id) = resolve_search_scope(tool_args, &owner, &repo);
if !s_repo_id.is_empty() {
desc = format!("{}:{}", tool_name, s_repo_id);
secrecy =
apply_repo_visibility_secrecy(&s_owner, &s_repo, &s_repo_id, secrecy, ctx);
// Use the search query's repo for privacy check when tool_args lacks owner/repo
let search_repo_private = repo_private
.or_else(|| super::backend::is_repo_private(&s_owner, &s_repo));
integrity = private_writer_integrity(&s_repo_id, search_repo_private, ctx);
} else {
integrity = vec![];
}
}
The search_pull_requests arm can be removed entirely (it was between the PR block and the Commits block — keeping the merged arm near the top, beside search_issues, is the natural placement).
Why This Matters
- Eliminates a maintenance hazard: any future tweak (e.g. adding a
desc fallback, changing the empty-scope behaviour) currently must be applied to two identical blocks.
- Reduces
apply_tool_labels by ~14 lines.
- The
desc values produced are unchanged ("search_issues:owner/repo" / "search_pull_requests:owner/repo").
- Zero behaviour change — verified by existing tests for both tool names.
Improvement 2: Remove Internal Utilities from labels/mod.rs pub use Re-export
Category: API Surface
File(s): guards/github-guard/rust-guard/src/labels/mod.rs
Effort: Small (< 15 min)
Risk: Low
Problem
labels/mod.rs re-exports a bundle of items from helpers (lines 41–52):
pub use helpers::{
blocked_integrity, commit_integrity, ensure_integrity_baseline, extract_graphql_nodes,
extract_graphql_single_object, extract_items_array,
extract_number_as_string, extract_repo_from_item, extract_repo_info,
extract_repo_info_from_search_query, get_bool_or, get_nested_str, get_str_or,
has_author_association, is_blocked_user, is_graphql_wrapper, is_mcp_text_wrapper,
is_search_result_wrapper, issue_integrity, limit_items_with_log, make_item_path,
merged_integrity, none_integrity, pr_integrity, private_scope_label, private_user_label,
project_github_label, reader_integrity, search_result_total_count,
writer_integrity, MinIntegrity, PolicyContext, PolicyScopeEntry, ScopeKind,
};
Five of these — get_str_or, get_nested_str, get_bool_or, make_item_path, and has_author_association — are low-level JSON accessor utilities. They are never used outside the labels/ submodule tree (not in lib.rs, not anywhere else in the crate). Their presence in the re-export list:
- Widens the apparent public interface of the
labels module with implementation details.
- Makes it ambiguous which helpers are semantically part of the labels API vs. incidental utilities.
- Increases the surface area that readers need to understand when studying the module.
Suggested Change
Remove the five utility names from the pub use list, and import them explicitly inside the mod tests block where they're needed.
Before
// labels/mod.rs, lines 41–52
pub use helpers::{
blocked_integrity, commit_integrity, ensure_integrity_baseline, extract_graphql_nodes,
extract_graphql_single_object, extract_items_array,
extract_number_as_string, extract_repo_from_item, extract_repo_info,
extract_repo_info_from_search_query, get_bool_or, get_nested_str, get_str_or,
has_author_association, is_blocked_user, is_graphql_wrapper, is_mcp_text_wrapper,
is_search_result_wrapper, issue_integrity, limit_items_with_log, make_item_path,
merged_integrity, none_integrity, pr_integrity, private_scope_label, private_user_label,
project_github_label, reader_integrity, search_result_total_count,
writer_integrity, MinIntegrity, PolicyContext, PolicyScopeEntry, ScopeKind,
};
// labels/mod.rs, line 153
mod tests {
use super::*;
// ...
After
// labels/mod.rs, lines 41–52 — remove the five utilities
pub use helpers::{
blocked_integrity, commit_integrity, ensure_integrity_baseline, extract_graphql_nodes,
extract_graphql_single_object, extract_items_array,
extract_number_as_string, extract_repo_from_item, extract_repo_info,
extract_repo_info_from_search_query, is_blocked_user, is_graphql_wrapper, is_mcp_text_wrapper,
is_search_result_wrapper, issue_integrity, limit_items_with_log,
merged_integrity, none_integrity, pr_integrity, private_scope_label, private_user_label,
project_github_label, reader_integrity, search_result_total_count,
writer_integrity, MinIntegrity, PolicyContext, PolicyScopeEntry, ScopeKind,
};
// labels/mod.rs — inside mod tests { ... }
mod tests {
use super::*;
use super::helpers::{get_bool_or, get_nested_str, get_str_or, has_author_association, make_item_path};
// ...
Why This Matters
- Makes the
labels module boundary cleaner: the re-export block becomes a list of semantically meaningful labeling concepts, not a mix of concepts and low-level JSON plumbing.
- The five functions remain
pub in helpers.rs and fully accessible via super::helpers::* — no behavior changes.
- Readers of
labels/mod.rs immediately understand which helpers are part of the module's contract.
Codebase Health Summary
- Total Rust files: 9
- Total lines: 12,018
- Areas analyzed:
lib.rs, tools.rs, labels/mod.rs, labels/helpers.rs, labels/tool_rules.rs, labels/backend.rs, labels/constants.rs, labels/response_items.rs, labels/response_paths.rs
- Areas with no further improvements:
tools.rs (well-structured, good test coverage), labels/constants.rs (clean), labels/backend.rs (complex but well-organized)
Generated by Rust Guard Improver • Run: §24447416059
Generated by Rust Guard Improver · ● 2.7M · ◷
🦀 Rust Guard Improvement Report
Improvement 1: Merge Duplicate
search_issues/search_pull_requestsMatch ArmsCategory: Duplication
File(s):
guards/github-guard/rust-guard/src/labels/tool_rules.rsEffort: Small (< 15 min)
Risk: Low
Problem
The
search_issuesarm (lines 223–236) andsearch_pull_requestsarm (lines 315–328) inapply_tool_labelsare byte-for-byte identical except for thedescformat string prefix. Every future change to the shared logic must be applied twice, and the duplication is easy to miss.Suggested Change
Combine the two arms into one, using the already-in-scope
tool_namevariable for thedescprefix:Before
After
The
search_pull_requestsarm can be removed entirely (it was between the PR block and the Commits block — keeping the merged arm near the top, besidesearch_issues, is the natural placement).Why This Matters
descfallback, changing the empty-scope behaviour) currently must be applied to two identical blocks.apply_tool_labelsby ~14 lines.descvalues produced are unchanged ("search_issues:owner/repo"/"search_pull_requests:owner/repo").Improvement 2: Remove Internal Utilities from
labels/mod.rspub useRe-exportCategory: API Surface
File(s):
guards/github-guard/rust-guard/src/labels/mod.rsEffort: Small (< 15 min)
Risk: Low
Problem
labels/mod.rsre-exports a bundle of items fromhelpers(lines 41–52):Five of these —
get_str_or,get_nested_str,get_bool_or,make_item_path, andhas_author_association— are low-level JSON accessor utilities. They are never used outside thelabels/submodule tree (not inlib.rs, not anywhere else in the crate). Their presence in the re-export list:labelsmodule with implementation details.Suggested Change
Remove the five utility names from the
pub uselist, and import them explicitly inside themod testsblock where they're needed.Before
After
Why This Matters
labelsmodule boundary cleaner: the re-export block becomes a list of semantically meaningful labeling concepts, not a mix of concepts and low-level JSON plumbing.pubinhelpers.rsand fully accessible viasuper::helpers::*— no behavior changes.labels/mod.rsimmediately understand which helpers are part of the module's contract.Codebase Health Summary
lib.rs,tools.rs,labels/mod.rs,labels/helpers.rs,labels/tool_rules.rs,labels/backend.rs,labels/constants.rs,labels/response_items.rs,labels/response_paths.rstools.rs(well-structured, good test coverage),labels/constants.rs(clean),labels/backend.rs(complex but well-organized)Generated by Rust Guard Improver • Run: §24447416059