diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..ac9e9137 --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-07-08 - [Avoid Serde Deep Clones During Parsing] +**Learning:** Calling `.clone()` on intermediate `serde_yaml::Value` and `serde_json::Value` objects just to pass them into `from_value` allocates memory for every nested AST node, causing severe performance bottlenecks when parsing large specs. +**Action:** Replace `serde_yaml::from_value::(value.clone())` with `Type::deserialize(value)` (requires `use serde::Deserialize;`) to allow `serde` to read directly from the borrowed AST reference. Keep un-cloned `from_value` calls intact at the end of functions to consume the full tree without re-allocating. diff --git a/xdk-openapi/src/parser.rs b/xdk-openapi/src/parser.rs index 6a92b4e5..1260a5d6 100644 --- a/xdk-openapi/src/parser.rs +++ b/xdk-openapi/src/parser.rs @@ -3,6 +3,8 @@ //! This module provides functions for parsing OpenAPI specifications from YAML or JSON formats. //! It supports both string inputs and file paths. +use serde::Deserialize; + use crate::context::OpenApiContextGuard; use crate::core::OpenApi; use crate::error::OpenApiError; @@ -56,9 +58,7 @@ 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(), - ) { + 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,7 @@ 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(), - ) { + 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 +82,7 @@ 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()) { + 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 +97,7 @@ 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()) { + 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}") @@ -211,7 +209,7 @@ 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()) { + 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 +217,7 @@ 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()) - { + 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 +225,7 @@ 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()) { + 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 +236,7 @@ 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()) { + 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}"), }