From 12165aeb42b09278a1046529cd3deb218689477f Mon Sep 17 00:00:00 2001 From: Ahmed Ibrahim Date: Tue, 5 May 2026 02:42:28 +0300 Subject: [PATCH] Use LenientEnum for config enum sanitization Route enum value classification through a LenientEnum wrapper so known values, unknown string values, and non-string shapes are handled explicitly before ConfigToml deserialization. Co-authored-by: Codex --- codex-rs/config/src/unknown_enum_values.rs | 54 +++++++++++++--------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/codex-rs/config/src/unknown_enum_values.rs b/codex-rs/config/src/unknown_enum_values.rs index 836077e11f..ca6cff3d12 100644 --- a/codex-rs/config/src/unknown_enum_values.rs +++ b/codex-rs/config/src/unknown_enum_values.rs @@ -21,9 +21,18 @@ use codex_protocol::config_types::WebSearchContextSize; use codex_protocol::config_types::WebSearchMode; use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::protocol::AskForApproval; +use serde::Deserialize; use serde::de::DeserializeOwned; use toml::Value as TomlValue; +#[derive(Debug, Deserialize)] +#[serde(untagged)] +enum LenientEnum { + Known(T), + Unknown(String), + Other(TomlValue), +} + #[derive(Debug, Clone, PartialEq, Eq)] enum PathSegment { Key(&'static str), @@ -117,17 +126,16 @@ where { let paths = matching_paths(root, path); for value_path in paths { - let Some(raw_value) = value_at_path(root, &value_path) - .and_then(TomlValue::as_str) - .map(str::to_string) - else { + let Some(value) = value_at_path(root, &value_path).cloned() else { continue; }; - if TomlValue::String(raw_value.clone()).try_into::().is_ok() { - continue; - } - - warn_and_remove(root, &value_path, &value_path, &raw_value, warnings); + match value.try_into::>() { + Ok(LenientEnum::Known(_)) => {} + Ok(LenientEnum::Unknown(raw_value)) => { + warn_and_remove(root, &value_path, &value_path, &raw_value, warnings); + } + Ok(LenientEnum::Other(_)) | Err(_) => {} + }; } } @@ -143,21 +151,23 @@ fn sanitize_tagged_enum( for parent_path in parent_paths { let mut tag_path = parent_path.clone(); tag_path.push(tag_key.to_string()); - let Some(raw_value) = value_at_path(root, &tag_path) - .and_then(TomlValue::as_str) - .map(str::to_string) - else { + let Some(value) = value_at_path(root, &parent_path).cloned() else { continue; }; - if value_at_path(root, &parent_path) - .cloned() - .and_then(|value| value.try_into::().ok()) - .is_some() - { - continue; - } - - warn_and_remove(root, &tag_path, &parent_path, &raw_value, warnings); + match value.try_into::>() { + Ok(LenientEnum::Known(_)) => {} + Ok(LenientEnum::Other(table_value)) => { + let Some(raw_value) = table_value + .get(tag_key) + .and_then(TomlValue::as_str) + .map(str::to_string) + else { + continue; + }; + warn_and_remove(root, &tag_path, &parent_path, &raw_value, warnings); + } + Ok(LenientEnum::Unknown(_)) | Err(_) => {} + }; } }