diff --git a/.jules/bolt.md b/.jules/bolt.md new file mode 100644 index 00000000..6f7d358c --- /dev/null +++ b/.jules/bolt.md @@ -0,0 +1,3 @@ +## 2024-05-16 - [Deep Clone Serde Avoidance] +**Learning:** In xdk-openapi, when deserializing components from an intermediate JSON/YAML Value into Rust types, `from_value(value.clone())` forces a deep copy of the tree. However, Serde’s `Deserialize` trait allows calling `T::deserialize(&value)` which borrows the intermediate representation, saving O(N) allocations for strings/arrays/objects within the tree when only a single read pass is needed. Finally at the end when deserializing the root value, you can use `from_value(value)` (without cloning) since we no longer need the root value tree. +**Action:** Replace `serde_json::from_value::(val.clone())` and `serde_yaml::from_value::(val.clone())` with `T::deserialize(val)` by bringing `Deserialize` into scope. diff --git a/xdk-openapi/src/parser.rs b/xdk-openapi/src/parser.rs index 6a92b4e5..1fb6c98c 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,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 +69,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 +81,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 +96,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}") @@ -114,9 +111,7 @@ 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(), - ) { + 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 +206,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 +214,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 +222,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 +233,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}"), }