tgqe 0.0.3

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.
# TGQE


[中文(简体)](./readme_cn.md)

## TOC


- [TGQE]#tgqe
  - [TOC]#toc
  - [I. Description]#i-description
  - [II. Usage]#ii-usage
    - [2.1 Quick Start]#21-quick-start
    - [2.2 Simple Example]#22-simple-example
    - [2.3 Integrate]#23-integrate
    - [2.4 Example]#24-example
  - [III. Output]#iii-output
  - [IV. License]#iv-license


## I. Description


`TGQE` is a Crate that makes it easy to integrate error handling from various compiler frontends, and comes with error conversions for multiple libraries and tunable configuration environment variables. When the number of errors exceeds a threshold, the remaining errors can be stored in `SQLite` to maintain a good reading experience. It uses `Ariadne` as the rendering implementation by default.

> This project has no special relationship with the other projects mentioned in this document.
> `tgqe` is only a comprehensive integration library, it cannot help you write a good Lexer and Parser.

---

## II. Usage


### 2.1 Quick Start


First, add `tgqe` to your project with the following command:

```shell
cargo add tgqe
```

Then enable the relevant `features` in your `Cargo.toml`. The currently available features are:

```toml
[features]

renderer      = ["ariadne", "dotenvy", "chrono"]

trans-chumsky = ["chumsky"]
trans-logos   = ["logos"]
trans-winnow  = ["winnow"]

store-to-db   = ["sqlx", "sqlx-sqlite", "chrono", "tokio", "dotenvy"]

chumsky-full = ["renderer", "trans-chumsky", "store-to-db"]
winnow-full  = ["renderer", "trans-winnow" , "store-to-db"]
logos-full   = ["renderer", "trans-logos"  , "store-to-db"]

all = ["chumsky-full", "winnow-full", "logos-full"]
```

> Since most of `logos`'s errors are compile-time errors, currently only `logos::Span` is converted.

---

### 2.2 Simple Example


Here is a simple example:

```rust
use tgqe::base_types::*;
use tgqe::enums::TgqeLevelFilter;
use tgqe::singletons::{ICC, SourceManager};

mod lexer; // Your Custom Lexer Implements

#[tokio::main(flavor = "current_thread")] // Only required if the `store-to-db` feature is enabled

async fn main() {
    if let Err(e) =
        TgqeReader::store_file(&path)
    {
        eprintln!("{}", e);
        std::process::exit(1);
    }
    let lines = SourceManager
        .lock()
        .unwrap()
        .iter_line()
        .filter(|line| {
            line.filepath == path
        });
    let mut lexer = lexer::Lexer::new();

    for line in lines {
        let (_, errors) =
            lexer.lex_line(&line.code);

        if !errors.is_empty() {
            let mut ctxs: Vec<TgqeCtx> =
                errors
                    .iter()
                    .map(|e| {
                        to_ctx(e, &path)
                    })
                    .collect();
            ICC.lock()
                .unwrap()
                .report(&mut ctxs) // Report the errors
                .await;
        }
    }
}

```

---

### 2.3 Integrate


You can also use it as an integration for your hand-written compiler frontend.

> Sometimes what we need is not a specific error, but an object that "looks like an error and can express the information we need".

`tgqe` exposes the following main information structs:

```rust
pub struct TgqeCtx {
    pub position: TgqePosition,
    pub err_info: TgqeErrorInfo,
    pub hints: String,
    pub labels: String,
    pub publisher: String,
    pub ns_timestamp: i64,
}

pub struct TgqePosition {
    pub coord: TgqeCoordinate,
    pub span: TgqeSpan,
}

pub struct TgqeCoordinate {
    pub filepath: String,
    pub offset: u32,
    pub line: i16,
    pub column: i8,
}

pub struct TgqeSpan {
    pub start: TgqeCoordinate,
    pub end: TgqeCoordinate,
}

pub struct TgqeErrorInfo {
    pub err_type: String,
    pub expect: String,
    pub got: String,
    pub level: TgqeLevelFilter,
    pub recoverable: bool,
}

pub enum TgqeLevelFilter {
    Error,
    Warn,
    Info,
    Undefined,
}
```

As you can see, these are all plain structs rather than inscrutable generic gymnastics, so you can easily implement `From` for your own data structures.

For example, for `chumsky::error::Rich`:

```rust
use chumsky::error::Rich;

impl<T: core::fmt::Display> From<Rich<'_, T>>
    for TgqeCtx
{
    fn from(value: Rich<'_, T>) -> Self {
        let (expect, got) = match value
            .reason()
        {
            RichReason::ExpectedFound {
                expected,
                found,
            } => (
                expected
                    .iter()
                    .map(ToString::to_string)
                    .collect::<Vec<_>>()
                    .join(", "),
                found
                    .as_ref()
                    .map(|tok| {
                        tok.to_string()
                    })
                    .unwrap_or_else(|| {
                        "end of input"
                            .to_string()
                    }),
            ),
            RichReason::Custom(msg) => (
                TGQE_DEFAULT_STR.to_string(),
                msg.clone(),
            ),
        };

        let hints = value
            .contexts()
            .map(|(label, _)| {
                label.to_string()
            })
            .collect::<Vec<_>>()
            .join(", ");

        Self::new(
            unknown_position(
                value.span().start as u32,
            ),
            TgqeErrorInfo::new(
                TGQE_ERRTYPE_CHUMSKY_RICH,
                &expect,
                &got,
                TgqeLevelFilter::Error,
            ),
            &hints,
            TGQE_DEFAULT_STR,
            TGQE_PUBLISHER_CHUMSKY,
            0,
        )
    }
}
```

---

### 2.4 Example


You can check [`tgqe-golex-example`](./crates/tgqe-golex-example) for more information.

---

## III. Output


The default renderer implementation of this project is based on `Ariadne`. It consumes the `TgqeCtx` objects and outputs error messages like the following:

![output](assets/img/1.png)

---

## IV. License


This project is open-sourced under the `BSD-3` license.