difftreelog
perf cheaper error formatting where possible
in: master
3 files changed
crates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth378 return Err($w$(::$i)*$({$($tt)*})?.into())378 return Err($w$(::$i)*$({$($tt)*})?.into())379 };379 };380 ($l:literal$(, $($tt:tt)*)?) => {380 ($l:literal$(, $($tt:tt)*)?) => {381 return Err($crate::error::ErrorKind::RuntimeError(format!($l$(, $($tt)*)?).into()).into())381 return Err($crate::error::ErrorKind::RuntimeError($crate::jrsonnet_macros::format_istr!($l$(, $($tt)*)?)).into())382 };382 };383}383}384384385#[macro_export]385#[macro_export]386macro_rules! runtime_error {386macro_rules! runtime_error {387 ($l:literal$(, $($tt:tt)*)?) => {387 ($l:literal$(, $($tt:tt)*)?) => {388 $crate::error::Error::from($crate::error::ErrorKind::RuntimeError(format!($l$(, $($tt)*)?).into()))388 $crate::error::Error::from($crate::error::ErrorKind::RuntimeError($crate::jrsonnet_macros::format_istr!($l$(, $($tt)*)?)))389 };389 };390}390}391391crates/jrsonnet-evaluator/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-evaluator/src/lib.rs
+++ b/crates/jrsonnet-evaluator/src/lib.rs
@@ -84,6 +84,8 @@
pub use import::*;
use jrsonnet_gcmodule::{Cc, Trace};
pub use jrsonnet_interner::{IBytes, IStr};
+#[doc(hidden)]
+pub use jrsonnet_macros;
pub use jrsonnet_parser as parser;
use jrsonnet_parser::*;
pub use obj::*;
crates/jrsonnet-macros/src/lib.rsdiffbeforeafterboth--- a/crates/jrsonnet-macros/src/lib.rs
+++ b/crates/jrsonnet-macros/src/lib.rs
@@ -7,7 +7,7 @@
punctuated::Punctuated,
spanned::Spanned,
token::{self, Comma},
- Attribute, DeriveInput, Error, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,
+ Attribute, DeriveInput, Error, Expr, FnArg, GenericArgument, Ident, ItemFn, LitStr, Pat, Path,
PathArguments, Result, ReturnType, Token, Type,
};
@@ -677,3 +677,102 @@
};
})
}
+
+struct FormatInput {
+ formatting: LitStr,
+ arguments: Vec<Expr>,
+}
+impl Parse for FormatInput {
+ fn parse(input: ParseStream) -> Result<Self> {
+ let formatting = input.parse()?;
+ let mut arguments = Vec::new();
+
+ while input.peek(Token![,]) {
+ input.parse::<Token![,]>()?;
+ if input.is_empty() {
+ // Trailing comma
+ break;
+ }
+ let expr = input.parse()?;
+ arguments.push(expr);
+ }
+
+ if !input.is_empty() {
+ return Err(syn::Error::new(input.span(), "unexpected trailing input"));
+ }
+
+ Ok(Self {
+ formatting,
+ arguments,
+ })
+ }
+}
+fn is_format_str(i: &str) -> bool {
+ let mut is_plain = true;
+ // -1 = {
+ // +1 = }
+ let mut is_bracket = 0i8;
+ for ele in i.chars() {
+ match ele {
+ '{' if is_bracket == -1 => {
+ is_bracket = 0;
+ }
+ '}' if is_bracket == -1 => {
+ is_plain = false;
+ break;
+ }
+ '}' if is_bracket == 1 => {
+ is_bracket = 0;
+ }
+ '{' if is_bracket == 1 => {
+ is_plain = false;
+ break;
+ }
+ '{' => {
+ is_bracket = -1;
+ }
+ '}' => {
+ is_bracket = 1;
+ }
+ _ if is_bracket != 0 => {
+ is_plain = false;
+ break;
+ }
+ _ => {}
+ }
+ }
+ !is_plain || is_bracket != 0
+}
+impl FormatInput {
+ fn expand(self) -> TokenStream {
+ let format = self.formatting;
+ if is_format_str(&format.value()) {
+ let args = self.arguments;
+ quote! {
+ ::jrsonnet_evaluator::IStr::from(format!(#format #(, #args)*))
+ }
+ } else {
+ if let Some(first) = self.arguments.first() {
+ return syn::Error::new(
+ first.span(),
+ "string has no formatting codes, it should not have the arguments",
+ )
+ .into_compile_error();
+ }
+ quote! {
+ ::jrsonnet_evaluator::IStr::from(#format)
+ }
+ }
+ }
+}
+
+/// IStr formatting helper
+///
+/// Using `format!("literal with no codes").into()` is slower than just `"literal with no codes".into()`
+/// This macro looks for formatting codes in the input string, and uses
+/// `format!()` only when necessary
+#[proc_macro]
+pub fn format_istr(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
+ let input = parse_macro_input!(input as FormatInput);
+ input.expand().into()
+}