use std::path::Path;
pub fn extract_session_id_from_logfile(
logfile: &str,
workspace: &dyn crate::workspace::Workspace,
) -> Option<String> {
let logfile_path = Path::new(logfile);
let content = workspace.read(logfile_path).ok()?;
content
.lines()
.take(10)
.find_map(extract_session_id_from_json_line)
}
pub(super) fn extract_session_id_from_json_line(line: &str) -> Option<String> {
let value: serde_json::Value = serde_json::from_str(line).ok()?;
if let Some(session_id) = value.get("session_id").and_then(|v| v.as_str()) {
return Some(session_id.to_string());
}
if let Some(session_id) = value.get("sessionID").and_then(|v| v.as_str()) {
return Some(session_id.to_string());
}
None
}
pub fn extract_error_message_from_logfile(
logfile: &str,
workspace: &dyn crate::workspace::Workspace,
) -> Option<String> {
let logfile_path = Path::new(logfile);
let content = workspace.read(logfile_path).ok()?;
content
.lines()
.rev()
.take(50)
.find_map(extract_error_message_from_json_line)
}
pub fn extract_error_identifier_from_logfile(
logfile: &str,
workspace: &dyn crate::workspace::Workspace,
) -> Option<String> {
let logfile_path = Path::new(logfile);
let content = workspace.read(logfile_path).ok()?;
content
.lines()
.rev()
.take(50)
.find_map(extract_error_identifier_from_json_line)
}
pub(super) fn is_explicit_error_event(value: &serde_json::Value) -> bool {
matches!(value.get("type").and_then(|v| v.as_str()), Some("error"))
}
pub(super) fn error_code_to_human_message(code: &str) -> Option<&'static str> {
match code {
"usage_limit_exceeded" | "usage_limit_reached" => Some("usage limit reached"),
"rate_limit_exceeded" => Some("rate limit exceeded"),
"quota_exceeded" | "insufficient_quota" => Some("quota exceeded"),
_ => None,
}
}
pub(super) fn extract_error_message_from_json_line(line: &str) -> Option<String> {
let value: serde_json::Value = serde_json::from_str(line).ok()?;
if !is_explicit_error_event(&value) {
return None;
}
if let Some(provider) = value.pointer("/error/provider").and_then(|v| v.as_str()) {
if let Some(msg) = value.pointer("/error/message").and_then(|v| v.as_str()) {
return Some(format!("{provider}: {msg}"));
}
}
if let Some(data_message) = value
.pointer("/error/data/message")
.and_then(|v| v.as_str())
{
return Some(data_message.to_string());
}
if let Some(error_message) = value.pointer("/error/message").and_then(|v| v.as_str()) {
return Some(error_message.to_string());
}
if let Some(message) = value.get("message").and_then(|v| v.as_str()) {
return Some(message.to_string());
}
if let Some(code) = value.pointer("/error/code").and_then(|v| v.as_str()) {
if let Some(mapped) = error_code_to_human_message(code) {
return Some(mapped.to_string());
}
}
value
.pointer("/error/name")
.and_then(|v| v.as_str())
.map(ToString::to_string)
}
pub(super) fn extract_error_identifier_from_json_line(line: &str) -> Option<String> {
let value: serde_json::Value = serde_json::from_str(line).ok()?;
if !is_explicit_error_event(&value) {
return None;
}
if let Some(code) = value.pointer("/error/code").and_then(|v| v.as_str()) {
return Some(code.to_string());
}
if let Some(provider) = value.pointer("/error/provider").and_then(|v| v.as_str()) {
if let Some(msg) = value.pointer("/error/message").and_then(|v| v.as_str()) {
return Some(format!("{provider}: {msg}"));
}
}
if let Some(data_message) = value
.pointer("/error/data/message")
.and_then(|v| v.as_str())
{
return Some(data_message.to_string());
}
if let Some(error_message) = value.pointer("/error/message").and_then(|v| v.as_str()) {
return Some(error_message.to_string());
}
if let Some(message) = value.get("message").and_then(|v| v.as_str()) {
return Some(message.to_string());
}
value
.pointer("/error/name")
.and_then(|v| v.as_str())
.map(ToString::to_string)
}