mirror of
https://github.com/Noratrieb/mono-fmt.git
synced 2026-01-14 15:25:08 +01:00
tests pass again
This commit is contained in:
parent
53e8bec4ec
commit
d7844568ca
5 changed files with 294 additions and 56 deletions
65
bacon.toml
Normal file
65
bacon.toml
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# This is a configuration file for the bacon tool
|
||||
# More info at https://github.com/Canop/bacon
|
||||
|
||||
default_job = "check"
|
||||
|
||||
[jobs]
|
||||
|
||||
[jobs.check]
|
||||
command = ["cargo", "check", "--color", "always"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.check-all]
|
||||
command = ["cargo", "check", "--all-targets", "--color", "always"]
|
||||
need_stdout = false
|
||||
watch = ["tests", "benches", "examples"]
|
||||
|
||||
[jobs.clippy]
|
||||
command = ["cargo", "clippy", "--color", "always"]
|
||||
need_stdout = false
|
||||
|
||||
[jobs.clippy-all]
|
||||
command = ["cargo", "clippy", "--all-targets", "--color", "always"]
|
||||
need_stdout = false
|
||||
watch = ["tests", "benches", "examples"]
|
||||
|
||||
[jobs.test]
|
||||
command = ["cargo", "test", "--color", "always"]
|
||||
need_stdout = true
|
||||
watch = ["tests"]
|
||||
|
||||
[jobs.doc]
|
||||
command = ["cargo", "doc", "--color", "always", "--no-deps"]
|
||||
need_stdout = false
|
||||
|
||||
# if the doc compiles, then it opens in your browser and bacon switches
|
||||
# to the previous job
|
||||
[jobs.doc-open]
|
||||
command = ["cargo", "doc", "--color", "always", "--no-deps", "--open"]
|
||||
need_stdout = false
|
||||
on_success = "back" # so that we don't open the browser at each change
|
||||
|
||||
# You can run your application and have the result displayed in bacon,
|
||||
# *if* it makes sense for this crate. You can run an example the same
|
||||
# way. Don't forget the `--color always` part or the errors won't be
|
||||
# properly parsed.
|
||||
[jobs.run]
|
||||
command = ["cargo", "run", "--color", "always"]
|
||||
need_stdout = true
|
||||
allow_warnings = true
|
||||
|
||||
[jobs.expand]
|
||||
command = ["cargo", "expand", "expand", "--color", "always"]
|
||||
need_stdout = true
|
||||
|
||||
# You may define here keybindings that would be specific to
|
||||
# a project, for example a shortcut to launch a specific job.
|
||||
# Shortcuts to internal functions (scrolling, toggling, etc.)
|
||||
# should go in your personal prefs.toml file instead.
|
||||
[keybindings]
|
||||
a = "job:check-all"
|
||||
i = "job:initial"
|
||||
c = "job:clippy"
|
||||
d = "job:doc-open"
|
||||
t = "job:test"
|
||||
r = "job:run"
|
||||
|
|
@ -1,29 +1,33 @@
|
|||
//! a bunch of this code is adapted from [stylish](https://github.com/Nullus157/stylish-rs)
|
||||
#![allow(dead_code, unreachable_code, unused_variables)]
|
||||
|
||||
use std::cell::Cell;
|
||||
|
||||
use format::Parse as _;
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use quote::{ToTokens, quote};
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
parse_macro_input, Expr, ExprAssign, ExprPath, Ident, LitStr, PathArguments, Result, Token,
|
||||
};
|
||||
use to_tokens::Scoped;
|
||||
|
||||
mod format;
|
||||
mod to_tokens;
|
||||
|
||||
struct Input {
|
||||
crate_ident: Ident,
|
||||
format_str: String,
|
||||
prefix: proc_macro2::TokenStream,
|
||||
format_str: LitStr,
|
||||
positional_args: Vec<Expr>,
|
||||
named_args: Vec<(Ident, Expr)>,
|
||||
}
|
||||
|
||||
impl Parse for Input {
|
||||
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||
let crate_ident = input.parse()?;
|
||||
let crate_ident = input.parse::<syn::Path>()?;
|
||||
let prefix = quote! { #crate_ident::_private };
|
||||
|
||||
let format_str = input.parse::<LitStr>()?.value();
|
||||
let format_str = input.parse::<LitStr>()?;
|
||||
|
||||
let mut positional_args = Vec::new();
|
||||
let mut named_args = Vec::new();
|
||||
|
|
@ -59,7 +63,7 @@ impl Parse for Input {
|
|||
}
|
||||
}
|
||||
Ok(Self {
|
||||
crate_ident,
|
||||
prefix,
|
||||
format_str,
|
||||
positional_args,
|
||||
named_args,
|
||||
|
|
@ -68,13 +72,12 @@ impl Parse for Input {
|
|||
}
|
||||
|
||||
fn format_args_impl(input: Input) -> syn::Result<TokenStream> {
|
||||
todo!();
|
||||
let (_, fmt_parts) = format::Format::parse(&input.format_str).unwrap();
|
||||
let str = input.format_str.value();
|
||||
let (_, fmt_parts) = format::Format::parse(&str).unwrap();
|
||||
|
||||
Ok(quote! {
|
||||
fmt_parts
|
||||
}
|
||||
.into())
|
||||
let current_position = Cell::new(0);
|
||||
|
||||
Ok(Scoped::new(&input, &fmt_parts, ¤t_position).to_token_stream().into())
|
||||
}
|
||||
|
||||
#[proc_macro]
|
||||
|
|
|
|||
|
|
@ -1,16 +1,28 @@
|
|||
use proc_macro2::TokenStream;
|
||||
use std::cell::Cell;
|
||||
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::{quote, ToTokens};
|
||||
|
||||
use crate::format::{Align, FormatTrait};
|
||||
use crate::{
|
||||
format::{
|
||||
Align, Count, Format, FormatArg, FormatArgRef, FormatTrait, FormatterArgs, Piece, Sign,
|
||||
},
|
||||
Input,
|
||||
};
|
||||
|
||||
pub struct Scoped<'a, T> {
|
||||
export: &'a syn::Path,
|
||||
pub(crate) struct Scoped<'a, T> {
|
||||
input: &'a Input,
|
||||
current_position: &'a Cell<usize>,
|
||||
inner: &'a T,
|
||||
}
|
||||
|
||||
impl<'a, T> Scoped<'a, T> {
|
||||
pub fn new(export: &'a syn::Path, inner: &'a T) -> Self {
|
||||
Self { export, inner }
|
||||
pub fn new(input: &'a Input, inner: &'a T, current_position: &'a Cell<usize>) -> Self {
|
||||
Self {
|
||||
input,
|
||||
inner,
|
||||
current_position,
|
||||
}
|
||||
}
|
||||
|
||||
fn scope<'b, U>(&self, inner: &'b U) -> Scoped<'b, U>
|
||||
|
|
@ -19,7 +31,8 @@ impl<'a, T> Scoped<'a, T> {
|
|||
{
|
||||
Scoped {
|
||||
inner,
|
||||
export: self.export,
|
||||
input: self.input,
|
||||
current_position: self.current_position,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -28,6 +41,164 @@ impl<'a, T> Scoped<'a, T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Scoped<'_, Format<'_>> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let parts = self.inner.pieces.iter().map(|piece| self.scope(piece));
|
||||
|
||||
tokens.extend(quote! {
|
||||
(
|
||||
#(#parts),*
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Scoped<'_, Piece<'_>> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let prefix = &self.input.prefix;
|
||||
|
||||
match self.inner {
|
||||
Piece::Lit(literal) => {
|
||||
let lit = syn::LitStr::new(literal, self.input.format_str.span());
|
||||
|
||||
tokens.extend(quote! { #prefix::Str(#lit) });
|
||||
}
|
||||
Piece::Arg(arg) => self.scope(arg).to_tokens(tokens),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToTokens for Scoped<'_, FormatArg<'_>> {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
let prefix = &self.input.prefix;
|
||||
|
||||
let base = self.inner.format_spec.format_trait;
|
||||
|
||||
let expr = match self.inner.arg {
|
||||
None => {
|
||||
let current_position = self.current_position.get();
|
||||
self.current_position.set(current_position + 1);
|
||||
|
||||
self.input.positional_args[current_position].to_token_stream()
|
||||
}
|
||||
Some(FormatArgRef::Positional(idx)) => {
|
||||
self.input.positional_args[idx].to_token_stream()
|
||||
}
|
||||
Some(FormatArgRef::Named(name)) => self
|
||||
.input
|
||||
.named_args
|
||||
.iter()
|
||||
.find(|(arg, _)| arg == name)
|
||||
.map(|(_, expr)| expr.to_token_stream())
|
||||
.unwrap_or_else(|| Ident::new(name, Span::call_site()).to_token_stream()),
|
||||
};
|
||||
|
||||
let opt_ty = opt_ty_tokens(self.scope(&self.inner.format_spec.formatter_args));
|
||||
let opt_values = opt_value_tokens(self.scope(&self.inner.format_spec.formatter_args));
|
||||
|
||||
|
||||
tokens.extend(quote! { #prefix::#base::<_, #opt_ty>(#expr, #opt_values) })
|
||||
}
|
||||
}
|
||||
|
||||
fn opt_value_tokens(scope: Scoped<'_, FormatterArgs<'_>>) -> TokenStream {
|
||||
let args = &scope.inner;
|
||||
let prefix = &scope.input.prefix;
|
||||
|
||||
let mut opts = quote! { () };
|
||||
|
||||
if args.alternate {
|
||||
opts = quote! { #prefix::WithAlternate(#opts) };
|
||||
}
|
||||
|
||||
if let Some(width) = args.width {
|
||||
let width = match width {
|
||||
Count::Integer(int) => int,
|
||||
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
|
||||
};
|
||||
opts = quote! { #prefix::WithWidth(#opts) };
|
||||
}
|
||||
|
||||
if let Some(align) = args.align {
|
||||
opts = quote! { #prefix::WithAlign(#opts) };
|
||||
}
|
||||
|
||||
if let Some(Sign::Plus) = args.sign {
|
||||
opts = quote! { #prefix::WithSignPlus(#opts) };
|
||||
}
|
||||
|
||||
if let Some(Sign::Minus) = args.sign {
|
||||
opts = quote! { #prefix::WithMinus(#opts)};
|
||||
}
|
||||
|
||||
if let Some(precision) = args.precision {
|
||||
let precision = match precision {
|
||||
Count::Integer(int) => int,
|
||||
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
|
||||
};
|
||||
opts = quote! { #prefix::WithPrecision(#opts) };
|
||||
}
|
||||
|
||||
if let Some(Sign::Plus) = args.sign {
|
||||
opts = quote! { #prefix::WithSignPlus(#opts) };
|
||||
}
|
||||
|
||||
if args.zero {
|
||||
opts = quote! { #prefix::WithSignAwareZeroPad(#opts) };
|
||||
}
|
||||
|
||||
opts
|
||||
}
|
||||
|
||||
fn opt_ty_tokens(scope: Scoped<'_, FormatterArgs<'_>>) -> TokenStream {
|
||||
let args = &scope.inner;
|
||||
let prefix = &scope.input.prefix;
|
||||
|
||||
let mut opts = quote! { () };
|
||||
|
||||
if args.alternate {
|
||||
opts = quote! { #prefix::WithAlternate<#opts> };
|
||||
}
|
||||
|
||||
if let Some(width) = args.width {
|
||||
let width = match width {
|
||||
Count::Integer(int) => int,
|
||||
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
|
||||
};
|
||||
opts = quote! { #prefix::WithWidth<#opts, #width> };
|
||||
}
|
||||
|
||||
if let Some(align) = args.align {
|
||||
opts = quote! { #prefix::WithAlign<#opts, #align> };
|
||||
}
|
||||
|
||||
if let Some(Sign::Plus) = args.sign {
|
||||
opts = quote! { #prefix::WithSignPlus<#opts> };
|
||||
}
|
||||
|
||||
if let Some(Sign::Minus) = args.sign {
|
||||
opts = quote! { #prefix::WithMinus<#opts> };
|
||||
}
|
||||
|
||||
if let Some(precision) = args.precision {
|
||||
let precision = match precision {
|
||||
Count::Integer(int) => int,
|
||||
Count::Parameter(_) => panic!("parameter counts are not supported right now"),
|
||||
};
|
||||
opts = quote! { #prefix::WithPrecision<#opts, #precision> };
|
||||
}
|
||||
|
||||
if let Some(Sign::Plus) = args.sign {
|
||||
opts = quote! { #prefix::WithSignPlus<#opts> };
|
||||
}
|
||||
|
||||
if args.zero {
|
||||
opts = quote! { #prefix::WithSignAwareZeroPad<#opts> };
|
||||
}
|
||||
|
||||
opts
|
||||
}
|
||||
|
||||
impl ToTokens for Align {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
tokens.extend(match self {
|
||||
|
|
@ -41,15 +212,15 @@ impl ToTokens for Align {
|
|||
impl ToTokens for FormatTrait {
|
||||
fn to_tokens(&self, tokens: &mut TokenStream) {
|
||||
match self {
|
||||
FormatTrait::Display => quote!(Display),
|
||||
FormatTrait::Debug => quote!(Debug),
|
||||
FormatTrait::Octal => quote!(Octal),
|
||||
FormatTrait::LowerHex => quote!(LowerHex),
|
||||
FormatTrait::UpperHex => quote!(UpperHex),
|
||||
FormatTrait::Pointer => quote!(Pointer),
|
||||
FormatTrait::Binary => quote!(Binary),
|
||||
FormatTrait::LowerExp => quote!(LowerExp),
|
||||
FormatTrait::UpperExp => quote!(UpperExp),
|
||||
FormatTrait::Display => quote! { DisplayArg },
|
||||
FormatTrait::Debug => quote! { DebugArg },
|
||||
FormatTrait::Octal => quote! { OctalArg },
|
||||
FormatTrait::LowerHex => quote! { LowerHexArg },
|
||||
FormatTrait::UpperHex => quote! { UpperHexArg },
|
||||
FormatTrait::Pointer => quote! { PointerArg },
|
||||
FormatTrait::Binary => quote! { BinaryArg },
|
||||
FormatTrait::LowerExp => quote! { LowerExpArg },
|
||||
FormatTrait::UpperExp => quote! { UpperExpArg },
|
||||
}
|
||||
.to_tokens(tokens)
|
||||
}
|
||||
|
|
|
|||
41
src/lib.rs
41
src/lib.rs
|
|
@ -80,7 +80,10 @@ impl<W: Write, O: FmtOpts> Formatter<W, O> {
|
|||
}
|
||||
|
||||
impl<W, O: FmtOpts> Formatter<W, O> {
|
||||
fn wrap_with<'opt, ONew: FmtOpts>(&mut self, opts: &ONew) -> Formatter<&mut W, ONew::ReplaceInnermost<O>> {
|
||||
fn wrap_with<'opt, ONew: FmtOpts>(
|
||||
&mut self,
|
||||
opts: &ONew,
|
||||
) -> Formatter<&mut W, ONew::ReplaceInnermost<O>> {
|
||||
Formatter {
|
||||
buf: &mut self.buf,
|
||||
opts: opts.override_other(self.opts),
|
||||
|
|
@ -88,15 +91,19 @@ impl<W, O: FmtOpts> Formatter<W, O> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn write<W: Write, A: Arguments>(buffer: W, args: A) -> Result {
|
||||
let mut fmt = Formatter::new(buffer);
|
||||
args.fmt(&mut fmt)
|
||||
}
|
||||
pub mod helpers {
|
||||
use crate::{Arguments, Formatter, Result, Write};
|
||||
|
||||
pub fn format<A: Arguments>(args: A) -> String {
|
||||
let mut string = String::new();
|
||||
write(&mut string, args).unwrap();
|
||||
string
|
||||
pub fn write<W: Write, A: Arguments>(buffer: W, args: A) -> Result {
|
||||
let mut fmt = Formatter::new(buffer);
|
||||
args.fmt(&mut fmt)
|
||||
}
|
||||
|
||||
pub fn format<A: Arguments>(args: A) -> String {
|
||||
let mut string = String::new();
|
||||
write(&mut string, args).unwrap();
|
||||
string
|
||||
}
|
||||
}
|
||||
|
||||
/// Not part of the public API.
|
||||
|
|
@ -116,7 +123,7 @@ mod _private {
|
|||
#[macro_export]
|
||||
macro_rules! format {
|
||||
($($tt:tt)*) => {
|
||||
$crate::format($crate::format_args!($($tt)*))
|
||||
$crate::helpers::format($crate::format_args!($($tt)*))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -154,17 +161,3 @@ mod tests {
|
|||
assert_eq!(result, "a: 32523532");
|
||||
}
|
||||
}
|
||||
|
||||
// testing
|
||||
fn fmt() {
|
||||
let a = (
|
||||
_private::Str("amount: "),
|
||||
_private::DebugArg::<_, _private::WithAlternate<()>>(5, _private::WithAlternate(())),
|
||||
);
|
||||
|
||||
let mut str = String::new();
|
||||
let mut f = Formatter::new(&mut str);
|
||||
Arguments::fmt(&a, &mut f).unwrap();
|
||||
|
||||
println!("{str}");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -154,7 +154,11 @@ impl<W: Write, O: FmtOpts> Formatter<W, O> {
|
|||
|
||||
// Writes the sign if it exists, and then the prefix if it was requested
|
||||
#[inline(never)]
|
||||
fn write_prefix<W: Write, O>(f: &mut Formatter<W, O>, sign: Option<char>, prefix: Option<&str>) -> Result {
|
||||
fn write_prefix<W: Write, O>(
|
||||
f: &mut Formatter<W, O>,
|
||||
sign: Option<char>,
|
||||
prefix: Option<&str>,
|
||||
) -> Result {
|
||||
if let Some(c) = sign {
|
||||
f.buf.write_char(c)?;
|
||||
}
|
||||
|
|
@ -183,14 +187,16 @@ impl<W: Write, O: FmtOpts> Formatter<W, O> {
|
|||
// is zero
|
||||
Some(min) if self.sign_aware_zero_pad() => {
|
||||
write_prefix(self, sign, prefix)?;
|
||||
let post_padding = self.padding(min - width, Alignment::Right, '0', Alignment::Right)?;
|
||||
let post_padding =
|
||||
self.padding(min - width, Alignment::Right, '0', Alignment::Right)?;
|
||||
self.buf.write_str(buf)?;
|
||||
post_padding.write(self)?;
|
||||
Ok(())
|
||||
}
|
||||
// Otherwise, the sign and prefix goes after the padding
|
||||
Some(min) => {
|
||||
let post_padding = self.padding(min - width, Alignment::Right, self.fill(), self.align())?;
|
||||
let post_padding =
|
||||
self.padding(min - width, Alignment::Right, self.fill(), self.align())?;
|
||||
write_prefix(self, sign, prefix)?;
|
||||
self.buf.write_str(buf)?;
|
||||
post_padding.write(self)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue