looprs 0.1.11

Concise coding assistant REPL
Documentation
use serde_json::Value;
use std::fs::{self, OpenOptions};
use std::io::{self, Write};
use std::path::{Path, PathBuf};
use std::time::{SystemTime, UNIX_EPOCH};

const OBSERVABILITY_DIR_ENV: &str = "LOOPRS_OBSERVABILITY_DIR";

pub fn observability_root() -> PathBuf {
    std::env::var(OBSERVABILITY_DIR_ENV)
        .ok()
        .filter(|v| !v.trim().is_empty())
        .map(PathBuf::from)
        .unwrap_or_else(|| PathBuf::from(".looprs/observability"))
}

pub fn trace_dir() -> PathBuf {
    observability_root().join("traces")
}

pub fn append_named_jsonl(name: &str, value: &Value) -> io::Result<()> {
    let path = observability_root().join(format!("{name}.jsonl"));
    append_jsonl(&path, value)
}

pub fn append_jsonl(path: &Path, value: &Value) -> io::Result<()> {
    if let Some(parent) = path.parent() {
        fs::create_dir_all(parent)?;
    }

    let mut file = OpenOptions::new().create(true).append(true).open(path)?;
    let mut record = serde_json::Map::new();
    record.insert("ts".to_string(), Value::Number(now_millis().into()));
    record.insert("event".to_string(), value.clone());
    writeln!(
        file,
        "{}",
        serde_json::to_string(&Value::Object(record)).map_err(io::Error::other)?
    )?;
    Ok(())
}

fn now_millis() -> u64 {
    SystemTime::now()
        .duration_since(UNIX_EPOCH)
        .map(|d| d.as_millis() as u64)
        .unwrap_or(0)
}