use serde::{Deserialize, Deserializer, Serialize};
use serde_json::Value;
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiSession {
pub session_id: String,
#[serde(default)]
pub project_hash: String,
#[serde(default)]
pub start_time: String,
#[serde(default)]
pub last_updated: String,
#[serde(default)]
pub kind: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct GeminiMessage {
#[serde(default)]
pub id: String,
#[serde(default)]
pub timestamp: String,
#[serde(rename = "type", default)]
pub message_type: String,
#[serde(default, deserialize_with = "deserialize_content")]
pub content: String,
#[serde(default)]
pub thoughts: Vec<GeminiThought>,
pub tokens: Option<GeminiTokens>,
pub model: Option<String>,
#[serde(default)]
pub tool_calls: Vec<Value>,
}
fn deserialize_content<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let value = Value::deserialize(deserializer)?;
match value {
Value::String(s) => Ok(s),
Value::Array(arr) => {
let texts: Vec<&str> = arr
.iter()
.filter_map(|item| item.get("text").and_then(|t| t.as_str()))
.collect();
Ok(texts.join("\n"))
}
Value::Null => Ok(String::new()),
_ => Ok(value.to_string()),
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeminiThought {
#[serde(default)]
pub subject: String,
#[serde(default)]
pub description: String,
#[serde(default)]
pub timestamp: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GeminiTokens {
#[serde(default)]
pub input: i64,
#[serde(default)]
pub output: i64,
#[serde(default)]
pub cached: i64,
#[serde(default)]
pub thoughts: i64,
#[serde(default)]
pub tool: i64,
#[serde(default)]
pub total: i64,
}