mirror of
https://github.com/cargo-bins/cargo-binstall.git
synced 2025-05-03 10:40:03 +00:00
Add new crate leon-macros
that provide template!
with identical syntax as runtime parsing (#946)
`leon_macros::template!` can parse template at compile-time. It accepts a utf-8 string literal and uses `leon` internally to parse it, then generate code that evaluates to `Template<'static>`. - Exclude fuzz from crate leon when publishing - Impl fn-like proc-macro `leon_macros::template!` - Add dep `leon-macros` to binstalk - Use `leon_macros::template!` in `binstalk::fetchers::gh_crate_meta::hosting` - Add doc for `leon-macros` in `leon` - Improve `std::fmt::Display` impl for `leon::ParseError` - Fixed broken infra link in leon Signed-off-by: Jiahao XU <Jiahao_XU@outlook.com>
This commit is contained in:
parent
fa0455a417
commit
5683ca2476
18 changed files with 373 additions and 91 deletions
|
@ -8,6 +8,7 @@ rust-version = "1.61.0"
|
|||
authors = ["Félix Saparelli <felix@passcod.name>"]
|
||||
edition = "2021"
|
||||
license = "Apache-2.0 OR MIT"
|
||||
exclude = ["fuzz"]
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.2.2", features = ["derive"], optional = true }
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#[derive(Debug, thiserror::Error)]
|
||||
use thiserror::Error as ThisError;
|
||||
|
||||
#[derive(Debug, ThisError)]
|
||||
#[cfg_attr(feature = "miette", derive(miette::Diagnostic))]
|
||||
pub enum RenderError {
|
||||
/// A key was missing from the provided values.
|
||||
#[error("missing key `{0}`")]
|
||||
MissingKey(String),
|
||||
|
||||
/// An I/O error passed through from [`Template::render_into`].
|
||||
/// An I/O error passed through from [`Template::render_into`](crate::Template::render_into).
|
||||
#[error("write failed: {0}")]
|
||||
Io(#[from] std::io::Error),
|
||||
}
|
||||
|
@ -15,71 +17,73 @@ pub enum RenderError {
|
|||
/// When the `miette` feature is enabled, this is a rich miette-powered error
|
||||
/// which will highlight the source of the error in the template when output
|
||||
/// (with miette's `fancy` feature). With `miette` disabled, this is opaque.
|
||||
#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, ThisError, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "miette", derive(miette::Diagnostic))]
|
||||
#[cfg_attr(feature = "miette", diagnostic(transparent))]
|
||||
#[error(transparent)]
|
||||
pub struct ParseError(Box<InnerParseError>);
|
||||
|
||||
/// The inner (unboxed) type of [`ParseError`].
|
||||
#[derive(Clone, Debug, thiserror::Error, PartialEq, Eq)]
|
||||
#[cfg_attr(feature = "miette", derive(miette::Diagnostic))]
|
||||
#[error("template parse failed")]
|
||||
#[derive(Clone, Debug, ThisError, PartialEq, Eq)]
|
||||
#[error("{kind} at span start = {offset}, len = {len}: {src}")]
|
||||
struct InnerParseError {
|
||||
#[cfg_attr(feature = "miette", source_code)]
|
||||
src: String,
|
||||
offset: usize,
|
||||
len: usize,
|
||||
kind: ErrorKind,
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "miette", label("This bracket is not opening or closing anything. Try removing it, or escaping it with a backslash."))]
|
||||
unbalanced: Option<(usize, usize)>,
|
||||
#[cfg(feature = "miette")]
|
||||
impl miette::Diagnostic for InnerParseError {
|
||||
fn source_code(&self) -> Option<&dyn miette::SourceCode> {
|
||||
Some(&self.src)
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "miette", label("This escape is malformed."))]
|
||||
escape: Option<(usize, usize)>,
|
||||
fn labels(&self) -> Option<Box<dyn Iterator<Item = miette::LabeledSpan> + '_>> {
|
||||
Some(Box::new(std::iter::once_with(|| {
|
||||
miette::LabeledSpan::new(Some(self.kind.to_string()), self.offset, self.len)
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(feature = "miette", label("A key cannot be empty."))]
|
||||
key_empty: Option<(usize, usize)>,
|
||||
#[derive(Clone, Debug, ThisError, PartialEq, Eq)]
|
||||
enum ErrorKind {
|
||||
#[error("This bracket is not opening or closing anything. Try removing it, or escaping it with a backslash.")]
|
||||
Unbalanced,
|
||||
|
||||
#[cfg_attr(feature = "miette", label("Escapes are not allowed in keys."))]
|
||||
key_escape: Option<(usize, usize)>,
|
||||
#[error("This escape is malformed.")]
|
||||
Escape,
|
||||
|
||||
#[error("A key cannot be empty.")]
|
||||
KeyEmpty,
|
||||
|
||||
#[error("Escapes are not allowed in keys.")]
|
||||
KeyEscape,
|
||||
}
|
||||
|
||||
impl ParseError {
|
||||
pub(crate) fn unbalanced(src: &str, start: usize, end: usize) -> Self {
|
||||
fn new(src: &str, start: usize, end: usize, kind: ErrorKind) -> Self {
|
||||
Self(Box::new(InnerParseError {
|
||||
src: String::from(src),
|
||||
unbalanced: Some((start, end.saturating_sub(start) + 1)),
|
||||
escape: None,
|
||||
key_empty: None,
|
||||
key_escape: None,
|
||||
offset: start,
|
||||
len: end.saturating_sub(start) + 1,
|
||||
kind,
|
||||
}))
|
||||
}
|
||||
|
||||
pub(crate) fn unbalanced(src: &str, start: usize, end: usize) -> Self {
|
||||
Self::new(src, start, end, ErrorKind::Unbalanced)
|
||||
}
|
||||
|
||||
pub(crate) fn escape(src: &str, start: usize, end: usize) -> Self {
|
||||
Self(Box::new(InnerParseError {
|
||||
src: String::from(src),
|
||||
unbalanced: None,
|
||||
escape: Some((start, end.saturating_sub(start) + 1)),
|
||||
key_empty: None,
|
||||
key_escape: None,
|
||||
}))
|
||||
Self::new(src, start, end, ErrorKind::Escape)
|
||||
}
|
||||
|
||||
pub(crate) fn key_empty(src: &str, start: usize, end: usize) -> Self {
|
||||
Self(Box::new(InnerParseError {
|
||||
src: String::from(src),
|
||||
unbalanced: None,
|
||||
escape: None,
|
||||
key_empty: Some((start, end.saturating_sub(start) + 1)),
|
||||
key_escape: None,
|
||||
}))
|
||||
Self::new(src, start, end, ErrorKind::KeyEmpty)
|
||||
}
|
||||
|
||||
pub(crate) fn key_escape(src: &str, start: usize, end: usize) -> Self {
|
||||
Self(Box::new(InnerParseError {
|
||||
src: String::from(src),
|
||||
unbalanced: None,
|
||||
escape: None,
|
||||
key_empty: None,
|
||||
key_escape: Some((start, end.saturating_sub(start) + 1)),
|
||||
}))
|
||||
Self::new(src, start, end, ErrorKind::KeyEscape)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -121,12 +121,21 @@
|
|||
//! assert_eq!(template.render(&values).unwrap().as_str(), "hello pontifex");
|
||||
//! ```
|
||||
//!
|
||||
//! # Compile-time parsing
|
||||
//!
|
||||
//! You can either use [`leon-macros`](https://docs.rs/leon-macros)'s
|
||||
//! [`template!`](https://docs.rs/leon-macros/latest/leon_macros/macro.template.html),
|
||||
//! a proc-macro, with the exact same syntax as the normal parser, or this
|
||||
//! crate's [`template!`] rules-macro, which requires a slightly different
|
||||
//! syntax but doesn't bring in additional dependencies. In either case,
|
||||
//! the leon library is required as a runtime dependency.
|
||||
//!
|
||||
//! # Errors
|
||||
//!
|
||||
//! Leon will return a [`LeonError::InvalidTemplate`] if the template fails to
|
||||
//! Leon will return a [`ParseError`] 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
|
||||
//! Leon will return a [`RenderError::MissingKey`] if a key is missing from keyed
|
||||
//! values passed to [`Template::render()`], unless a default value is provided
|
||||
//! with [`Template.default`].
|
||||
//!
|
||||
|
|
|
@ -29,7 +29,7 @@ macro_rules! __template_impl {
|
|||
}
|
||||
|
||||
/// Construct a template constant using syntax similar to the template to be
|
||||
/// passed to [`Template::parse`].
|
||||
/// passed to [`Template::parse`](crate::Template::parse).
|
||||
///
|
||||
/// This is essentially a shorthand for:
|
||||
///
|
||||
|
|
|
@ -47,7 +47,7 @@ impl<'s> Template<'s> {
|
|||
/// assert_eq!(TEMPLATE.render(&[("unrelated", "value")]).unwrap(), "Hello world");
|
||||
/// ```
|
||||
///
|
||||
/// For an even more ergonomic syntax, see the [`leon::template!`] macro.
|
||||
/// For an even more ergonomic syntax, see the [`leon::template!`](crate::template!) macro.
|
||||
pub const fn new(items: &'s [Item<'s>], default: Option<&'s str>) -> Template<'s> {
|
||||
Template {
|
||||
items: Cow::Borrowed(items),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue