fix on chart config object

This commit is contained in:
dal 2025-03-31 15:23:33 -06:00
parent 7111f9e110
commit ec0524f83a
No known key found for this signature in database
GPG Key ID: 16F4B0E1E9F61122
1 changed files with 105 additions and 28 deletions

View File

@ -152,35 +152,112 @@ impl<'de> Deserialize<'de> for ChartConfig {
// Helper function to create detailed error messages for deserialization failures // Helper function to create detailed error messages for deserialization failures
fn detailed_deserialization_error<E: serde::de::Error>(chart_type: &str, err: &serde_json::Error) -> E { fn detailed_deserialization_error<E: serde::de::Error>(chart_type: &str, err: &serde_json::Error) -> E {
match err.classify() {
serde_json::error::Category::Data => {
// Type mismatch or invalid value
let error_message = format!("{} config error: {}", chart_type, err);
// Extract the field path from the error message if possible
if let Some(field_name) = extract_field_from_error(err) {
E::custom(format!("{} config error at field '{}': {}", chart_type, field_name, err))
} else {
E::custom(error_message)
}
}
serde_json::error::Category::Syntax | serde_json::error::Category::Io | serde_json::error::Category::Eof => {
// Other errors (shouldn't happen in this context)
E::custom(format!("{} config parse error: {}", chart_type, err))
}
}
}
// Helper function to extract field name from error message
fn extract_field_from_error(err: &serde_json::Error) -> Option<String> {
// Get the error message as a string // Get the error message as a string
let err_msg = err.to_string(); let err_msg = err.to_string();
// Try to extract field path using common patterns in serde_json error messages // Match different error patterns to extract field information
if let Some(start_idx) = err_msg.find("at key ") { let detailed_error = match extract_detailed_error_info(&err_msg) {
if let Some(key_start) = err_msg[start_idx + 8..].find('"') { Some((field_name, expected_type, found_type)) => {
if let Some(key_end) = err_msg[start_idx + 8 + key_start + 1..].find('"') { // Complete type mismatch info available
let field = &err_msg[start_idx + 8 + key_start + 1..start_idx + 8 + key_start + 1 + key_end]; format!(
"{} config error: field '{}' has invalid type: {}, expected {}",
chart_type, field_name, found_type, expected_type
)
}
None => {
// Try to at least extract the field name
if let Some(field_name) = extract_field_from_error(&err_msg) {
format!("{} config error at field '{}': {}", chart_type, field_name, err)
} else {
format!("{} config error: {}", chart_type, err)
}
}
};
E::custom(detailed_error)
}
// Helper function to extract detailed type mismatch information
fn extract_detailed_error_info(err_msg: &str) -> Option<(String, String, String)> {
// Pattern for serde "invalid type" errors: "invalid type: X, expected Y at ..."
if err_msg.starts_with("invalid type:") {
// Extract expected and found types
let parts: Vec<&str> = err_msg.split(", expected ").collect();
if parts.len() >= 2 {
let found_type = parts[0].replace("invalid type: ", "").trim().to_string();
// Extract field path and expected type
let remaining = parts[1];
let field_parts: Vec<&str> = remaining.split(" at ").collect();
let expected_type = field_parts[0].trim().to_string();
// Try to extract field name
if field_parts.len() >= 2 {
if let Some(field_name) = extract_field_path(field_parts[1]) {
return Some((field_name, expected_type, found_type));
}
}
// If we couldn't extract field but have types, use a placeholder
return Some(("unknown_field".to_string(), expected_type, found_type));
}
}
None
}
// Helper function to extract field path from location info
fn extract_field_path(location_info: &str) -> Option<String> {
// Try to extract field name from different patterns
// Pattern: key `field_name` at line X column Y
if let Some(start_idx) = location_info.find("key `") {
if let Some(end_idx) = location_info[start_idx + 5..].find('`') {
return Some(location_info[start_idx + 5..start_idx + 5 + end_idx].to_string());
}
}
// Pattern: at key "field_name"
if let Some(start_idx) = location_info.find("at key \"") {
if let Some(end_idx) = location_info[start_idx + 8..].find('"') {
return Some(location_info[start_idx + 8..start_idx + 8 + end_idx].to_string());
}
}
// Pattern: "field_name":
if let Some(start_idx) = location_info.find('"') {
if let Some(end_idx) = location_info[start_idx + 1..].find('"') {
return Some(location_info[start_idx + 1..start_idx + 1 + end_idx].to_string());
}
}
None
}
// Helper function to extract field name from error message
fn extract_field_from_error(err_msg: &str) -> Option<String> {
// Try multiple patterns to extract field names
// Pattern: key `field_name` at line X column Y
if let Some(start_idx) = err_msg.find("key `") {
if let Some(end_idx) = err_msg[start_idx + 5..].find('`') {
return Some(err_msg[start_idx + 5..start_idx + 5 + end_idx].to_string());
}
}
// Pattern: at key "field_name"
if let Some(start_idx) = err_msg.find("at key \"") {
if let Some(end_idx) = err_msg[start_idx + 8..].find('"') {
return Some(err_msg[start_idx + 8..start_idx + 8 + end_idx].to_string());
}
}
// Pattern: "field_name":
if let Some(start_idx) = err_msg.find('"') {
if let Some(end_idx) = err_msg[start_idx + 1..].find('"') {
let field = &err_msg[start_idx + 1..start_idx + 1 + end_idx];
// Avoid capturing things that aren't likely field names
if !field.contains(' ') && field.len() > 0 && field.len() < 50 {
return Some(field.to_string()); return Some(field.to_string());
} }
} }
@ -208,7 +285,7 @@ pub struct BaseChartConfig {
pub grid_lines: Option<bool>, pub grid_lines: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[serde(alias = "show_legend_headline")] #[serde(alias = "show_legend_headline")]
pub show_legend_headline: Option<String>, pub show_legend_headline: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[serde(alias = "goal_lines")] #[serde(alias = "goal_lines")]
pub goal_lines: Option<Vec<GoalLine>>, pub goal_lines: Option<Vec<GoalLine>>,
@ -367,7 +444,7 @@ pub struct BarLineChartConfig {
pub bar_layout: Option<String>, pub bar_layout: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[serde(alias = "bar_sort_by")] #[serde(alias = "bar_sort_by")]
pub bar_sort_by: Option<String>, pub bar_sort_by: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")] #[serde(skip_serializing_if = "Option::is_none")]
#[serde(alias = "bar_group_type")] #[serde(alias = "bar_group_type")]
pub bar_group_type: Option<String>, pub bar_group_type: Option<String>,