batless 0.6.0

A non-blocking, LLM-friendly code viewer inspired by bat
Documentation
//! JSON output formatter

use crate::config::BatlessConfig;
use crate::error::{BatlessError, BatlessResult};
use crate::file_info::FileInfo;
use crate::formatter::OutputMode;
use crate::formatters::Formatter;
use serde_json::json;

pub struct JsonFormatter;

impl Formatter for JsonFormatter {
    fn format(
        &self,
        file_info: &FileInfo,
        file_path: &str,
        config: &BatlessConfig,
    ) -> BatlessResult<String> {
        let line_source = file_info
            .original_lines
            .as_ref()
            .unwrap_or(&file_info.lines);

        let lines_value: serde_json::Value = if config.json_line_numbers {
            line_source
                .iter()
                .enumerate()
                .map(|(i, text)| json!({"n": i + 1, "text": text}))
                .collect()
        } else {
            json!(line_source)
        };

        let mut json_data = json!({
            "file": file_path,
            "lines": lines_value,
            "processed_lines": file_info.processed_lines(),
            "total_lines": file_info.total_lines,
            "total_lines_exact": file_info.total_lines_exact,
            "total_bytes": file_info.total_bytes,
            "truncated": file_info.truncated,
            "truncated_by_lines": file_info.truncated_by_lines,
            "truncated_by_bytes": file_info.truncated_by_bytes,
            "truncated_by_context": file_info.truncated_by_context,
            "language": file_info.language,
            "encoding": file_info.encoding,
            "syntax_errors": file_info.syntax_errors,
            "mode": "json"
        });

        if let Some(ref tokens) = file_info.tokens {
            json_data["identifiers"] = json!(tokens);
        }
        json_data["identifier_count"] = json!(file_info.token_count());
        json_data["identifiers_truncated"] = json!(file_info.tokens_truncated());

        if let Some(ref summary_lines) = file_info.summary_lines {
            json_data["summary_lines"] = json!(summary_lines);
        }
        if let Some(ref hash) = file_info.file_hash {
            json_data["file_hash"] = json!(hash);
        }
        if let Some(estimated) = file_info.estimated_llm_tokens {
            json_data["estimated_llm_tokens"] = json!(estimated);
        }
        if let Some(ref model) = file_info.token_model {
            json_data["token_model"] = json!(model);
        }
        if let Some(ratio) = file_info.compression_ratio {
            json_data["compression_ratio"] = json!(ratio);
        }

        if config.pretty_json {
            serde_json::to_string_pretty(&json_data).map_err(BatlessError::from)
        } else {
            serde_json::to_string(&json_data).map_err(BatlessError::from)
        }
    }

    fn output_mode(&self) -> OutputMode {
        OutputMode::Json
    }
}