use std::fmt::Write;
use crate::ser::Result;
pub fn first_line_leading_spaces(s: &str) -> usize {
for line in s.split('\n') {
if !line.is_empty() {
return line.len() - line.trim_start_matches(' ').len();
}
}
0
}
pub fn write_folded_block<W: Write>(
out: &mut W,
s: &str,
indent: usize,
indent_step: usize,
folded_wrap_col: usize,
) -> Result<()> {
let mut indent_buf: String = String::new();
let spaces = indent_step * indent;
if spaces > 0 {
indent_buf.reserve(spaces);
for _ in 0..spaces {
indent_buf.push(' ');
}
}
let indent_str = indent_buf.as_str();
for line in s.split('\n') {
if line.is_empty() {
out.write_str(indent_str)?;
out.write_char('\n')?;
continue;
}
if line.starts_with(' ') {
out.write_str(indent_str)?;
out.write_str(line)?;
out.write_char('\n')?;
continue;
}
let mut start = 0usize; let mut col = 0usize; let mut last_space_run: Option<(usize, usize, usize)> = None;
let mut in_space_run = false;
let mut run_start = 0usize;
let mut run_len = 0usize;
let mut prev_i = 0usize;
let mut prev_ch_len = 0usize;
for (i, ch) in line.char_indices() {
if in_space_run && ch != ' ' {
let run_end = prev_i + prev_ch_len;
last_space_run = Some((run_start, run_end, run_len));
in_space_run = false;
run_len = 0;
}
if ch == ' ' {
if !in_space_run {
in_space_run = true;
run_start = i;
run_len = 1;
} else {
run_len += 1;
}
}
col += 1;
if col > folded_wrap_col {
let Some((ws_start, ws_end, ws_len)) = last_space_run else {
break;
};
out.write_str(indent_str)?;
out.write_str(&line[start..ws_start])?;
if ws_len > 1 {
for _ in 0..(ws_len - 1) {
out.write_char(' ')?;
}
}
out.write_char('\n')?;
start = ws_end;
col = 0;
last_space_run = None;
}
prev_i = i;
prev_ch_len = ch.len_utf8();
}
out.write_str(indent_str)?;
out.write_str(&line[start..])?;
out.write_char('\n')?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_line_leading_spaces_no_spaces() {
assert_eq!(first_line_leading_spaces("hello"), 0);
assert_eq!(first_line_leading_spaces("hello\nworld"), 0);
}
#[test]
fn first_line_leading_spaces_with_spaces() {
assert_eq!(first_line_leading_spaces(" hello"), 2);
assert_eq!(first_line_leading_spaces(" hello\nworld"), 3);
}
#[test]
fn first_line_leading_spaces_empty_first_line() {
assert_eq!(first_line_leading_spaces("\n hello"), 2);
assert_eq!(first_line_leading_spaces("\n\n world"), 3);
}
#[test]
fn first_line_leading_spaces_all_empty() {
assert_eq!(first_line_leading_spaces(""), 0);
assert_eq!(first_line_leading_spaces("\n\n"), 0);
}
#[test]
fn write_folded_block_simple() {
let mut out = String::new();
write_folded_block(&mut out, "hello world", 1, 2, 80).unwrap();
assert_eq!(out, " hello world\n");
}
#[test]
fn write_folded_block_wraps_at_space() {
let mut out = String::new();
write_folded_block(&mut out, "hello world", 1, 2, 8).unwrap();
assert_eq!(out, " hello\n world\n");
}
#[test]
fn write_folded_block_preserves_empty_lines() {
let mut out = String::new();
write_folded_block(&mut out, "para1\n\npara2", 1, 2, 80).unwrap();
assert_eq!(out, " para1\n \n para2\n");
}
#[test]
fn write_folded_block_preserves_leading_spaces() {
let mut out = String::new();
write_folded_block(&mut out, " indented line", 1, 2, 80).unwrap();
assert_eq!(out, " indented line\n");
}
#[test]
fn write_folded_block_multi_space_run() {
let mut out = String::new();
write_folded_block(&mut out, "AA BB", 0, 2, 4).unwrap();
assert_eq!(out, "AA \nBB\n");
}
}