git.delta.rocks / jrsonnet / refs/commits / 218d8ccde140

difftreelog

perf cheaper error formatting where possible

Yaroslav Bolyukin2023-08-13parent: #6d08ea9.patch.diff
in: master

3 files changed

modifiedcrates/jrsonnet-evaluator/src/error.rsdiffbeforeafterboth
378 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}
384384
385#[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}
391391
modifiedcrates/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::*;
modifiedcrates/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()
+}