From 86441cf10bd70f6b377574d89ffc60a7a7069bde Mon Sep 17 00:00:00 2001 From: Nilstrieb <48135649+Nilstrieb@users.noreply.github.com> Date: Sun, 11 Sep 2022 22:21:29 +0200 Subject: [PATCH] wow it works --- mono-fmt-macro/Cargo.toml | 3 +- mono-fmt-macro/src/lib.rs | 110 +++++++++++++++++++++++++++++++++++--- src/lib.rs | 7 ++- 3 files changed, 109 insertions(+), 11 deletions(-) diff --git a/mono-fmt-macro/Cargo.toml b/mono-fmt-macro/Cargo.toml index 09fd01c..41083c8 100644 --- a/mono-fmt-macro/Cargo.toml +++ b/mono-fmt-macro/Cargo.toml @@ -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"] } diff --git a/mono-fmt-macro/src/lib.rs b/mono-fmt-macro/src/lib.rs index aedb30f..18c5862 100644 --- a/mono-fmt-macro/src/lib.rs +++ b/mono-fmt-macro/src/lib.rs @@ -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, + exprs: Punctuated, } impl Parse for Input { fn parse(input: ParseStream) -> syn::Result { let first = input.parse::()?; + + let mut exprs = Punctuated::new(); + + if !input.is_empty() { + let _ = input.parse::(); + } + + 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>, + exprs: I, + fmt_parts: Vec, +} + +impl<'a, I> Formatter<'a, I> +where + I: Iterator, +{ + fn parse(mut self) -> Vec { + 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() } diff --git a/src/lib.rs b/src/lib.rs index 35dad59..e4a7af9 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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"); } }