160 lines
5.0 KiB
Rust
160 lines
5.0 KiB
Rust
use serde_json::Value;
|
|
|
|
pub(crate) fn parse_api_error_message(raw_text: &str, fallback_message: &str) -> String {
|
|
if let Ok(parsed) = serde_json::from_str::<Value>(raw_text) {
|
|
for key in ["message", "detail", "error"] {
|
|
if let Some(message) = find_first_string_by_key(&parsed, key)
|
|
&& !message.trim().is_empty()
|
|
{
|
|
return message;
|
|
}
|
|
}
|
|
}
|
|
raw_text
|
|
.trim()
|
|
.chars()
|
|
.take(240)
|
|
.collect::<String>()
|
|
.trim()
|
|
.to_string()
|
|
.chars()
|
|
.next()
|
|
.map(|_| raw_text.trim().chars().take(240).collect())
|
|
.unwrap_or_else(|| fallback_message.to_string())
|
|
}
|
|
|
|
pub(crate) fn find_first_array_by_keys<'a>(
|
|
value: &'a Value,
|
|
keys: &[&str],
|
|
) -> Option<&'a Vec<Value>> {
|
|
match value {
|
|
Value::Object(object) => {
|
|
for (key, value) in object {
|
|
if keys.iter().any(|target| key.eq_ignore_ascii_case(target))
|
|
&& let Some(array) = value.as_array()
|
|
{
|
|
return Some(array);
|
|
}
|
|
if let Some(found) = find_first_array_by_keys(value, keys) {
|
|
return Some(found);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
Value::Array(items) => items
|
|
.iter()
|
|
.find_map(|item| find_first_array_by_keys(item, keys)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn find_first_string_by_keys(value: &Value, keys: &[&str]) -> Option<String> {
|
|
keys.iter()
|
|
.find_map(|key| find_first_string_by_key(value, key))
|
|
}
|
|
|
|
pub(crate) fn find_first_f64_by_keys(value: &Value, keys: &[&str]) -> Option<f64> {
|
|
match value {
|
|
Value::Object(object) => {
|
|
for (key, value) in object {
|
|
if keys.iter().any(|target| key.eq_ignore_ascii_case(target))
|
|
&& let Some(number) = value.as_f64()
|
|
{
|
|
return Some(number);
|
|
}
|
|
if let Some(found) = find_first_f64_by_keys(value, keys) {
|
|
return Some(found);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
Value::Array(items) => items
|
|
.iter()
|
|
.find_map(|item| find_first_f64_by_keys(item, keys)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn find_first_string_by_key(value: &Value, target_key: &str) -> Option<String> {
|
|
match value {
|
|
Value::Object(object) => {
|
|
for (key, value) in object {
|
|
if key.eq_ignore_ascii_case(target_key)
|
|
&& let Some(text) = value.as_str()
|
|
{
|
|
return Some(text.trim().to_string());
|
|
}
|
|
if let Some(found) = find_first_string_by_key(value, target_key) {
|
|
return Some(found);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
Value::Array(items) => items
|
|
.iter()
|
|
.find_map(|item| find_first_string_by_key(item, target_key)),
|
|
_ => None,
|
|
}
|
|
}
|
|
|
|
pub(crate) fn find_root_string_by_keys(value: &Value, keys: &[&str]) -> Option<String> {
|
|
let object = value.as_object()?;
|
|
for key in keys {
|
|
if let Some(text) = object
|
|
.iter()
|
|
.find(|(candidate, _)| candidate.eq_ignore_ascii_case(key))
|
|
.and_then(|(_, value)| value.as_str())
|
|
.map(str::trim)
|
|
.filter(|value| !value.is_empty())
|
|
{
|
|
return Some(text.to_string());
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
pub(crate) fn collect_strings_by_keys(value: &Value, keys: &[&str]) -> Vec<String> {
|
|
let mut results = Vec::new();
|
|
collect_strings(value, keys, &mut results);
|
|
let mut deduped = Vec::new();
|
|
for result in results {
|
|
if !deduped.contains(&result) {
|
|
deduped.push(result);
|
|
}
|
|
}
|
|
deduped
|
|
}
|
|
|
|
fn collect_strings(value: &Value, keys: &[&str], output: &mut Vec<String>) {
|
|
match value {
|
|
Value::Object(object) => {
|
|
for (key, value) in object {
|
|
if keys.iter().any(|target| key.eq_ignore_ascii_case(target)) {
|
|
match value {
|
|
Value::String(text) if !text.trim().is_empty() => {
|
|
output.push(text.trim().to_string());
|
|
}
|
|
Value::Array(items) => {
|
|
for item in items {
|
|
if let Some(text) = item.as_str().map(str::trim)
|
|
&& !text.is_empty()
|
|
{
|
|
output.push(text.to_string());
|
|
}
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|
|
collect_strings(value, keys, output);
|
|
}
|
|
}
|
|
Value::Array(items) => {
|
|
for item in items {
|
|
collect_strings(item, keys, output);
|
|
}
|
|
}
|
|
_ => {}
|
|
}
|
|
}
|