Skip to content

Commit 4f3b748

Browse files
refactor: reduce complexity of execute_impl in comment_on_work_item.rs (#1521)
Extract validate_target_policy helper to flatten the 3-arm match/nested-match block that combined allows_id, area_path_prefix, and get_work_item_area_path into a single nesting depth 6 block. The new helper returns Ok(None) for "allowed", Ok(Some(msg)) for "rejected", keeping execute_impl to a single early-return guard. Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent c641210 commit 4f3b748

1 file changed

Lines changed: 65 additions & 51 deletions

File tree

src/safe_outputs/comment_on_work_item.rs

Lines changed: 65 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,66 @@ impl Default for CommentOnWorkItemConfig {
118118
}
119119
}
120120

121+
/// Validate that a work item is allowed by the configured target policy.
122+
///
123+
/// Returns `Ok(None)` when the work item is allowed, `Ok(Some(msg))` when it is
124+
/// rejected by policy (the caller should return `ExecutionResult::failure(msg)`),
125+
/// or `Err(…)` on an unexpected infrastructure failure.
126+
async fn validate_target_policy(
127+
target: &CommentTarget,
128+
client: &reqwest::Client,
129+
org_url: &str,
130+
project: &str,
131+
token: &str,
132+
work_item_id: i64,
133+
) -> anyhow::Result<Option<String>> {
134+
match target.allows_id(work_item_id) {
135+
Some(true) => {
136+
debug!("Work item #{} allowed by target policy", work_item_id);
137+
Ok(None)
138+
}
139+
Some(false) => Ok(Some(format!(
140+
"Work item #{} is not in the allowed target set",
141+
work_item_id
142+
))),
143+
None => {
144+
// Area path validation — `allows_id` returns `None` only for
145+
// `StringTarget(s != "*")`, so `area_path_prefix` is always `Some` here.
146+
let prefix = target
147+
.area_path_prefix()
148+
.expect("allows_id returned None but area_path_prefix is also None");
149+
debug!(
150+
"Validating area path for work item #{} against prefix '{}'",
151+
work_item_id, prefix
152+
);
153+
match get_work_item_area_path(client, org_url, project, token, work_item_id).await {
154+
Ok(area_path) => {
155+
// ADO area paths are case-insensitive and use backslash separators.
156+
// Require the match to land on a path boundary so that prefix "4x4"
157+
// doesn't accidentally match "4x4Production".
158+
let ap = area_path.to_lowercase();
159+
let pf = prefix.to_lowercase();
160+
let is_match =
161+
ap == pf || (ap.starts_with(&*pf) && ap[pf.len()..].starts_with('\\'));
162+
if is_match {
163+
debug!("Area path '{}' validated against '{}'", area_path, prefix);
164+
Ok(None)
165+
} else {
166+
Ok(Some(format!(
167+
"Work item #{} has area path '{}' which is not under allowed prefix '{}'",
168+
work_item_id, area_path, prefix
169+
)))
170+
}
171+
}
172+
Err(e) => Ok(Some(format!(
173+
"Failed to validate area path for work item #{}: {}",
174+
work_item_id, e
175+
))),
176+
}
177+
}
178+
}
179+
}
180+
121181
/// Fetch a work item's area path from the ADO API
122182
async fn get_work_item_area_path(
123183
client: &reqwest::Client,
@@ -216,57 +276,11 @@ impl Executor for CommentOnWorkItemResult {
216276
let client = reqwest::Client::new();
217277

218278
// Validate work item ID against target policy
219-
match target.allows_id(self.work_item_id) {
220-
Some(true) => {
221-
debug!("Work item #{} allowed by target policy", self.work_item_id);
222-
}
223-
Some(false) => {
224-
return Ok(ExecutionResult::failure(format!(
225-
"Work item #{} is not in the allowed target set",
226-
self.work_item_id
227-
)));
228-
}
229-
None => {
230-
// Area path validation — need to fetch the work item.
231-
// Invariant: allows_id returns None only for StringTarget(s != "*"),
232-
// and area_path_prefix returns Some for exactly that case.
233-
let prefix = match target.area_path_prefix() {
234-
Some(p) => p,
235-
None => {
236-
unreachable!("allows_id returned None but area_path_prefix is also None")
237-
}
238-
};
239-
debug!(
240-
"Validating area path for work item #{} against prefix '{}'",
241-
self.work_item_id, prefix
242-
);
243-
match get_work_item_area_path(&client, org_url, project, token, self.work_item_id)
244-
.await
245-
{
246-
Ok(area_path) => {
247-
// ADO area paths are case-insensitive and use backslash separators.
248-
// Require the match to land on a path boundary so that prefix "4x4"
249-
// doesn't accidentally match "4x4Production".
250-
let ap = area_path.to_lowercase();
251-
let pf = prefix.to_lowercase();
252-
let is_match =
253-
ap == pf || (ap.starts_with(&*pf) && ap[pf.len()..].starts_with('\\'));
254-
if !is_match {
255-
return Ok(ExecutionResult::failure(format!(
256-
"Work item #{} has area path '{}' which is not under allowed prefix '{}'",
257-
self.work_item_id, area_path, prefix
258-
)));
259-
}
260-
debug!("Area path '{}' validated against '{}'", area_path, prefix);
261-
}
262-
Err(e) => {
263-
return Ok(ExecutionResult::failure(format!(
264-
"Failed to validate area path for work item #{}: {}",
265-
self.work_item_id, e
266-
)));
267-
}
268-
}
269-
}
279+
if let Some(rejection_msg) =
280+
validate_target_policy(target, &client, org_url, project, token, self.work_item_id)
281+
.await?
282+
{
283+
return Ok(ExecutionResult::failure(rejection_msg));
270284
}
271285

272286
// Build the Azure DevOps REST API URL for adding a comment

0 commit comments

Comments
 (0)