From 2032d61f591ce8da56038d652e601818770e873d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 00:57:00 +0000 Subject: [PATCH 1/3] Initial plan From c9322f0eb6fd44acce89f36657d2b3f9c147525d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 00:59:51 +0000 Subject: [PATCH 2/3] fix: use RAII guard for v2 schema expansion Agent-Logs-Url: https://github.com/rawkode/kubectl-ditto/sessions/df015851-ebe2-4560-a7f1-7fd01429615c Co-authored-by: rawkode <145816+rawkode@users.noreply.github.com> --- src/schema.rs | 60 +++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 46 insertions(+), 14 deletions(-) diff --git a/src/schema.rs b/src/schema.rs index 428a7df..3ac3689 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -575,16 +575,19 @@ impl<'a> V2Resolver<'a> { schema } - /// Try to enter expansion of a resolved schema. Returns true if this is - /// a new expansion; false if the schema is already on the call stack (cycle). - fn enter(&self, schema: &Value) -> bool { + /// Try to enter expansion of a resolved schema. Returns `Some(guard)` if + /// this is a new expansion; `None` if the schema is already on the call + /// stack (cycle). + fn enter(&self, schema: &Value) -> Option> { let addr = schema as *const Value as usize; - self.expanding.borrow_mut().insert(addr) - } - - fn leave(&self, schema: &Value) { - let addr = schema as *const Value as usize; - self.expanding.borrow_mut().remove(&addr); + if self.expanding.borrow_mut().insert(addr) { + Some(ExpandGuard { + set: &self.expanding, + addr, + }) + } else { + None + } } } @@ -629,9 +632,10 @@ fn v2_parse_field(name: &str, schema: &Value, resolver: &V2Resolver) -> FieldSch let schema = resolver.resolve(schema); // Cycle detection - if !resolver.enter(schema) { - return any_field(name); - } + let _guard = match resolver.enter(schema) { + Some(guard) => guard, + None => return any_field(name), + }; let description = schema .get("description") @@ -646,8 +650,6 @@ fn v2_parse_field(name: &str, schema: &Value, resolver: &V2Resolver) -> FieldSch let field_type = v2_parse_type(schema, resolver); - resolver.leave(schema); - FieldSchema { name: name.to_string(), description, @@ -759,3 +761,33 @@ fn find_definition_key(candidates: &[String], resolved: &ResolvedResource) -> Re kind ); } + +#[cfg(test)] +mod tests { + use super::*; + use anyhow::{Result, bail}; + use serde_json::json; + + fn v2_enter_then_fail(resolver: &V2Resolver<'_>, schema: &Value) -> Result<()> { + let _guard = resolver + .enter(schema) + .expect("schema should not already be expanding"); + bail!("simulated early return") + } + + #[test] + fn v2_expand_guard_cleans_up_after_early_return() { + let root = json!({}); + let schema = json!({ "type": "object" }); + let resolver = V2Resolver { + root: &root, + expanding: RefCell::new(HashSet::new()), + }; + + assert!(v2_enter_then_fail(&resolver, &schema).is_err()); + assert!( + resolver.enter(&schema).is_some(), + "schema should be removable from the expanding set when the guard drops", + ); + } +} From 1c4c7369edf540c030c844eee5d0e437e4f0ea40 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 17 Apr 2026 01:03:17 +0000 Subject: [PATCH 3/3] test: simplify v2 expand guard regression helper Agent-Logs-Url: https://github.com/rawkode/kubectl-ditto/sessions/df015851-ebe2-4560-a7f1-7fd01429615c Co-authored-by: rawkode <145816+rawkode@users.noreply.github.com> --- src/schema.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/schema.rs b/src/schema.rs index 3ac3689..e1b2be4 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -765,14 +765,13 @@ fn find_definition_key(candidates: &[String], resolved: &ResolvedResource) -> Re #[cfg(test)] mod tests { use super::*; - use anyhow::{Result, bail}; use serde_json::json; - fn v2_enter_then_fail(resolver: &V2Resolver<'_>, schema: &Value) -> Result<()> { + fn v2_enter_then_fail(resolver: &V2Resolver<'_>, schema: &Value) -> Result<(), ()> { let _guard = resolver .enter(schema) .expect("schema should not already be expanding"); - bail!("simulated early return") + Err(()) } #[test]