mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-05-16 08:50:02 +00:00
Leon template library (#766)
* 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:
parent
daf8cdd010
commit
2227d363f7
20 changed files with 2382 additions and 162 deletions
crates/leon/src
148
crates/leon/src/lib.rs
Normal file
148
crates/leon/src/lib.rs
Normal 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;
|
Loading…
Add table
Add a link
Reference in a new issue