tgqe 0.0.1

The Great Qin Empire —— A centralized system for integrating and summarizing common compiler front-end library errors and span error output libraries, with optional Ariadne integration.
/**

 *  - Copyright (c) 星灿长风v(Starwindv) 2026/08/03
 *  - License: BSD-3
 *  - Author: 星灿长风v(StarWindv)
 *  - Location: tgqe/src/modules/channel/renderer.rs
 */
use ariadne::{
    Color, Config, IndexType, Label, Report,
    ReportKind, Source,
};

use crate::base_types::TgqeCtx;
use crate::enums::TgqeLevelFilter;
use crate::singletons::{
    Configure, SourceManager,
};

/**

 * ## I. What
 *
 * ### 1.1 Description
 *
 * This structure is a zero-sized type.
 *
 * It draws a `TgqeCtx` into a human-readable error report
 * via the `ariadne` crate.
 *
 * ---
 *
 * ## II. Default Value:
 * - There is no field, so there is nothing to default.
 */
pub struct Renderer;

impl Renderer {
    pub fn draw(error: &TgqeCtx) {
        let Some(source_text) = ({
            let cache = SourceManager
                .lock()
                .unwrap();
            cache.source_text(
                &error
                    .position
                    .coord
                    .filepath,
            )
        }) else {
            eprintln!(
                "[TGQE][{}] {}",
                error.publisher,
                error.err_info.got
            );
            return;
        };

        let filepath = if error
            .position
            .coord
            .filepath
            .is_empty()
        {
            "unknown"
        } else {
            error
                .position
                .coord
                .filepath
                .as_str()
        };

        let len = source_text.len();
        let start = (error
            .position
            .span
            .start
            .offset
            as usize)
            .min(len);
        let end =
            (error.position.span.end.offset
                as usize)
                .min(len)
                .max(start);

        let span = (filepath, start..end);

        let (kind, color) = match error
            .err_info
            .level
        {
            TgqeLevelFilter::Warn => (
                ReportKind::Warning,
                Color::Yellow,
            ),
            TgqeLevelFilter::Info => (
                ReportKind::Advice,
                Color::Cyan,
            ),
            TgqeLevelFilter::Error
            | TgqeLevelFilter::Undefined => {
                (
                    ReportKind::Error,
                    Color::Red,
                )
            }
        };

        let mut builder = Report::build(
            kind,
            span.clone(),
        )
        .with_config(
            Config::default()
                .with_index_type(
                    IndexType::Byte,
                )
                .with_compact(
                    Configure.compact,
                ),
        )
        .with_message(format!(
            "{}: {}",
            error.err_info.err_type,
            error.err_info.got
        ))
        .with_label(
            Label::new(span.clone())
                .with_message(
                    error
                        .err_info
                        .expect
                        .clone(),
                )
                .with_color(color),
        );

        builder.add_note(format!(
            "\n - Publisher: {}\n - Label: {}\n - Hints: {}\n - Recoverable: {}\n - Time: {}",
            error.publisher,
            error.labels,
            error.hints,
            error.err_info.recoverable,
            Self::format_time(error.ns_timestamp),
        ));

        if let Err(e) =
            builder.finish().eprint((
                filepath,
                Source::from(source_text),
            ))
        {
            eprintln!(
                "[TGQE] Failed to render the error msg: {e}"
            );
        }
    }

    /// timestamp to local time
    fn format_time(ns: i64) -> String {
        chrono::DateTime::from_timestamp_nanos(ns)
            .with_timezone(&chrono::Local)
            .format("%Y-%m-%d %H:%M:%S")
            .to_string()
    }
}