use agent_types::common::BuildError;
use serde_json::Value;
use super::backend::BACKEND_TYPE_MOIRAI_SQLITE;
pub(crate) fn default_db_path() -> String {
let home = dirs::home_dir().unwrap_or_else(|| ".".into());
home.join(".xiaoo/data/trace.db")
.to_string_lossy()
.into_owned()
}
#[derive(Debug, Clone)]
pub(crate) struct TraceRecorderConfig {
pub(crate) storage_backend: String,
pub(crate) db_path: Option<String>,
pub(crate) agent_id: Option<String>,
}
impl Default for TraceRecorderConfig {
fn default() -> Self {
Self {
storage_backend: BACKEND_TYPE_MOIRAI_SQLITE.to_string(),
db_path: Some(default_db_path()),
agent_id: None,
}
}
}
impl TraceRecorderConfig {
pub(crate) fn from_json(config: Value) -> Result<Self, BuildError> {
match config {
Value::Null => Ok(Self::default()),
Value::Object(map) => {
let storage_backend = match map.get("storage_backend") {
None => BACKEND_TYPE_MOIRAI_SQLITE.to_string(),
Some(Value::String(value)) => value.clone(),
Some(_) => {
return Err(BuildError::InvalidConfig {
message: "storage_backend must be a string".to_string(),
});
}
};
let db_path =
parse_optional_string(&map, "db_path")?.or_else(|| Some(default_db_path()));
let agent_id = parse_optional_string(&map, "agent_id")?;
Ok(Self {
storage_backend,
db_path,
agent_id,
})
}
_ => Err(BuildError::InvalidConfig {
message: "trace recorder config must be a JSON object or null".to_string(),
}),
}
}
}
fn parse_optional_string(
map: &serde_json::Map<String, Value>,
key: &str,
) -> Result<Option<String>, BuildError> {
match map.get(key) {
None | Some(Value::Null) => Ok(None),
Some(Value::String(value)) => Ok(Some(value.clone())),
Some(_) => Err(BuildError::InvalidConfig {
message: format!("{key} must be a string when provided"),
}),
}
}