1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
use std::str::Chars;
use peekmore::{PeekMore, PeekMoreIterator};
use smallvec::SmallVec;
/// Formats a type description string to have nice indents.
pub fn format_type_description(input: &str) -> String {
/// Big scope means we want to spread out items over multiple lines.
/// Small scope means, we want to keep it compact (on one line).
#[derive(Debug, Clone, PartialEq)]
enum Scope {
Big,
Small,
}
const SMALL_SCOPE_MAX_TOKENS: usize = 32;
/// should be called on the chars iterator shortly after open_token was encountered.
///
/// scope should never be considered small if any curly braces are encountered inbetween.
fn scope_is_small(
chars: &mut PeekMoreIterator<Chars>,
open_token: char,
close_token: char,
) -> bool {
// 1 because starting assuming bracket is open
let mut open_close_balance = 1;
for ch in chars.peek_amount(SMALL_SCOPE_MAX_TOKENS) {
let Some(ch) = ch else {
break;
};
if *ch == open_token {
open_close_balance += 1;
}
if *ch == close_token {
open_close_balance -= 1;
if open_close_balance == 0 {
return true;
}
}
if *ch == '{' {
return false;
}
}
false
}
fn add_indentation(output: &mut String, indent_level: i32) {
for _ in 0..indent_level {
output.push_str(" ");
}
}
let mut output = String::new();
let mut indent_level: i32 = 0;
let mut tuple_level: SmallVec<[Scope; 8]> = SmallVec::new();
let mut angle_level: SmallVec<[Scope; 8]> = SmallVec::new();
let mut chars_peekable = input.chars().peekmore();
while let Some(ch) = chars_peekable.next() {
match ch {
'{' => {
indent_level += 1;
output.push(' ');
output.push(ch);
output.push('\n');
add_indentation(&mut output, indent_level);
}
'}' => {
indent_level -= 1;
output.push('\n');
add_indentation(&mut output, indent_level);
output.push(ch);
}
',' => {
output.push(ch);
if tuple_level.last() == Some(&Scope::Small) {
output.push(' ');
} else {
output.push('\n');
add_indentation(&mut output, indent_level);
}
}
'(' => {
output.push(ch);
if scope_is_small(&mut chars_peekable, '(', ')') {
tuple_level.push(Scope::Small)
} else {
tuple_level.push(Scope::Big);
indent_level += 1;
output.push('\n');
add_indentation(&mut output, indent_level);
}
}
')' => {
if let Some(Scope::Big) = tuple_level.pop() {
indent_level -= 1;
output.push('\n');
add_indentation(&mut output, indent_level);
}
output.push(ch);
}
'<' => {
output.push(ch);
if scope_is_small(&mut chars_peekable, '<', '>') {
angle_level.push(Scope::Small)
} else {
angle_level.push(Scope::Big);
indent_level += 1;
output.push('\n');
add_indentation(&mut output, indent_level);
}
}
'>' => {
if let Some(Scope::Big) = angle_level.pop() {
indent_level -= 1;
output.push('\n');
add_indentation(&mut output, indent_level);
}
output.push(ch);
}
_ => {
output.push(ch);
}
}
}
output
}