diag_lang/lib.rs
1//! # diag_lang
2//!
3//! The diagnostics layer of a compiler front-end: it collects diagnostics — a
4//! severity, a message, and a labelled span — and renders them with source
5//! context, drawing a caret underline exactly under the characters at fault, in
6//! the style developers expect from a modern toolchain.
7//!
8//! It owns presentation only. A lexer, parser, or type checker decides *what* is
9//! wrong and builds a [`Diagnostic`]; diag-lang decides how it looks. Positions
10//! come from [`span_lang`] and source text from [`source_lang`]: a [`Label`]
11//! points with a [`Span`] in a [`SourceMap`]'s global position space, and the
12//! [`Renderer`] resolves that span back to a file, a line, and a column.
13//!
14//! ## Alignment
15//!
16//! The caret line is aligned in display columns, not bytes: a multi-byte UTF-8
17//! character counts as one column, and a tab advances to the next tab stop. The
18//! same diagnostic always renders byte-identical output, and a span that falls
19//! outside the loaded sources renders a defined note rather than panicking.
20//!
21//! ## Quickstart
22//!
23//! ```
24//! use diag_lang::{Diagnostic, Label, Renderer, Severity};
25//! use diag_lang::{SourceMap, Span};
26//!
27//! let mut map = SourceMap::new();
28//! map.add("main.rs", "fn main() {\n let x = foo();\n}\n").expect("fits");
29//!
30//! let diag = Diagnostic::new(
31//! Severity::Error,
32//! "cannot find value `foo` in this scope",
33//! Label::new(Span::new(24, 27), "not found in this scope"),
34//! );
35//!
36//! let rendered = Renderer::new().render(&diag, &map);
37//! assert!(rendered.contains("^^^ not found in this scope"));
38//! ```
39//!
40//! ## Output
41//!
42//! [`Renderer::render`] returns a `String`; [`Renderer::render_to`] writes into
43//! any [`core::fmt::Write`] sink, so the same renderer feeds a terminal, a reused
44//! buffer, or a formatter without an intermediate allocation. With the `color`
45//! feature, [`Renderer::with_color`] wraps the output in ANSI styling; the plain
46//! output is complete and identically aligned, so colour only ever adds.
47//!
48//! ## Features
49//!
50//! - `std` (default) — the standard library; without it the crate is `no_std`
51//! (it always needs `alloc`). Forwards to `span-lang/std` and `source-lang/std`.
52//! - `color` (default) — ANSI styling via [`Renderer::with_color`]. Disable it,
53//! or simply never call `with_color`, for plain output.
54//!
55//! ## Stability
56//!
57//! The public API is stable as of `1.0` and follows Semantic Versioning: no
58//! breaking changes before `2.0`, additions arrive in minor releases, and the MSRV
59//! (Rust 1.85) only rises in a minor. The full surface is catalogued in
60//! [`docs/API.md`](https://github.com/jamesgober/diag-lang/blob/main/docs/API.md).
61
62#![cfg_attr(not(feature = "std"), no_std)]
63#![cfg_attr(docsrs, feature(doc_cfg))]
64#![deny(missing_docs)]
65#![forbid(unsafe_code)]
66
67extern crate alloc;
68
69mod diagnostic;
70mod label;
71mod render;
72mod severity;
73
74pub use diagnostic::Diagnostic;
75pub use label::Label;
76pub use render::Renderer;
77pub use severity::Severity;
78
79// Re-exported so a downstream building and rendering diagnostics can spell the
80// position and source types this crate's API uses without also having to name
81// `span-lang` and `source-lang`.
82pub use source_lang::SourceMap;
83pub use span_lang::Span;