diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..4b24efff --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-18 - [Rust Serde Deserialization Memory Optimization] +**Learning:** In Rust's Serde, deep cloning `Value` structures (from `serde_json` or `serde_yaml`) just to pass to a deserializer (`T::deserialize(val)`) causes a significant number of expensive intermediate allocations. +**Action:** Always prefer borrowing from the parsed `Value` directly via `&val` to map to the structure if `from_value` requires an owned type and you are not consuming the tree, thus avoiding the deep clone tax. diff --git a/xdk-openapi/src/parser.rs b/xdk-openapi/src/parser.rs index 6a92b4e5..1b69f0bb 100644 --- a/xdk-openapi/src/parser.rs +++ b/xdk-openapi/src/parser.rs @@ -7,6 +7,7 @@ use crate::context::OpenApiContextGuard; use crate::core::OpenApi; use crate::error::OpenApiError; use crate::error::Result; +use serde::Deserialize; use serde_json::Value as JsonValue; use serde_yaml::Value as YamlValue; @@ -56,9 +57,8 @@ pub fn parse_yaml(yaml: &str) -> Result { if let Some(schemas) = components.get("schemas").and_then(|s| s.as_mapping()) { for (name, schema_val) in schemas { if let Some(name_str) = name.as_str() { - match serde_yaml::from_value::( - schema_val.clone(), - ) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::components::Schema::deserialize(schema_val) { Ok(schema) => ctx.add_schema(name_str.to_string(), schema), Err(e) => { eprintln!("Warning: Failed to parse schema {name_str}: {e}") @@ -70,9 +70,8 @@ pub fn parse_yaml(yaml: &str) -> Result { if let Some(parameters) = components.get("parameters").and_then(|p| p.as_mapping()) { for (name, param_val) in parameters { if let Some(name_str) = name.as_str() { - match serde_yaml::from_value::( - param_val.clone(), - ) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::components::Parameter::deserialize(param_val) { Ok(param) => ctx.add_parameter(name_str.to_string(), param), Err(e) => { eprintln!("Warning: Failed to parse parameter {name_str}: {e}") @@ -84,7 +83,8 @@ pub fn parse_yaml(yaml: &str) -> Result { if let Some(responses) = components.get("responses").and_then(|r| r.as_mapping()) { for (name, resp_val) in responses { if let Some(name_str) = name.as_str() { - match serde_yaml::from_value::(resp_val.clone()) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::core::Response::deserialize(resp_val) { Ok(resp) => ctx.add_response(name_str.to_string(), resp), Err(e) => { eprintln!("Warning: Failed to parse response {name_str}: {e}") @@ -99,7 +99,8 @@ pub fn parse_yaml(yaml: &str) -> Result { { for (name, rb_val) in request_bodies { if let Some(name_str) = name.as_str() { - match serde_yaml::from_value::(rb_val.clone()) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::core::RequestBody::deserialize(rb_val) { Ok(rb) => ctx.add_request_body(name_str.to_string(), rb), Err(e) => { eprintln!("Warning: Failed to parse requestBody {name_str}: {e}") @@ -114,9 +115,8 @@ pub fn parse_yaml(yaml: &str) -> Result { { for (name, ss_val) in security_schemes { if let Some(name_str) = name.as_str() { - match serde_yaml::from_value::( - ss_val.clone(), - ) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::components::SecurityScheme::deserialize(ss_val) { Ok(ss) => ctx.add_security_scheme(name_str.to_string(), ss), Err(e) => { eprintln!("Warning: Failed to parse securityScheme {name_str}: {e}") @@ -211,7 +211,8 @@ pub fn parse_json(json: &str) -> Result { OpenApiContextGuard::with_context_mut(|ctx| { if let Some(schemas) = components.get("schemas").and_then(|s| s.as_object()) { for (name, schema_val) in schemas { - match serde_json::from_value::(schema_val.clone()) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::components::Schema::deserialize(schema_val) { Ok(schema) => ctx.add_schema(name.clone(), schema), Err(e) => eprintln!("Warning: Failed to parse schema {name}: {e}"), } @@ -219,8 +220,8 @@ pub fn parse_json(json: &str) -> Result { } if let Some(parameters) = components.get("parameters").and_then(|p| p.as_object()) { for (name, param_val) in parameters { - match serde_json::from_value::(param_val.clone()) - { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::components::Parameter::deserialize(param_val) { Ok(param) => ctx.add_parameter(name.clone(), param), Err(e) => eprintln!("Warning: Failed to parse parameter {name}: {e}"), } @@ -228,7 +229,8 @@ pub fn parse_json(json: &str) -> Result { } if let Some(responses) = components.get("responses").and_then(|r| r.as_object()) { for (name, resp_val) in responses { - match serde_json::from_value::(resp_val.clone()) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::core::Response::deserialize(resp_val) { Ok(resp) => ctx.add_response(name.clone(), resp), Err(e) => eprintln!("Warning: Failed to parse response {name}: {e}"), } @@ -239,7 +241,8 @@ pub fn parse_json(json: &str) -> Result { .and_then(|rb| rb.as_object()) { for (name, rb_val) in request_bodies { - match serde_json::from_value::(rb_val.clone()) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::core::RequestBody::deserialize(rb_val) { Ok(rb) => ctx.add_request_body(name.clone(), rb), Err(e) => eprintln!("Warning: Failed to parse requestBody {name}: {e}"), } @@ -250,9 +253,8 @@ pub fn parse_json(json: &str) -> Result { .and_then(|ss| ss.as_object()) { for (name, ss_val) in security_schemes { - match serde_json::from_value::( - ss_val.clone(), - ) { + // Performance: deserialize directly from &Value to avoid deep cloning + match crate::components::SecurityScheme::deserialize(ss_val) { Ok(ss) => ctx.add_security_scheme(name.clone(), ss), Err(e) => { eprintln!("Warning: Failed to parse securityScheme {name}: {e}")