use std::iter::repeat_n;
unsafe fn find_ws(s: &str, start: usize) -> usize {
unsafe { s.get_unchecked(start..) }
.find(char::is_whitespace)
.map(|index| index + start)
.unwrap_or(s.len())
}
unsafe fn find_non_ws(s: &str, start: usize) -> usize {
unsafe { s.get_unchecked(start..) }
.find(|c: char| !c.is_whitespace())
.map(|index| index + start)
.unwrap_or(s.len())
}
fn line_parts(line: &str) -> (&str, &str, &str) {
unsafe {
let loc_end = find_ws(line, 0);
let op_start = find_non_ws(line, loc_end);
let op_end = find_ws(line, op_start);
let after_op_start = find_non_ws(line, op_end);
(
line.get_unchecked(..loc_end),
line.get_unchecked(op_start..op_end),
line.get_unchecked(after_op_start..),
)
}
}
fn format_line(line: &str, dest: &mut String) {
debug_assert!(line.trim_end() == line);
debug_assert!(!line.is_empty());
let (loc, op, after_op) = line_parts(line);
dest.push_str(loc);
if op.is_empty() {
return;
}
if loc.len() < 10 {
dest.extend(repeat_n(' ', 11 - loc.len()));
} else {
dest.push(' ');
}
dest.push_str(op);
if after_op.is_empty() {
return;
}
if op.len() < 4 {
dest.extend(repeat_n(' ', 5 - op.len()));
} else {
dest.push(' ');
}
dest.push_str(after_op);
}
pub fn format_code(src: &str, dest: &mut String) {
let src = src.trim();
let mut last_empty = false;
for line in src.lines().map(str::trim_end) {
if line.is_empty() {
if !last_empty {
dest.push('\n');
last_empty = true;
}
} else if line.starts_with('*') {
dest.push_str(line);
dest.push('\n');
last_empty = false;
} else {
format_line(line, dest);
dest.push('\n');
last_empty = false;
}
}
}
pub fn format_code_to_string(src: &str) -> String {
let mut dest = String::new();
format_code(src, &mut dest);
dest
}
#[cfg(test)]
mod tests {
use super::*;
const UNFORMATTED: &str = concat!(
"\n\n",
"* COMMENT LINE \n",
"A \n",
"A EQU \n",
"ABCDEFGHJK EQU \n",
"ABCDEFGHJKL EQU \n",
"ABCDEFGHJKL EQU AFTER \n",
"ABCDEFGHJKL ORIG AFTER \n",
"ABCDEFGHJKL ABCDE AFTER \n",
"\n\n",
"* COMMENT LINE\n",
"\n\n",
);
const FORMATTED: &str = concat!(
"* COMMENT LINE\n",
"A\n",
"A EQU\n",
"ABCDEFGHJK EQU\n",
"ABCDEFGHJKL EQU\n",
"ABCDEFGHJKL EQU AFTER\n",
"ABCDEFGHJKL ORIG AFTER\n",
"ABCDEFGHJKL ABCDE AFTER\n",
"\n",
"* COMMENT LINE\n",
);
#[test]
fn format_code_follows_style() {
assert_eq!(format_code_to_string(UNFORMATTED), FORMATTED);
}
}