ccql 0.1.2

Claude Code Query Language - SQL query engine for Claude Code data
Documentation
use crate::error::{Error, Result};
use serde::de::DeserializeOwned;
use std::path::Path;
use tokio::io::{AsyncBufReadExt, BufReader};

pub async fn read_jsonl<T>(path: impl AsRef<Path>) -> Result<Vec<T>>
where
    T: DeserializeOwned,
{
    let path = path.as_ref();
    if !path.exists() {
        return Err(Error::FileNotFound(path.display().to_string()));
    }

    let file = tokio::fs::File::open(path).await?;
    let reader = BufReader::new(file);
    let mut lines = reader.lines();
    let mut entries = Vec::new();

    while let Some(line) = lines.next_line().await? {
        if line.trim().is_empty() {
            continue;
        }
        match serde_json::from_str::<T>(&line) {
            Ok(entry) => entries.push(entry),
            Err(e) => {
                tracing::debug!("Failed to parse line: {}", e);
                continue;
            }
        }
    }

    Ok(entries)
}

pub async fn read_jsonl_raw(path: impl AsRef<Path>) -> Result<Vec<serde_json::Value>> {
    read_jsonl::<serde_json::Value>(path).await
}

pub async fn read_json<T>(path: impl AsRef<Path>) -> Result<T>
where
    T: DeserializeOwned,
{
    let path = path.as_ref();
    if !path.exists() {
        return Err(Error::FileNotFound(path.display().to_string()));
    }

    let content = tokio::fs::read_to_string(path).await?;
    let data: T = serde_json::from_str(&content)?;
    Ok(data)
}