diff --git a/src/lib.rs b/src/lib.rs index 1958ccd1..bf3d8604 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,17 @@ use std::path::Path; /// Split a markdown table line into its cells. #[must_use] +/// Splits a markdown table line into trimmed cell strings. +/// +/// Removes leading and trailing pipe characters, splits the line by pipes, trims whitespace from each cell, and returns the resulting cell strings as a vector. +/// +/// # Examples +/// +/// ``` +/// let line = "| cell1 | cell2 | cell3 |"; +/// let cells = split_cells(line); +/// assert_eq!(cells, vec!["cell1", "cell2", "cell3"]); +/// ``` fn split_cells(line: &str) -> Vec { let mut s = line.trim(); if let Some(stripped) = s.strip_prefix('|') { @@ -24,6 +35,23 @@ fn split_cells(line: &str) -> Vec { /// # Panics /// Panics if the internal regex fails to compile. #[must_use] +/// Reflows a broken markdown table into properly aligned rows and columns. +/// +/// Takes a slice of strings representing lines of a markdown table, reconstructs the table by splitting and aligning cells, and returns the reflowed table as a vector of strings. If the rows have inconsistent numbers of non-empty columns, the original lines are returned unchanged. +/// +/// # Examples +/// +/// ``` +/// let lines = vec![ +/// "| a | b |".to_string(), +/// "| c | d |".to_string(), +/// ]; +/// let fixed = reflow_table(&lines); +/// assert_eq!(fixed, vec![ +/// "| a | b |".to_string(), +/// "| c | d |".to_string(), +/// ]); +/// ``` pub fn reflow_table(lines: &[String]) -> Vec { let raw = lines.iter().map(|l| l.trim()).collect::>().join(" "); let sentinel_re = Regex::new(r"\|\s*\|\s*").unwrap(); @@ -82,6 +110,35 @@ pub fn reflow_table(lines: &[String]) -> Vec { /// # Panics /// Panics if the regex used for code fences fails to compile. #[must_use] +/// Processes a stream of markdown lines, reflowing tables while preserving code blocks and other content. +/// +/// Detects fenced code blocks and avoids modifying their contents. Buffers lines that appear to be part of a markdown table and reflows them when the table ends. Non-table lines and code blocks are output unchanged. +/// +/// # Returns +/// +/// A vector of strings representing the processed markdown document with tables reflowed. +/// +/// # Examples +/// +/// ``` +/// let input = vec![ +/// "| a | b |", +/// "|---|---|", +/// "| 1 | 2 |", +/// "", +/// "```", +/// "code block", +/// "```", +/// ]; +/// let output = process_stream(&input); +/// assert_eq!(output[0], "| a | b |"); +/// assert_eq!(output[1], "| --- | --- |"); +/// assert_eq!(output[2], "| 1 | 2 |"); +/// assert_eq!(output[3], ""); +/// assert_eq!(output[4], "```"); +/// assert_eq!(output[5], "code block"); +/// assert_eq!(output[6], "```"); +/// ``` pub fn process_stream(lines: &[String]) -> Vec { let fence_re = Regex::new(r"^(```|~~~)").unwrap(); let mut out = Vec::new(); @@ -143,7 +200,17 @@ pub fn process_stream(lines: &[String]) -> Vec { /// Rewrite a file in place with fixed tables. /// /// # Errors +/// Reads a markdown file, reflows any broken tables within it, and writes the updated content back to the same file. +/// /// Returns an error if the file cannot be read or written. +/// +/// # Examples +/// +/// ``` +/// use std::path::Path; +/// let path = Path::new("example.md"); +/// rewrite(path).unwrap(); +/// ``` pub fn rewrite(path: &Path) -> std::io::Result<()> { let text = fs::read_to_string(path)?; let lines: Vec = text.lines().map(str::to_string).collect(); diff --git a/src/main.rs b/src/main.rs index 9c6030b3..c2db03d2 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,6 +14,26 @@ struct Cli { files: Vec, } +/// Entry point for the command-line tool that reflows broken markdown tables. +/// +/// Parses command-line arguments to determine whether to process files in place, print fixed output to standard output, or read from standard input. Handles file I/O and error propagation as needed. +/// +/// # Returns +/// +/// Returns `Ok(())` if all operations complete successfully; otherwise, returns an error if argument validation or file processing fails. +/// +/// # Examples +/// +/// ```sh +/// # Fix tables in a file and print to stdout +/// mdtablefix myfile.md +/// +/// # Fix tables in place +/// mdtablefix --in-place myfile.md +/// +/// # Fix tables from standard input +/// cat myfile.md | mdtablefix +/// ``` fn main() -> anyhow::Result<()> { let cli = Cli::parse(); diff --git a/tests/integration.rs b/tests/integration.rs index e39934c8..b35b9baa 100644 --- a/tests/integration.rs +++ b/tests/integration.rs @@ -6,6 +6,16 @@ use std::io::Write; use tempfile::tempdir; #[fixture] +/// Provides a sample Markdown table with broken rows for testing purposes. +/// +/// The returned vector contains lines representing a table with inconsistent columns, useful for validating table reflow logic. +/// +/// # Examples +/// +/// ``` +/// let table = broken_table(); +/// assert_eq!(table[0], "| A | B | |"); +/// ``` fn broken_table() -> Vec { vec![ "| A | B | |".to_string(), @@ -14,22 +24,47 @@ fn broken_table() -> Vec { } #[fixture] +/// Returns a vector of strings representing a malformed Markdown table with inconsistent columns. +/// +/// The returned table has rows with differing numbers of columns, making it invalid for standard Markdown table parsing. +/// +/// # Examples +/// +/// ``` +/// let table = malformed_table(); +/// assert_eq!(table, vec![String::from("| A | |"), String::from("| 1 | 2 | 3 |")]); +/// ``` fn malformed_table() -> Vec { vec!["| A | |".to_string(), "| 1 | 2 | 3 |".to_string()] } #[rstest] +/// Tests that `reflow_table` correctly restructures a broken Markdown table into a well-formed table. +/// +/// # Examples +/// +/// ``` +/// let broken = vec![String::from("| A | B |"), String::from("| 1 | 2 |"), String::from("| 3 | 4 |")]; +/// let expected = vec!["| A | B |", "| 1 | 2 |", "| 3 | 4 |"]; +/// assert_eq!(reflow_table(&broken), expected); +/// ``` fn test_reflow_basic(broken_table: Vec) { let expected = vec!["| A | B |", "| 1 | 2 |", "| 3 | 4 |"]; assert_eq!(reflow_table(&broken_table), expected); } #[rstest] +/// Tests that `reflow_table` returns the original input unchanged when given a malformed Markdown table. +/// +/// This ensures that the function does not attempt to modify tables with inconsistent columns or structure. fn test_reflow_malformed_returns_original(malformed_table: Vec) { assert_eq!(reflow_table(&malformed_table), malformed_table); } #[rstest] +/// Tests that `process_stream` leaves lines inside code fences unchanged. +/// +/// Verifies that both backtick (```) and tilde (~~~) fenced code blocks are ignored by the table processing logic, ensuring their contents are not altered. fn test_process_stream_ignores_code_fences() { let lines = vec![ "```".to_string(), @@ -48,6 +83,16 @@ fn test_process_stream_ignores_code_fences() { } #[rstest] +/// Verifies that the CLI fails when the `--in-place` flag is used without specifying a file. +/// +/// This test ensures that running `mdtablefix --in-place` without a file argument results in a command failure. +/// +/// # Examples +/// +/// ``` +/// test_cli_in_place_requires_file(); +/// // The command should fail as no file is provided. +/// ``` fn test_cli_in_place_requires_file() { Command::cargo_bin("mdtablefix") .unwrap() @@ -57,6 +102,20 @@ fn test_cli_in_place_requires_file() { } #[rstest] +/// Tests that the CLI processes a file containing a broken Markdown table and outputs the corrected table to stdout. +/// +/// This test creates a temporary file with a malformed table, runs the `mdtablefix` binary on it, and asserts that the output is the expected fixed table. +/// +/// # Examples +/// +/// ``` +/// let broken_table = vec![ +/// "| A | B |".to_string(), +/// "| 1 | 2 |".to_string(), +/// "| 3 | 4 |".to_string(), +/// ]; +/// test_cli_process_file(broken_table); +/// ``` fn test_cli_process_file(broken_table: Vec) { let dir = tempdir().unwrap(); let file_path = dir.path().join("sample.md");