use crate::read::plot;
use crate::read::speaker_suggestions::{
ApiError, fetch_available_speakers, find_similar_speakers, format_speaker_not_found_error,
};
use serde_json::Value;
use std::error::Error;
pub fn normalize_plotly_value(v: &Value) -> Result<Value, Box<dyn Error>> {
if let Some(arr) = v.as_array() {
if let Some(first) = arr.first() {
if let Some(s) = first.as_str() {
let parsed: Value = serde_json::from_str(s)?;
return Ok(parsed);
} else {
return Err("First element is not a string".into());
}
} else {
return Err("Empty API response".into());
}
}
Err("API response is not an array".into())
}
pub async fn normalize_plotly_value_with_suggestions(v: &Value) -> Result<Value, Box<dyn Error>> {
if let Some(arr) = v.as_array() {
if let Some(first) = arr.first() {
if let Some(s) = first.as_str() {
let parsed: Value = serde_json::from_str(s)?;
return Ok(parsed);
} else {
return Err("First element is not a string".into());
}
} else {
return Err("Empty API response".into());
}
}
if let Some(api_error) = ApiError::from_json(v) {
if let Some(invalid_speaker) = api_error.speaker_name {
match fetch_available_speakers().await {
Ok(available_speakers) => {
let suggestions =
find_similar_speakers(&invalid_speaker, &available_speakers, 5);
let helpful_error =
format_speaker_not_found_error(&invalid_speaker, &suggestions);
return Err(helpful_error.into());
}
Err(_) => {
let fallback_error = format!(
"Speaker '{}' not found in the database. Unable to fetch suggestions at this time. Please check the speaker name and try again.",
invalid_speaker
);
return Err(fallback_error.into());
}
}
} else {
return Err(api_error.message.into());
}
}
Err("API response is not an array".into())
}
pub fn normalize_plotly_json_from_str(content: &str) -> Result<Value, Box<dyn Error>> {
let v: Value = serde_json::from_str(content)?;
if v.is_object() {
return Ok(v);
}
if let Ok(parsed) = plot::normalize_plotly_value(&v) {
return Ok(parsed);
}
if let Some(s) = v.as_str() {
let inner: Value = serde_json::from_str(s)?;
return Ok(inner);
}
Err("Unrecognized cached JSON format".into())
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn normalize_plotly_handles_object_array_and_string() {
let obj = json!({"data": [{"name": "On Axis"}]});
let s1 = serde_json::to_string(&obj).unwrap();
let p1 = normalize_plotly_json_from_str(&s1).unwrap();
assert!(p1.get("data").is_some());
let inner = json!({"data": [{"name": "Listening Window"}]});
let s_inner = serde_json::to_string(&inner).unwrap();
let api = json!([s_inner]);
let s2 = serde_json::to_string(&api).unwrap();
let p2 = normalize_plotly_json_from_str(&s2).unwrap();
assert!(p2.get("data").is_some());
let s3 = serde_json::to_string(&s_inner).unwrap();
let p3 = normalize_plotly_json_from_str(&s3).unwrap();
assert!(p3.get("data").is_some());
}
#[test]
fn test_normalize_plotly_value_with_valid_array() {
let plotly_data = json!({"data": [{"name": "Test"}]});
let plotly_str = serde_json::to_string(&plotly_data).unwrap();
let api_response = json!([plotly_str]);
let result = normalize_plotly_value(&api_response).unwrap();
assert_eq!(result, plotly_data);
}
#[test]
fn test_normalize_plotly_value_with_error_object() {
let error_response = json!({
"error": "Speaker ASCILAB F6B is not in our database!"
});
let result = normalize_plotly_value(&error_response);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert_eq!(error_msg, "API response is not an array");
}
#[test]
fn test_normalize_plotly_value_with_empty_array() {
let empty_array = json!([]);
let result = normalize_plotly_value(&empty_array);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert_eq!(error_msg, "Empty API response");
}
#[test]
fn test_normalize_plotly_value_with_invalid_content() {
let invalid_array = json!([123]);
let result = normalize_plotly_value(&invalid_array);
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert_eq!(error_msg, "First element is not a string");
}
#[tokio::test]
async fn test_normalize_with_suggestions_detects_speaker_error() {
let error_response = json!({
"error": "Speaker Test Speaker is not in our database!"
});
let result = normalize_plotly_value_with_suggestions(&error_response).await;
assert!(result.is_err());
let error_msg = result.unwrap_err().to_string();
assert!(error_msg.contains("Test Speaker"));
}
}