use serde_json::Value;
use tracing::debug;
use tracing::{info_span, Instrument};
use crate::{
api::{ApiResponseTrait, BaseResponse, RawResponse, Response, ResponseFormat},
content_disposition,
error::{network_error, validation_error},
observability::ResponseTracker,
SDKResult,
};
use serde::Deserialize;
use std::any::Any;
#[cfg(test)]
use crate::error::{CoreError, ErrorCategory, ErrorCode, ErrorContext};
pub struct ImprovedResponseHandler;
impl ImprovedResponseHandler {
pub async fn handle_response<T: ApiResponseTrait + for<'de> Deserialize<'de>>(
response: reqwest::Response,
) -> SDKResult<Response<T>> {
let format = match T::data_format() {
ResponseFormat::Data => "data",
ResponseFormat::Flatten => "flatten",
ResponseFormat::Binary => "binary",
ResponseFormat::Text => "text",
ResponseFormat::Custom => "custom",
};
let span = info_span!(
"response_handling",
format = format,
status_code = response.status().as_u16(),
content_length = tracing::field::Empty,
processing_duration_ms = tracing::field::Empty,
);
async move {
let start_time = std::time::Instant::now();
let content_length = response.content_length();
if let Some(length) = content_length {
tracing::Span::current().record("content_length", length);
}
let result = match T::data_format() {
ResponseFormat::Data => Self::handle_data_response(response).await,
ResponseFormat::Flatten => Self::handle_flatten_response(response).await,
ResponseFormat::Binary => Self::handle_binary_response(response).await,
ResponseFormat::Text => Self::handle_data_response(response).await, ResponseFormat::Custom => Self::handle_data_response(response).await, };
let duration_ms = start_time.elapsed().as_millis() as u64;
tracing::Span::current().record("processing_duration_ms", duration_ms);
result
}
.instrument(span)
.await
}
async fn handle_data_response<T: ApiResponseTrait + for<'de> Deserialize<'de>>(
response: reqwest::Response,
) -> SDKResult<Response<T>> {
let tracker = ResponseTracker::start("json_data", response.content_length());
let response_text = response.text().await?;
debug!("Raw response: {response_text}");
tracker.parsing_complete();
match serde_json::from_str::<Response<T>>(&response_text) {
Ok(base_response) => {
tracker.success();
Ok(base_response)
}
Err(direct_parse_err) => {
tracing::debug!("Direct parsing failed, attempting structured data extraction");
match serde_json::from_str::<Value>(&response_text) {
Ok(raw_value) => {
let code = raw_value["code"].as_i64().unwrap_or(-1) as i32;
let msg = raw_value["msg"]
.as_str()
.unwrap_or("Unknown error")
.to_string();
let data = if code == 0 {
if let Some(data_value) = raw_value.get("data") {
match serde_json::from_value::<T>(data_value.clone()) {
Ok(parsed_data) => {
tracing::debug!("Successfully parsed data field as type T");
Some(parsed_data)
}
Err(data_parse_err) => {
tracing::debug!(
"Failed to parse data field as type T: {data_parse_err:?}"
);
None
}
}
} else {
tracing::debug!("No data field found in successful response");
None
}
} else {
None
};
tracker.validation_complete();
tracker.success();
Ok(BaseResponse {
raw_response: RawResponse {
code,
msg,
request_id: None,
data: None,
error: None,
},
data,
})
}
Err(fallback_err) => {
let error_msg = format!(
"Failed to parse response. Direct parse error: {}. Fallback parse error: {}",
direct_parse_err, fallback_err
);
tracker.error(&error_msg);
Err(validation_error("api_response", error_msg))
}
}
}
}
}
async fn handle_flatten_response<T: ApiResponseTrait + for<'de> Deserialize<'de>>(
response: reqwest::Response,
) -> SDKResult<Response<T>> {
let tracker = ResponseTracker::start("json_flatten", response.content_length());
let response_text = response.text().await?;
debug!("Raw response: {response_text}");
let raw_value: Value = match serde_json::from_str(&response_text) {
Ok(value) => {
tracker.parsing_complete();
value
}
Err(e) => {
let error_msg = format!("Failed to parse JSON: {}", e);
tracker.error(&error_msg);
return Err(validation_error("base_response", error_msg));
}
};
let raw_response: RawResponse = match serde_json::from_value(raw_value.clone()) {
Ok(response) => response,
Err(e) => {
let error_msg = format!("Failed to parse raw response: {}", e);
tracker.error(&error_msg);
return Err(validation_error("response", error_msg));
}
};
let data = if raw_response.code == 0 {
match serde_json::from_value::<T>(raw_value) {
Ok(parsed_data) => {
tracker.validation_complete();
Some(parsed_data)
}
Err(e) => {
debug!("Failed to parse data for flatten response: {e}");
tracker.validation_complete();
None
}
}
} else {
tracker.validation_complete();
None
};
tracker.success();
Ok(BaseResponse { raw_response, data })
}
async fn handle_binary_response<T: ApiResponseTrait>(
response: reqwest::Response,
) -> SDKResult<Response<T>> {
let tracker = ResponseTracker::start("binary", response.content_length());
let _file_name = response
.headers()
.get("Content-Disposition")
.and_then(|header| header.to_str().ok())
.and_then(content_disposition::extract_filename)
.unwrap_or_default();
tracker.parsing_complete();
let bytes = match response.bytes().await {
Ok(bytes) => {
let byte_vec = bytes.to_vec();
tracing::debug!("Binary response received: {} bytes", byte_vec.len());
byte_vec
}
Err(e) => {
let error_msg = format!("Failed to read binary response: {}", e);
tracker.error(&error_msg);
return Err(network_error(error_msg));
}
};
let data: Option<T> = {
let any: Box<dyn Any> =
if std::any::TypeId::of::<T>() == std::any::TypeId::of::<Vec<u8>>() {
Box::new(bytes)
} else if std::any::TypeId::of::<T>() == std::any::TypeId::of::<String>() {
let s = String::from_utf8_lossy(&bytes).to_string();
Box::new(s)
} else {
Box::new(())
};
any.downcast::<T>().ok().map(|boxed| *boxed)
};
tracker.success();
Ok(BaseResponse {
raw_response: RawResponse {
code: 0,
msg: "success".to_string(),
request_id: None,
data: None,
error: None,
},
data,
})
}
}
#[cfg(test)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct OptimizedBaseResponse<T>
where
T: Default,
{
pub code: i32,
pub msg: String,
#[serde(rename = "error", default, skip_serializing_if = "Option::is_none")]
pub error: Option<ErrorInfo>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub data: Option<T>,
}
#[cfg(test)]
impl<T> OptimizedBaseResponse<T>
where
T: Default,
{
pub fn is_success(&self) -> bool {
self.code == 0
}
pub fn into_data(self) -> Result<T, CoreError> {
if self.is_success() {
self.data.ok_or_else(|| {
validation_error("data", "Response is successful but data is missing")
})
} else {
let mapped_code = ErrorCode::from_feishu_code(self.code)
.unwrap_or_else(|| ErrorCode::from_code(self.code));
let mut ctx = ErrorContext::new();
ctx.add_context("feishu_code", self.code.to_string());
if let Some(log_id) = self.error.as_ref().and_then(|e| e.log_id.clone()) {
ctx.set_request_id(log_id);
}
let status =
mapped_code
.http_status()
.unwrap_or_else(|| match mapped_code.category() {
ErrorCategory::RateLimit => 429,
ErrorCategory::Authentication
| ErrorCategory::Permission
| ErrorCategory::Parameter => 400,
ErrorCategory::Resource => 404,
_ => 500,
});
Err(CoreError::Api(Box::new(crate::error::ApiError {
status,
endpoint: "unknown_endpoint".into(),
message: self.msg,
source: None,
code: mapped_code,
ctx: Box::new(ctx),
})))
}
}
pub fn data(&self) -> Option<&T> {
self.data.as_ref()
}
pub fn has_error(&self) -> bool {
self.error.is_some()
}
}
#[cfg(test)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct ErrorInfo {
#[serde(rename = "key", default, skip_serializing_if = "Option::is_none")]
pub log_id: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub details: Vec<ErrorDetail>,
}
#[cfg(test)]
#[derive(Debug, serde::Serialize, serde::Deserialize)]
pub struct ErrorDetail {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub key: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub description: Option<String>,
}
#[macro_export]
macro_rules! impl_api_response {
($type:ty, $format:expr) => {
impl ApiResponseTrait for $type {
fn data_format() -> ResponseFormat {
$format
}
}
};
($type:ty, $format:expr, binary) => {
impl ApiResponseTrait for $type {
fn data_format() -> ResponseFormat {
$format
}
fn from_binary(file_name: String, body: Vec<u8>) -> Option<Self> {
Some(<$type>::from_binary_data(file_name, body))
}
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::api::ResponseFormat;
use serde::{Deserialize, Serialize};
#[derive(Debug, Serialize, Deserialize, PartialEq, Default, Clone)]
struct TestData {
id: i32,
name: String,
}
impl ApiResponseTrait for TestData {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Default, Clone)]
struct TestFlattenData {
id: i32,
name: String,
code: i32,
msg: String,
}
impl ApiResponseTrait for TestFlattenData {
fn data_format() -> ResponseFormat {
ResponseFormat::Flatten
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Default, Clone)]
struct TestBinaryData {
file_name: String,
content: Vec<u8>,
}
impl ApiResponseTrait for TestBinaryData {
fn data_format() -> ResponseFormat {
ResponseFormat::Binary
}
}
#[test]
fn test_optimized_base_response_success() {
let response = OptimizedBaseResponse {
code: 0,
msg: "success".to_string(),
error: None,
data: Some(TestData {
id: 1,
name: "test".to_string(),
}),
};
assert!(response.is_success());
assert!(response.data().is_some());
assert_eq!(response.data().unwrap().id, 1);
assert!(!response.has_error());
}
#[test]
fn test_optimized_base_response_error() {
let response: OptimizedBaseResponse<TestData> = OptimizedBaseResponse {
code: 400,
msg: "Bad Request".to_string(),
error: Some(ErrorInfo {
log_id: Some("log123".to_string()),
details: vec![],
}),
data: None,
};
assert!(!response.is_success());
assert!(response.has_error());
assert!(response.data().is_none());
}
#[test]
fn test_optimized_base_response_into_data_success() {
let response = OptimizedBaseResponse {
code: 0,
msg: "success".to_string(),
error: None,
data: Some(TestData {
id: 1,
name: "test".to_string(),
}),
};
let data = response.into_data().unwrap();
assert_eq!(data.id, 1);
assert_eq!(data.name, "test");
}
#[test]
fn test_optimized_base_response_into_data_error() {
let response: OptimizedBaseResponse<TestData> = OptimizedBaseResponse {
code: 400,
msg: "Bad Request".to_string(),
error: None,
data: None,
};
let result = response.into_data();
assert!(result.is_err());
match result.unwrap_err() {
CoreError::Api(api) => {
assert_eq!(api.status, 400);
assert_eq!(api.message, "Bad Request");
}
_ => panic!("Expected ApiError"),
}
}
#[test]
fn test_optimized_base_response_into_data_success_but_no_data() {
let response: OptimizedBaseResponse<TestData> = OptimizedBaseResponse {
code: 0,
msg: "success".to_string(),
error: None,
data: None,
};
let result = response.into_data();
assert!(result.is_err());
match result.unwrap_err() {
CoreError::Validation { message, .. } => {
assert!(message.contains("data is missing"));
}
_ => panic!("Expected IllegalParamError"),
}
}
#[test]
fn test_error_info_serialization() {
let error_info = ErrorInfo {
log_id: Some("test_log_id".to_string()),
details: vec![
ErrorDetail {
key: Some("field1".to_string()),
value: Some("invalid_value".to_string()),
description: Some("Field is required".to_string()),
},
ErrorDetail {
key: Some("field2".to_string()),
value: None,
description: Some("Missing field".to_string()),
},
],
};
let json = serde_json::to_string(&error_info).unwrap();
let deserialized: ErrorInfo = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.log_id, error_info.log_id);
assert_eq!(deserialized.details.len(), 2);
assert_eq!(deserialized.details[0].key, Some("field1".to_string()));
assert_eq!(deserialized.details[1].value, None);
}
#[test]
fn test_error_detail_optional_fields() {
let detail = ErrorDetail {
key: None,
value: Some("test_value".to_string()),
description: None,
};
let json = serde_json::to_string(&detail).unwrap();
let deserialized: ErrorDetail = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.key, None);
assert_eq!(deserialized.value, Some("test_value".to_string()));
assert_eq!(deserialized.description, None);
}
#[test]
fn test_filename_extraction() {
let cases = vec![
(
"attachment; filename=\"test.txt\"",
Some("test.txt".to_string()),
),
(
"attachment; filename*=UTF-8''test%20file.pdf",
Some("test%20file.pdf".to_string()),
),
(
"attachment; filename=simple.doc",
Some("simple.doc".to_string()),
),
("attachment", None),
("", None),
("filename=\"quoted.txt\"", Some("quoted.txt".to_string())),
("filename=unquoted.txt", Some("unquoted.txt".to_string())),
(
"filename*=UTF-8''unicode%E2%9C%93.txt",
Some("unicode%E2%9C%93.txt".to_string()),
),
(
"attachment; filename=\"spaced file.doc\"; other=value",
Some("spaced file.doc".to_string()),
),
];
for (input, expected) in cases {
let result = crate::content_disposition::extract_filename(input);
assert_eq!(result, expected, "Failed for input: {input}");
}
}
#[test]
fn test_filename_extraction_edge_cases() {
assert_eq!(crate::content_disposition::extract_filename(""), None);
assert_eq!(crate::content_disposition::extract_filename(" "), None);
assert_eq!(crate::content_disposition::extract_filename(";;;"), None);
assert_eq!(
crate::content_disposition::extract_filename("filename="),
Some("".to_string())
);
assert_eq!(
crate::content_disposition::extract_filename("filename*="),
None
);
assert_eq!(
crate::content_disposition::extract_filename("filename=\""),
Some("".to_string())
);
assert_eq!(
crate::content_disposition::extract_filename("filename=\"\""),
Some("".to_string())
);
let multi_filename = "filename=\"first.txt\"; filename=\"second.txt\"";
assert_eq!(
crate::content_disposition::extract_filename(multi_filename),
Some("first.txt".to_string())
);
}
#[test]
fn test_json_parsing_performance() {
let json_data = r#"{"code": 0, "msg": "success", "data": {"id": 1, "name": "test"}}"#;
let start = std::time::Instant::now();
let _result: Result<OptimizedBaseResponse<TestData>, _> = serde_json::from_str(json_data);
let direct_parse_time = start.elapsed();
let start = std::time::Instant::now();
let _value: Value = serde_json::from_str(json_data).unwrap();
let _result: Result<OptimizedBaseResponse<TestData>, _> = serde_json::from_value(_value);
let double_parse_time = start.elapsed();
println!("Direct parse time: {direct_parse_time:?}");
println!("Double parse time: {double_parse_time:?}");
}
#[test]
fn test_api_response_trait_data_format() {
assert_eq!(TestData::data_format(), ResponseFormat::Data);
assert_eq!(TestFlattenData::data_format(), ResponseFormat::Flatten);
assert_eq!(TestBinaryData::data_format(), ResponseFormat::Binary);
}
#[test]
fn test_api_response_trait_from_binary() {
let file_name = "test.txt".to_string();
let content = b"Hello, World!".to_vec();
let binary_data = TestBinaryData {
file_name: file_name.clone(),
content: content.clone(),
};
assert_eq!(binary_data.file_name, file_name);
assert_eq!(binary_data.content, content);
let _ = "test.txt".to_string();
}
#[tokio::test]
async fn test_handle_data_response_parsing_logic() {
let test_cases = vec![
(r#"{"code": 400, "msg": "Bad Request"}"#, true),
(r#"{"invalid": json"#, false),
];
for (json, should_succeed) in test_cases {
if json.contains("code") && !json.contains("raw_response") {
let fallback_result = serde_json::from_str::<Value>(json);
if should_succeed {
assert!(
fallback_result.is_ok(),
"Fallback parsing should succeed for: {}",
json
);
let value = fallback_result.unwrap();
assert!(value["code"].is_i64());
assert!(value["msg"].is_string());
}
} else if json.contains("invalid") {
let parse_result = serde_json::from_str::<Value>(json);
assert!(parse_result.is_err(), "Invalid JSON should fail to parse");
}
}
}
#[tokio::test]
async fn test_handle_flatten_response_parsing_logic() {
let test_cases = vec![
(
r#"{"id": 1, "name": "test", "code": 0, "msg": "success"}"#,
0,
true,
),
(r#"{"code": 400, "msg": "Bad Request"}"#, 400, false),
(r#"{"invalid": json"#, -1, false),
];
for (json, expected_code, should_have_data) in test_cases {
if json.contains("invalid") {
let parse_result = serde_json::from_str::<Value>(json);
assert!(parse_result.is_err(), "Invalid JSON should fail to parse");
continue;
}
let value_result = serde_json::from_str::<Value>(json);
assert!(value_result.is_ok(), "Valid JSON should parse as Value");
let value = value_result.unwrap();
let raw_response_result = serde_json::from_value::<RawResponse>(value.clone());
if expected_code >= 0 {
assert!(
raw_response_result.is_ok(),
"Should parse RawResponse for: {}",
json
);
let raw_response = raw_response_result.unwrap();
assert_eq!(raw_response.code, expected_code);
if should_have_data && raw_response.code == 0 {
let data_result = serde_json::from_value::<TestFlattenData>(value);
assert!(
data_result.is_ok(),
"Should parse data for success response"
);
}
}
}
}
#[test]
fn test_response_format_display_logic() {
let formats = vec![
(ResponseFormat::Data, "data"),
(ResponseFormat::Flatten, "flatten"),
(ResponseFormat::Binary, "binary"),
];
for (format, expected_str) in formats {
let format_str = match format {
ResponseFormat::Data => "data",
ResponseFormat::Flatten => "flatten",
ResponseFormat::Binary => "binary",
ResponseFormat::Text => "text",
ResponseFormat::Custom => "custom",
};
assert_eq!(format_str, expected_str);
}
}
#[test]
fn test_binary_response_logic() {
let test_file_name = "test_document.pdf";
let test_content = b"PDF content here".to_vec();
let binary_data = TestBinaryData {
file_name: test_file_name.to_string(),
content: test_content.clone(),
};
assert!(binary_data.file_name == test_file_name);
assert!(binary_data.content == test_content);
let empty_data = TestBinaryData {
file_name: "empty.txt".to_string(),
content: vec![],
};
assert_eq!(empty_data.content.len(), 0);
}
#[test]
fn test_optimized_response_serialization_roundtrip() {
let original = OptimizedBaseResponse {
code: 0,
msg: "success".to_string(),
error: Some(ErrorInfo {
log_id: Some("test123".to_string()),
details: vec![ErrorDetail {
key: Some("validation".to_string()),
value: Some("failed".to_string()),
description: Some("Field validation failed".to_string()),
}],
}),
data: Some(TestData {
id: 42,
name: "serialization_test".to_string(),
}),
};
let json = serde_json::to_string(&original).unwrap();
let deserialized: OptimizedBaseResponse<TestData> = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.code, original.code);
assert_eq!(deserialized.msg, original.msg);
assert_eq!(deserialized.data, original.data);
assert!(deserialized.error.is_some());
let error = deserialized.error.unwrap();
assert_eq!(error.log_id, Some("test123".to_string()));
assert_eq!(error.details.len(), 1);
assert_eq!(error.details[0].key, Some("validation".to_string()));
}
#[test]
fn test_optimized_response_skipped_fields() {
let response: OptimizedBaseResponse<TestData> = OptimizedBaseResponse {
code: 0,
msg: "success".to_string(),
error: None,
data: None,
};
let json = serde_json::to_string(&response).unwrap();
assert!(!json.contains("\"error\""));
assert!(!json.contains("\"data\""));
assert!(json.contains("\"code\":0"));
assert!(json.contains("\"msg\":\"success\""));
}
#[test]
fn test_macro_api_response_implementation() {
#[derive(Debug, Default, Serialize, Deserialize)]
struct MacroTestData;
impl ApiResponseTrait for MacroTestData {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
assert_eq!(MacroTestData::data_format(), ResponseFormat::Data);
}
#[test]
fn test_error_detail_empty_values() {
let detail = ErrorDetail {
key: Some("".to_string()),
value: Some("".to_string()),
description: Some("".to_string()),
};
let json = serde_json::to_string(&detail).unwrap();
let deserialized: ErrorDetail = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.key, Some("".to_string()));
assert_eq!(deserialized.value, Some("".to_string()));
assert_eq!(deserialized.description, Some("".to_string()));
}
#[test]
fn test_content_disposition_header_edge_cases() {
let edge_cases = vec![
("FILENAME=\"test.txt\"", None), ("Filename=\"test.txt\"", None), (
"attachment; filename=\"test.txt\"",
Some("test.txt".to_string()),
),
("attachment; filename = \"test.txt\"", None), (
"attachment; filename=\"test-file_v1.2.txt\"",
Some("test-file_v1.2.txt".to_string()),
),
(
"attachment; filename=\"测试文件.txt\"",
Some("测试文件.txt".to_string()),
),
(
"attachment; filename=\"test.txt\"; filename*=UTF-8''better.txt",
Some("better.txt".to_string()),
),
];
for (input, expected) in edge_cases {
let result = crate::content_disposition::extract_filename(input);
assert_eq!(result, expected, "Failed for input: {}", input);
}
}
#[test]
fn test_complex_error_response_scenarios() {
use serde_json::Value;
let complex_error = r#"{
"code": 400,
"msg": "Validation failed",
"error": {
"log_id": "error_12345",
"details": [
{
"key": "field1",
"value": "invalid",
"description": "Field must be valid email"
},
{
"key": "field2",
"description": "Required field missing"
}
]
}
}"#;
let parsed: Value = serde_json::from_str(complex_error).unwrap();
assert_eq!(parsed["code"], 400);
assert_eq!(parsed["msg"], "Validation failed");
assert!(parsed["error"]["log_id"].is_string());
assert_eq!(parsed["error"]["details"].as_array().unwrap().len(), 2);
let error_missing_msg = r#"{"code": 500}"#;
let parsed_missing: Value = serde_json::from_str(error_missing_msg).unwrap();
assert_eq!(parsed_missing["code"], 500);
assert!(!parsed_missing["msg"].is_string());
let invalid_code = r#"{"code": "400", "msg": "Invalid code type"}"#;
let parsed_invalid: Value = serde_json::from_str(invalid_code).unwrap();
assert!(parsed_invalid["code"].is_string());
}
#[test]
fn test_large_response_data_handling() {
use serde_json::Value;
let large_data_list: Vec<String> = (0..1000).map(|i| format!("item_{}", i)).collect();
let large_response = serde_json::json!({
"code": 0,
"msg": "success",
"data": {
"items": large_data_list,
"metadata": {
"total": 1000,
"page": 1
}
}
});
let json_str = serde_json::to_string(&large_response).unwrap();
assert!(json_str.len() > 10000);
let parsed: Value = serde_json::from_str(&json_str).unwrap();
assert_eq!(parsed["code"], 0);
assert_eq!(parsed["data"]["items"].as_array().unwrap().len(), 1000);
}
#[test]
fn test_unicode_response_handling() {
use serde_json::json;
let unicode_response = json!({
"code": 0,
"msg": "操作成功",
"data": {
"title": "测试标题",
"description": "这是一个包含中文、English و العربية 的描述",
"tags": ["标签1", "tag2", "العربية", "🚀"]
}
});
let json_str = serde_json::to_string(&unicode_response).unwrap();
let parsed: Value = serde_json::from_str(&json_str).unwrap();
assert_eq!(parsed["msg"], "操作成功");
assert_eq!(parsed["data"]["title"], "测试标题");
assert!(parsed["data"]["description"]
.as_str()
.unwrap()
.contains("中文"));
assert!(parsed["data"]["tags"].as_array().unwrap()[3] == "🚀");
}
#[test]
fn test_memory_efficient_response_processing() {
use std::mem;
let response = OptimizedBaseResponse {
code: 0,
msg: "success".to_string(),
error: None,
data: Some(TestData {
id: 123,
name: "test".to_string(),
}),
};
let response_size = mem::size_of_val(&response);
assert!(response_size > 0);
let empty_response: OptimizedBaseResponse<TestData> = OptimizedBaseResponse {
code: 0,
msg: "success".to_string(),
error: None,
data: None,
};
let empty_size = mem::size_of_val(&empty_response);
assert!(empty_size > 0);
}
#[test]
fn test_response_parsing_performance() {
use std::time::Instant;
let test_json = r#"{"code": 0, "msg": "success", "data": {"id": 1, "name": "test"}}"#;
let iterations = 1000;
let start = Instant::now();
for _ in 0..iterations {
let _: Result<OptimizedBaseResponse<TestData>, _> = serde_json::from_str(test_json);
}
let direct_time = start.elapsed();
let start = Instant::now();
for _ in 0..iterations {
let value: Value = serde_json::from_str(test_json).unwrap();
let _: Result<Response<TestData>, _> = serde_json::from_value(value);
}
let fallback_time = start.elapsed();
println!(
"Direct parsing: {:?}, Fallback parsing: {:?}",
direct_time, fallback_time
);
assert!(direct_time.as_millis() < 1000); assert!(fallback_time.as_millis() < 1000);
let ratio = fallback_time.as_nanos() as f64 / direct_time.as_nanos() as f64;
println!("Performance ratio (fallback/direct): {:.2}x", ratio);
}
#[test]
fn test_concurrent_response_parsing() {
use std::sync::{Arc, Mutex};
use std::thread;
let test_responses = vec![
r#"{"code": 0, "msg": "success", "data": {"id": 1, "name": "test1"}}"#,
r#"{"code": 0, "msg": "success", "data": {"id": 2, "name": "test2"}}"#,
r#"{"code": 400, "msg": "error", "data": null}"#,
r#"{"code": 0, "msg": "success", "data": {"id": 4, "name": "test4"}}"#,
];
let results = Arc::new(Mutex::new(Vec::new()));
let mut handles = vec![];
for (i, response_json) in test_responses.into_iter().enumerate() {
let results_clone = results.clone();
let handle = thread::spawn(move || {
let parsed: Result<OptimizedBaseResponse<TestData>, _> =
serde_json::from_str(response_json);
results_clone.lock().unwrap().push((i, parsed.is_ok()));
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
let results_vec = results.lock().unwrap();
assert_eq!(results_vec.len(), 4);
assert!(results_vec.iter().all(|(_, success)| *success));
}
#[test]
fn test_edge_case_json_structures() {
use serde_json::json;
let null_response = json!({
"code": 0,
"msg": "success",
"data": null
});
let parsed: OptimizedBaseResponse<TestData> =
serde_json::from_value(null_response).unwrap();
assert!(parsed.is_success());
assert!(parsed.data().is_none());
let empty_response = json!({
"code": 0,
"msg": "success",
"data": {
"items": [],
"metadata": {}
}
});
let parsed_value: Value = serde_json::from_value(empty_response).unwrap();
assert_eq!(parsed_value["data"]["items"].as_array().unwrap().len(), 0);
assert!(parsed_value["data"]["metadata"]
.as_object()
.unwrap()
.is_empty());
let extra_fields_response = json!({
"code": 0,
"msg": "success",
"data": {"id": 1, "name": "test"},
"unexpected_field": "should_be_ignored",
"another_unexpected": {"nested": "data"}
});
let parsed_extra: OptimizedBaseResponse<TestData> =
serde_json::from_value(extra_fields_response).unwrap();
assert!(parsed_extra.is_success());
assert!(parsed_extra.data().is_some());
}
#[test]
fn test_response_format_validation() {
let test_cases = vec![
(ResponseFormat::Data, "data", true),
(ResponseFormat::Flatten, "flatten", false),
(ResponseFormat::Binary, "binary", false),
];
for (format, expected_str, supports_data) in test_cases {
let format_str = match format {
ResponseFormat::Data => "data",
ResponseFormat::Flatten => "flatten",
ResponseFormat::Binary => "binary",
ResponseFormat::Text => "text",
ResponseFormat::Custom => "custom",
};
assert_eq!(format_str, expected_str);
match format {
ResponseFormat::Data => assert!(supports_data),
ResponseFormat::Flatten => assert!(!supports_data),
ResponseFormat::Binary => assert!(!supports_data),
ResponseFormat::Text => assert!(supports_data),
ResponseFormat::Custom => assert!(supports_data),
}
}
}
#[test]
fn test_binary_response_edge_cases() {
let large_binary = vec![0u8; 1_000_000]; let large_binary_data = TestBinaryData {
file_name: "large_file.bin".to_string(),
content: large_binary,
};
assert_eq!(large_binary_data.content.len(), 1_000_000);
let empty_binary_data = TestBinaryData {
file_name: "empty_file.txt".to_string(),
content: vec![],
};
assert!(empty_binary_data.content.is_empty());
let special_filename_data = TestBinaryData {
file_name: "测试文件@#$%.txt".to_string(),
content: b"test content".to_vec(),
};
assert_eq!(special_filename_data.file_name, "测试文件@#$%.txt");
}
#[test]
fn test_complex_error_detail_scenarios() {
let complex_error = ErrorInfo {
log_id: Some("complex_error_123".to_string()),
details: vec![
ErrorDetail {
key: Some("validation".to_string()),
value: Some("email格式不正确".to_string()),
description: Some("邮箱地址格式验证失败".to_string()),
},
ErrorDetail {
key: Some("required_field".to_string()),
value: None,
description: Some("必填字段缺失".to_string()),
},
ErrorDetail {
key: Some("constraint".to_string()),
value: Some("长度超过限制".to_string()),
description: Some("字段长度超出最大限制".to_string()),
},
],
};
let json = serde_json::to_string(&complex_error).unwrap();
let deserialized: ErrorInfo = serde_json::from_str(&json).unwrap();
assert_eq!(deserialized.log_id, complex_error.log_id);
assert_eq!(deserialized.details.len(), 3);
assert_eq!(
deserialized.details[0].description,
Some("邮箱地址格式验证失败".to_string())
);
assert_eq!(deserialized.details[1].value, None);
assert_eq!(deserialized.details[2].key, Some("constraint".to_string()));
}
#[test]
fn test_response_tracker_integration_simulation() {
let tracker_calls = [
("start", "json_data", Some(1024)),
("parsing_complete", "", None),
("validation_complete", "", None),
("success", "", None),
];
assert_eq!(tracker_calls.len(), 4);
assert_eq!(tracker_calls[0].0, "start");
assert_eq!(tracker_calls[0].1, "json_data");
assert_eq!(tracker_calls[0].2, Some(1024));
let error_tracker_calls = [
("start", "json_flatten", Some(512)),
("error", "解析失败", None),
];
assert_eq!(error_tracker_calls.len(), 2);
assert_eq!(error_tracker_calls[1].0, "error");
assert_eq!(error_tracker_calls[1].1, "解析失败");
}
#[test]
fn test_real_world_response_patterns() {
use serde_json::json;
let pagination_response = json!({
"code": 0,
"msg": "success",
"data": {
"items": [
{"id": 1, "name": "item1"},
{"id": 2, "name": "item2"}
],
"page_token": "next_page_token",
"has_more": true
}
});
let pagination_parsed: Value = serde_json::from_value(pagination_response).unwrap();
assert_eq!(
pagination_parsed["data"]["items"].as_array().unwrap().len(),
2
);
assert!(pagination_parsed["data"]["has_more"].as_bool().unwrap());
let nested_response = json!({
"code": 0,
"msg": "success",
"data": {
"user": {
"id": "user_123",
"profile": {
"name": "张三",
"department": "技术部"
}
},
"permissions": ["read", "write"]
}
});
let nested_parsed: Value = serde_json::from_value(nested_response).unwrap();
assert_eq!(nested_parsed["data"]["user"]["profile"]["name"], "张三");
assert_eq!(
nested_parsed["data"]["permissions"]
.as_array()
.unwrap()
.len(),
2
);
let validation_error_response = json!({
"code": 400,
"msg": "参数验证失败",
"error": {
"log_id": "validation_error_456",
"details": [
{
"field": "email",
"error_code": "INVALID_FORMAT",
"message": "邮箱格式不正确"
}
]
}
});
let validation_parsed: Value = serde_json::from_value(validation_error_response).unwrap();
assert_eq!(validation_parsed["code"], 400);
assert!(!validation_parsed["error"]["details"]
.as_array()
.unwrap()
.is_empty());
}
#[test]
fn test_optimized_response_performance_characteristics() {
use std::time::Instant;
let start = Instant::now();
let mut responses = Vec::new();
for i in 0..1000 {
responses.push(OptimizedBaseResponse {
code: if i % 10 == 0 { 400 } else { 0 },
msg: if i % 10 == 0 {
"error".to_string()
} else {
"success".to_string()
},
error: if i % 10 == 0 {
Some(ErrorInfo {
log_id: Some(format!("log_{}", i)),
details: vec![],
})
} else {
None
},
data: if i % 10 != 0 {
Some(TestData {
id: i,
name: format!("test_{}", i),
})
} else {
None
},
});
}
let creation_time = start.elapsed();
assert_eq!(responses.len(), 1000);
assert!(creation_time.as_millis() < 100);
let start = Instant::now();
let successful_responses: Vec<_> = responses.iter().filter(|r| r.is_success()).collect();
let filter_time = start.elapsed();
assert_eq!(successful_responses.len(), 900);
assert!(filter_time.as_millis() < 10); }
}
mod usage_examples {}