wow it works

This commit is contained in:
nora 2022-09-11 22:21:29 +02:00
parent d43f4577e6
commit 86441cf10b
3 changed files with 109 additions and 11 deletions

View file

@ -9,5 +9,6 @@ proc-macro = true
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
proc-macro2 = "1.0.43"
quote = "1.0.21"
syn = "1.0.99"
syn = { version = "1.0.99", features = ["full"] }

View file

@ -1,31 +1,129 @@
use std::{iter::Peekable, str::Chars};
use proc_macro::TokenStream;
use quote::quote;
use proc_macro2::Span;
use quote::{quote, ToTokens};
use syn::{
parse::{Parse, ParseStream},
Expr, parse_macro_input,
parse_macro_input,
punctuated::Punctuated,
Expr, LitStr, Token,
};
struct Input {
format_str: String,
items: Vec<Expr>,
exprs: Punctuated<Expr, Token![,]>,
}
impl Parse for Input {
fn parse(input: ParseStream) -> syn::Result<Self> {
let first = input.parse::<syn::LitStr>()?;
let mut exprs = Punctuated::new();
if !input.is_empty() {
let _ = input.parse::<Token![,]>();
}
while !input.is_empty() {
let punct = input.parse()?;
exprs.push(punct);
if input.is_empty() {
break;
}
let value = input.parse()?;
exprs.push(value);
}
Ok(Self {
format_str: first.value(),
items: Vec::new(),
exprs,
})
}
}
enum FmtPart {
Literal(String),
Debug(Expr),
Display(Expr),
}
struct Formatter<'a, I> {
string: Peekable<Chars<'a>>,
exprs: I,
fmt_parts: Vec<FmtPart>,
}
impl<'a, I> Formatter<'a, I>
where
I: Iterator<Item = Expr>,
{
fn parse(mut self) -> Vec<FmtPart> {
let mut next_string = String::new();
while let Some(char) = self.string.next() {
match char {
'{' => {
self.save_string(std::mem::take(&mut next_string));
if self.string.next() != Some('}') {
panic!("only supports display formatting!");
}
let expr = self
.exprs
.next()
.expect("missing argument for display formatting");
self.fmt_parts.push(FmtPart::Display(expr));
}
other => {
next_string.push(other);
}
}
}
self.save_string(next_string);
self.fmt_parts
}
fn save_string(&mut self, string: String) {
if string.is_empty() {
return;
}
self.fmt_parts.push(FmtPart::Literal(string));
}
}
impl ToTokens for FmtPart {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let own_tokens = match self {
FmtPart::Literal(lit) => {
let literal = LitStr::new(lit, Span::call_site());
quote! { mono_fmt::_private::Str(#literal) }
}
FmtPart::Display(expr) => {
quote! { mono_fmt::_private::DisplayArg(#expr) }
}
FmtPart::Debug(expr) => {
quote! { mono_fmt::_private::DebugArg(#expr) }
}
};
tokens.extend(own_tokens);
}
}
#[proc_macro]
pub fn format_args(tokens: TokenStream) -> TokenStream {
let input = parse_macro_input!(tokens as Input);
let str = input.format_str;
let formatter = Formatter {
string: input.format_str.chars().peekable(),
exprs: input.exprs.into_iter(),
fmt_parts: Vec::new(),
};
let fmt_parts = formatter.parse();
quote! {
(mono_fmt::_private::Str(#str),)
(#(#fmt_parts),*,)
}
.into()
}

View file

@ -139,7 +139,6 @@ mod tests {
// for the macros
use crate as mono_fmt;
use crate::arguments::DebugArg;
use crate::format;
#[test]
@ -149,8 +148,8 @@ mod tests {
}
#[test]
fn debug() {
let result = format((DebugArg("uwu"),));
assert_eq!(result, "\"uwu\"");
fn display() {
let result = format!("{}", "uwu");
assert_eq!(result, "uwu");
}
}