Leon template library ()

* leon: first implementation

* Update crates/leon/src/values.rs

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>

* Workaround orphan rules to make API more intuitive

* Fmt

* Clippy

* Use CoW

* Use cow for items too

* Test that const construction works

* leon: Initial attempt at O(n) parser

* leon: finish parser (except escapes)

* leon: Improve ergonomics of compile-time templates

* Document helpers

* leon: Docs tweaks

* leon: Use macro to minimise parser tests

* leon: add escapes to parser

* leon: test escapes preceding keys

* leon: add multibyte tests

* leon: test escapes following keys

* Format

* Debug

* leon: Don't actually need to keep track of the key

* leon: Parse to vec first

* leon: there's actually no need for string cows

* leon: reorganise and redo macro now that there's no coww

* Well that was silly

* leon: Adjust text end when pushing

* leon: catch unbalanced keys

* Add error tests

* leon: Catch unfinished escape

* Comment out debugging

* leon: fuzz

* Clippy

* leon: Box parse error

* leon: &dyn instead of impl

* Can't impl FromStr, so rename to parse

* Add Vec<> to values

* leon: Add benches for ways to supply values

* leon: Add bench comparing to std and tt

* Fix fuzz

* Fmt

* Split ParseError and RenderError

* Make miette optional

* Remove RenderError lifetime

* Simplify ParseError type schema

* Write concrete Values types instead of generics

* Add license files

* Reduce criterion deps

* Make default a cow

* Add a CLI leon tool

* Fix tests

* Clippy

* Disable cli by default

* Avoid failing the build when cli is off

* Add to ci

* Update crates/leon/src/main.rs

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>

* Update crates/leon/Cargo.toml

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>

* Bump version

* Error not transparent

* Diagnostic can do forwarding

* Simplify error type

* Expand doc examples

* Generic Values for Hash and BTree maps

* One more borrowed

* Forward implementations

* More generics

* Add has_keys

* Lock stdout in leon tool

* No more debug comments in parser

* Even more generics

* Macros to reduce bench duplication

* Further simplify error

* Fix leon main

* Stable support

* Clippy

---------

Co-authored-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
Félix Saparelli 2023-03-21 14:36:02 +13:00 committed by GitHub
parent daf8cdd010
commit 2227d363f7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 2382 additions and 162 deletions
crates/leon/src

148
crates/leon/src/lib.rs Normal file
View file

@ -0,0 +1,148 @@
//! Dead-simple string templating.
//!
//! Leon parses a template string into a list of tokens, and then substitutes
//! provided values in. Unlike other templating engines, it is extremely simple:
//! it supports no logic, only replaces. It is even simpler than `format!()`,
//! albeit with a similar syntax.
//!
//! # Syntax
//!
//! ```plain
//! it is better to rule { group }
//! one can live {adverb} without power
//! ```
//!
//! A replacement is denoted by `{` and `}`. The contents of the braces, trimmed
//! of any whitespace, are the key. Any text outside of braces is left as-is.
//!
//! To escape a brace, use `\{` or `\}`. To escape a backslash, use `\\`. Keys
//! cannot contain escapes.
//!
//! ```plain
//! \{ leon \}
//! ```
//!
//! The above examples, given the values `group = "no one"` and
//! `adverb = "honourably"`, would render to:
//!
//! ```plain
//! it is better to rule no one
//! one can live honourably without power
//! { leon }
//! ```
//!
//! # Usage
//!
//! A template is first parsed to a token list:
//!
//! ```
//! use leon::Template;
//!
//! let template = Template::parse("hello {name}").unwrap();
//! ```
//!
//! The template can be inspected, for example to check if a key is present:
//!
//! ```
//! # use leon::Template;
//! #
//! # let template = Template::parse("hello {name}").unwrap();
//! assert!(template.has_key("name"));
//! ```
//!
//! The template can be rendered to a string:
//!
//! ```
//! # use leon::Template;
//! use leon::vals;
//! #
//! # let template = Template::parse("hello {name}").unwrap();
//! assert_eq!(
//! template.render(
//! &vals(|_key| Some("marcus".into()))
//! ).unwrap().as_str(),
//! "hello marcus",
//! );
//! ```
//!
//! …or to a writer:
//!
//! ```
//! use std::io::Write;
//! # use leon::Template;
//! use leon::vals;
//! #
//! # let template = Template::parse("hello {name}").unwrap();
//! let mut buf: Vec<u8> = Vec::new();
//! template.render_into(
//! &mut buf,
//! &vals(|key| if key == "name" {
//! Some("julius".into())
//! } else {
//! None
//! })
//! ).unwrap();
//! assert_eq!(buf.as_slice(), b"hello julius");
//! ```
//!
//! …with a map:
//!
//! ```
//! use std::collections::HashMap;
//! # use leon::Template;
//! # let template = Template::parse("hello {name}").unwrap();
//! let mut values = HashMap::new();
//! values.insert("name", "brutus");
//! assert_eq!(template.render(&values).unwrap().as_str(), "hello brutus");
//! ```
//!
//! …or with your own type, if you implement the [`Values`] trait:
//!
//! ```
//! # use leon::Template;
//! use std::borrow::Cow;
//! use leon::Values;
//!
//! struct MyMap {
//! name: &'static str,
//! }
//! impl Values for MyMap {
//! fn get_value<'s, 'k: 's>(&'s self, key: &'k str) -> Option<Cow<'s, str>> {
//! if key == "name" {
//! Some(self.name.into())
//! } else {
//! None
//! }
//! }
//! }
//! #
//! # let template = Template::parse("hello {name}").unwrap();
//! let values = MyMap { name: "pontifex" };
//! assert_eq!(template.render(&values).unwrap().as_str(), "hello pontifex");
//! ```
//!
//! # Errors
//!
//! Leon will return a [`LeonError::InvalidTemplate`] if the template fails to
//! parse. This can happen if there are unbalanced braces, or if a key is empty.
//!
//! Leon will return a [`LeonError::MissingKey`] if a key is missing from keyed
//! values passed to [`Template::render()`], unless a default value is provided
//! with [`Template.default`].
//!
//! It will also pass through I/O errors when using [`Template::render_into()`].
#[doc(inline)]
pub use error::*;
#[doc(inline)]
pub use template::*;
#[doc(inline)]
pub use values::*;
mod error;
mod macros;
mod parser;
mod template;
mod values;