Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,26 @@ mk_macros! { @with_dollar![$]=>
writeln_f
=> writeln!(stream, ...)
,
#[doc = "Shorthand for [`error!(format_f!`]."]
error_f
=> debug!(...)
,
#[doc = "Shorthand for [`warn!(format_f!`]."]
warn_f
=> debug!(...)
,
#[doc = "Shorthand for [`info!(format_f!`]."]
info_f
=> debug!(...)
,
#[doc = "Shorthand for [`debug!(format_f!`]."]
debug_f
=> debug!(...)
,
#[doc = "Shorthand for [`trace!(format_f!`]."]
trace_f
=> debug!(...)
,
}

/// Like [`format_args!`](
Expand Down
1 change: 1 addition & 0 deletions src/proc_macro/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ proc-macro2 = "0.4.30"
proc-macro-hack = "0.5.8"
proc-quote = "0.2.2"
syn = { version = "0.15.42", features = ["full", ]}
regex = "1.3.1"

[features]
verbose-expansions = ["syn/extra-traits", ]
123 changes: 89 additions & 34 deletions src/proc_macro/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ use ::syn::{*,
punctuated::Punctuated,
};
use ::std::ops::Not;
use regex::Regex;

#[macro_use]
mod macros;
Expand Down Expand Up @@ -85,52 +86,106 @@ fn format_args_f (input: TokenStream) -> TokenStream
{
#[allow(unused)]
const FUNCTION_NAME: &str = "format_args_f";
let mut expr_cnt = 0;

debug_input!(&input);

let Args {
format_literal,
mut format_literal,
mut extra_args,
} = parse_macro_input!(input);
let s = format_literal.value();
let mut iterator = s.chars().peekable();
let mut curly_bracket_count = 0;
let mut frmt = String::new();
let mut item = String::new();

let re_fmt = Regex::new(r":([xX]?\?|[oxXpbeE])?(\d+)?$").unwrap();
// identify any trailing formatting traits
// see: https://doc.rust-lang.org/std/fmt/#formatting-traits
// ? ⇒ Debug
// x? ⇒ Debug with lower-case hexadecimal integers
// X? ⇒ Debug with upper-case hexadecimal integers
// o ⇒ Octal
// x ⇒ LowerHex
// X ⇒ UpperHex
// p ⇒ Pointer
// b ⇒ Binary
// e ⇒ LowerExp
// E ⇒ UpperExp

let mut iterator = s.char_indices().peekable();
while let Some((i, c)) = iterator.next() {
frmt.push('"');
while let Some(c) = iterator.next() {
if c != '{' {
frmt.push(c);
continue;
}
// encountered `{`, let's see if it was `{{`
if let Some(&(_, '{')) = iterator.peek() {
let _ = iterator.next();
continue;
}
let s = &s[i + 1 ..];
let end =
s .chars()
.position(|c| c == '}' || c == ':')
.expect(concat!(
"Invalid format string literal\n",
"note: if you intended to print `{`, ",
"you can escape it using `{{`",
))
;
let arg = s[.. end].trim();
let ident = match parse_str::<Ident>(arg) {
| Ok(ident) => ident,
| Err(_) => continue,
};
// if `ident = ...` is not yet among the extra args
if extra_args
.iter()
.all(|arg| Some(&ident) != arg.ident.as_ref())
{
extra_args.push(FmtArg {
expr: parse_quote!(#ident),
ident: Some(ident),
});
} else {
// encountered `{`, let's see if it was `{{`
if let Some(&'{') = iterator.peek() {
let _ = iterator.next();
frmt.push('{');
continue;
}
curly_bracket_count += 1;
while let Some(c) = iterator.next() {
if c == '{' {
curly_bracket_count += 1;
item.push(c);
continue;
} else if c == '}' {
curly_bracket_count -= 1;
if curly_bracket_count == 0 {
let s = item.as_str();
let (mut arg, fmt) =
if let Some(fmt_match) = re_fmt.find(s)
{
(s[.. fmt_match.start()].trim(),
Some(fmt_match.as_str()))
} else {
(s.trim(),
None)
};
let trailing_eq = arg.ends_with('=');
if trailing_eq {
arg = &arg[.. arg.len() - 1];
}
let exp = match parse_str::<Expr>(arg) {
| Ok(expr) => expr,
| Err(_) => continue,
};
let id = format!("expr_{}_", expr_cnt);
expr_cnt += 1;
extra_args.push(FmtArg {
expr: parse_quote!(#exp),
ident: Some(parse_str::<Ident>(&id)
.unwrap())
});
if trailing_eq {
frmt.push_str(arg);
frmt.push('=');
}
frmt.push('{');
frmt.push_str(id.as_str());
if let Some(m) = fmt {
frmt.push_str(m);
}
frmt.push('}');
item.clear();
break;
} else {
item.push(c);
continue;
}
} else {
item.push(c);
continue;
}
}
}
}

frmt.push('"');
format_literal = parse_str::<LitStr>(frmt.as_str()).unwrap();

TokenStream::from(debug_output!(quote! {
format_args!(#format_literal #(, #extra_args)*)
}))
Expand Down