Skip to content

Commit

Permalink
split out pretty.rs
Browse files Browse the repository at this point in the history
  • Loading branch information
sourcefrog committed Sep 15, 2023
1 parent 6355b39 commit f05a94f
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 64 deletions.
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ mod options;
mod outcome;
mod output;
mod path;
mod pretty;
mod process;
mod scenario;
mod source;
Expand Down
69 changes: 69 additions & 0 deletions src/pretty.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2021-2023 Martin Pool

//! Convert a token stream back to (reasonably) pretty Rust code in a string.
use proc_macro2::{Delimiter, TokenTree};
use quote::ToTokens;

/// Convert a TokenStream representing some code to a reasonably formatted
/// string of Rust code.
///
/// [TokenStream] has a `to_string`, but it adds spaces in places that don't
/// look idiomatic, so this reimplements it in a way that looks better.
///
/// This is probably not correctly formatted for all Rust syntax, and only tries
/// to cover cases that can emerge from the code we generate.
pub(crate) fn tokens_to_pretty_string<T: ToTokens>(t: T) -> String {
use TokenTree::*;
let mut b = String::with_capacity(200);
let mut ts = t.to_token_stream().into_iter().peekable();
while let Some(tt) = ts.next() {
match tt {
Punct(p) => {
let pc = p.as_char();
b.push(pc);
if ts.peek().is_some() && (b.ends_with("->") || pc == ',' || pc == ';') {
b.push(' ');
}
}
Ident(_) | Literal(_) => {
match tt {
Literal(l) => b.push_str(&l.to_string()),
Ident(i) => b.push_str(&i.to_string()),
_ => unreachable!(),
};
if let Some(next) = ts.peek() {
match next {
Ident(_) | Literal(_) => b.push(' '),
Punct(p) => match p.as_char() {
',' | ';' | '<' | '>' | ':' | '.' | '!' => (),
_ => b.push(' '),
},
Group(_) => (),
}
}
}
Group(g) => {
match g.delimiter() {
Delimiter::Brace => b.push('{'),
Delimiter::Bracket => b.push('['),
Delimiter::Parenthesis => b.push('('),
Delimiter::None => (),
}
b.push_str(&tokens_to_pretty_string(g.stream()));
match g.delimiter() {
Delimiter::Brace => b.push('}'),
Delimiter::Bracket => b.push(']'),
Delimiter::Parenthesis => b.push(')'),
Delimiter::None => (),
}
}
}
}
debug_assert!(
!b.ends_with(' '),
"generated a trailing space: ts={ts:?}, b={b:?}",
ts = t.to_token_stream(),
);
b
}
66 changes: 2 additions & 64 deletions src/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use std::sync::Arc;

use anyhow::Context;
use itertools::Itertools;
use proc_macro2::{Delimiter, TokenStream, TokenTree};
use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::ext::IdentExt;
use syn::visit::Visit;
Expand All @@ -23,6 +23,7 @@ use syn::{
};
use tracing::{debug, debug_span, trace, trace_span, warn};

use crate::pretty::tokens_to_pretty_string;
use crate::source::SourceFile;
use crate::*;

Expand Down Expand Up @@ -625,69 +626,6 @@ fn path_is_nonzero_unsigned(path: &Path) -> bool {
}
}

/// Convert a TokenStream representing some code to a reasonably formatted
/// string of Rust code.
///
/// [TokenStream] has a `to_string`, but it adds spaces in places that don't
/// look idiomatic, so this reimplements it in a way that looks better.
///
/// This is probably not correctly formatted for all Rust syntax, and only tries
/// to cover cases that can emerge from the code we generate.
fn tokens_to_pretty_string<T: ToTokens>(t: T) -> String {
use TokenTree::*;
let mut b = String::with_capacity(200);
let mut ts = t.to_token_stream().into_iter().peekable();
while let Some(tt) = ts.next() {
match tt {
Punct(p) => {
let pc = p.as_char();
b.push(pc);
if ts.peek().is_some() && (b.ends_with("->") || pc == ',' || pc == ';') {
b.push(' ');
}
}
Ident(_) | Literal(_) => {
match tt {
Literal(l) => b.push_str(&l.to_string()),
Ident(i) => b.push_str(&i.to_string()),
_ => unreachable!(),
};
if let Some(next) = ts.peek() {
match next {
Ident(_) | Literal(_) => b.push(' '),
Punct(p) => match p.as_char() {
',' | ';' | '<' | '>' | ':' | '.' | '!' => (),
_ => b.push(' '),
},
Group(_) => (),
}
}
}
Group(g) => {
match g.delimiter() {
Delimiter::Brace => b.push('{'),
Delimiter::Bracket => b.push('['),
Delimiter::Parenthesis => b.push('('),
Delimiter::None => (),
}
b.push_str(&tokens_to_pretty_string(g.stream()));
match g.delimiter() {
Delimiter::Brace => b.push('}'),
Delimiter::Bracket => b.push(']'),
Delimiter::Parenthesis => b.push(')'),
Delimiter::None => (),
}
}
}
}
debug_assert!(
!b.ends_with(' '),
"generated a trailing space: ts={ts:?}, b={b:?}",
ts = t.to_token_stream(),
);
b
}

/// If this looks like `Result<T, E>` (optionally with `Result` in some module), return `T`.
fn result_ok_type(path: &Path) -> Option<&Type> {
match_first_type_arg(path, "Result")
Expand Down

0 comments on commit f05a94f

Please sign in to comment.