use std::borrow::Cow;
const PREFIX_CACHE: &str = " ";
#[cfg(feature = "color")]
mod with_color {
use crate::core::OutputStyle;
#[derive(Debug, Default, Clone)]
pub struct OutputSegment {
pub buf: String,
pub style: OutputStyle,
}
pub fn indent_segments(
segments: Vec<OutputSegment>,
prefix: &str,
hanging: bool,
) -> Vec<OutputSegment> {
let mut new_segments = Vec::with_capacity(segments.len() * 2);
if !hanging {
new_segments.push(OutputSegment {
buf: prefix.to_string(),
style: OutputStyle::default(),
});
}
let non_empty_segments = segments
.into_iter()
.filter(|segment| !segment.buf.is_empty())
.collect::<Vec<_>>();
for (segment_index, segment) in non_empty_segments.iter().enumerate() {
if segment.buf.is_empty() {
continue;
}
let is_last_segment = segment_index == non_empty_segments.len() - 1;
let segment_ends_in_newline =
matches!(segment.buf.chars().next_back(), Some(last_char) if last_char == '\n');
let lines = segment.buf.lines().collect::<Vec<_>>();
let num_lines = lines.len();
for (line_index, line) in lines.into_iter().enumerate() {
let is_last_line_in_segment = line_index == num_lines - 1;
let needs_newline = !is_last_line_in_segment || segment_ends_in_newline;
let mut owned_line = line.to_owned();
if needs_newline {
owned_line.push('\n');
}
new_segments.push(OutputSegment {
buf: owned_line,
style: segment.style.clone(),
});
if (!is_last_segment || !is_last_line_in_segment) && needs_newline {
new_segments.push(OutputSegment {
buf: prefix.to_owned(),
style: OutputStyle::default(),
});
}
}
}
new_segments.shrink_to_fit();
new_segments
}
}
#[cfg(feature = "color")]
pub use with_color::*;
#[cfg(feature = "fmt")]
mod with_fmt {
use std::borrow::Cow;
pub fn int_len(n: usize, base: u32) -> u32 {
let mut power = base as usize;
let mut count = 1;
while n >= power {
count += 1;
if let Some(new_power) = power.checked_mul(base as usize) {
power = new_power;
} else {
break;
}
}
count
}
pub fn pad_int(n: usize, longest: usize, base: u32) -> Cow<'static, str> {
super::whitespace((int_len(longest, base) - int_len(n, base)) as usize)
}
}
#[cfg(feature = "fmt")]
pub use with_fmt::*;
#[cfg(any(not(feature = "color"), test))]
pub fn indent<'a>(s: &'a str, prefix: &'a str, hanging: bool) -> Cow<'a, str> {
if s.is_empty() || prefix.is_empty() {
return Cow::Borrowed(s);
}
let mut result = String::with_capacity(s.len() * 2);
for (i, line) in s.lines().enumerate() {
if !hanging || i != 0 {
result.push_str(prefix.as_ref());
}
result.push_str(line);
result.push('\n');
}
if !s.ends_with('\n') {
result.truncate(result.len() - 1);
}
result.shrink_to_fit();
Cow::Owned(result)
}
pub fn whitespace(spaces: usize) -> Cow<'static, str> {
match spaces {
i if i < PREFIX_CACHE.len() => Cow::Borrowed(&PREFIX_CACHE[..i]),
i => Cow::Owned(" ".repeat(i)),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn indent_when_the_indent_len_is_zero() {
let input = "line 1\nline 2\n line 3\n";
let expected = Cow::Borrowed(input);
assert_eq!(indent(input, "", false), expected);
}
#[test]
fn indent_when_the_indent_len_is_non_zero() {
let input = " line 1\n line 2\n line 3\n";
let expected = Cow::<'_, str>::Owned(String::from(" line 1\n line 2\n line 3\n"));
assert_eq!(indent(input, " ", false), expected);
}
#[test]
fn indent_when_there_is_no_trailing_newline() {
let input = " line 1\n line 2\n line 3";
let expected = Cow::<'_, str>::Owned(String::from(" line 1\n line 2\n line 3"));
assert_eq!(indent(input, " ", false), expected);
}
}