use crate::{Transform, TransformError, TransformerCategory};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct LineNumberRemover;
pub const DEFAULT_TEST_INPUT: &str = "1. First line\n2. Second line\n3. Third line";
impl Transform for LineNumberRemover {
fn name(&self) -> &'static str {
"Line Number Remover"
}
fn id(&self) -> &'static str {
"linenumberremover"
}
fn description(&self) -> &'static str {
"Removes line numbers (and optional delimiters) from the beginning of each line."
}
fn category(&self) -> TransformerCategory {
TransformerCategory::Formatter
}
fn default_test_input(&self) -> &'static str {
""
}
fn transform(&self, input: &str) -> Result<String, TransformError> {
let mut output = String::new();
for (i, line) in input.lines().enumerate() {
let trimmed_line = line.trim_start();
let first_char_idx = trimmed_line
.find(|c: char| !c.is_ascii_digit())
.unwrap_or(trimmed_line.len());
if trimmed_line[..first_char_idx]
.chars()
.all(|c| c.is_ascii_digit())
{
let content_start_idx = trimmed_line[first_char_idx..]
.find(|c: char| !c.is_whitespace() && !matches!(c, '.' | ':' | '-' | ')'))
.map(|idx| first_char_idx + idx)
.unwrap_or(trimmed_line.len()); output.push_str(&trimmed_line[content_start_idx..]);
} else {
output.push_str(line.trim_start()); }
if i < input.lines().count() - 1 || input.ends_with('\n') {
output.push('\n');
}
}
if input.is_empty() {
return Ok("".to_string());
}
if output.is_empty() && input.chars().all(|c| c == '\n') {
return Ok(input.to_string()); } else if !input.ends_with('\n') && output.ends_with('\n') {
output.pop();
}
Ok(output)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_line_number_remover() {
let transformer = LineNumberRemover;
assert_eq!(
transformer.transform(DEFAULT_TEST_INPUT).unwrap(),
"First line\nSecond line\nThird line"
);
assert_eq!(
transformer.transform("1 Hello\n2 World").unwrap(),
"Hello\nWorld"
);
assert_eq!(
transformer
.transform("1. First line\n2. Second line\n")
.unwrap(),
"First line\nSecond line\n"
);
assert_eq!(
transformer.transform("3:\tThird line").unwrap(),
"Third line"
);
assert_eq!(
transformer.transform("No leading number").unwrap(),
"No leading number"
);
assert_eq!(transformer.transform("").unwrap(), "");
assert_eq!(transformer.transform("1 \n2 \n").unwrap(), "\n\n"); assert_eq!(
transformer.transform("1 Line1\n\n3 Line3").unwrap(),
"Line1\n\nLine3"
); assert_eq!(transformer.transform(" 4) Item 4").unwrap(), "Item 4"); assert_eq!(transformer.transform("5.").unwrap(), ""); assert_eq!(
transformer
.transform("Line without number\n6 Line with number")
.unwrap(),
"Line without number\nLine with number"
);
assert_eq!(transformer.transform("10- Item ten").unwrap(), "Item ten");
}
}