rumdl/utils/
mod.rs

1//!
2//! Shared utilities for rumdl, including document structure analysis, code block handling, regex helpers, and string extensions.
3//! Provides reusable traits and functions for rule implementations and core linter logic.
4
5pub mod ast_utils;
6pub mod code_block_utils;
7pub mod document_structure;
8pub mod early_returns;
9pub mod element_cache;
10pub mod markdown_elements;
11pub mod range_utils;
12pub mod regex_cache;
13pub mod string_interner;
14
15pub use ast_utils::AstCache;
16pub use code_block_utils::CodeBlockUtils;
17pub use document_structure::DocumentStructure;
18pub use markdown_elements::{ElementQuality, ElementType, MarkdownElement, MarkdownElements};
19pub use range_utils::LineIndex;
20
21/// Trait for string-related extensions
22pub trait StrExt {
23    /// Replace trailing spaces with a specified replacement string
24    fn replace_trailing_spaces(&self, replacement: &str) -> String;
25
26    /// Check if the string has trailing whitespace
27    fn has_trailing_spaces(&self) -> bool;
28
29    /// Count the number of trailing spaces in the string
30    fn trailing_spaces(&self) -> usize;
31}
32
33impl StrExt for str {
34    fn replace_trailing_spaces(&self, replacement: &str) -> String {
35        // Custom implementation to handle both newlines and tabs specially
36
37        // Check if string ends with newline
38        let (content, ends_with_newline) = if let Some(stripped) = self.strip_suffix('\n') {
39            (stripped, true)
40        } else {
41            (self, false)
42        };
43
44        // Find where the trailing spaces begin
45        let mut non_space_len = content.len();
46        for c in content.chars().rev() {
47            if c == ' ' {
48                non_space_len -= 1;
49            } else {
50                break;
51            }
52        }
53
54        // Build the final string
55        let mut result = String::with_capacity(
56            non_space_len + replacement.len() + if ends_with_newline { 1 } else { 0 },
57        );
58        result.push_str(&content[..non_space_len]);
59        result.push_str(replacement);
60        if ends_with_newline {
61            result.push('\n');
62        }
63
64        result
65    }
66
67    fn has_trailing_spaces(&self) -> bool {
68        self.trailing_spaces() > 0
69    }
70
71    fn trailing_spaces(&self) -> usize {
72        // Custom implementation to handle both newlines and tabs specially
73
74        // Prepare the string without newline if it ends with one
75        let content = self.strip_suffix('\n').unwrap_or(self);
76
77        // Count only trailing spaces at the end, not tabs
78        let mut space_count = 0;
79        for c in content.chars().rev() {
80            if c == ' ' {
81                space_count += 1;
82            } else {
83                break;
84            }
85        }
86
87        space_count
88    }
89}
90
91use std::collections::hash_map::DefaultHasher;
92use std::hash::{Hash, Hasher};
93
94/// Fast hash function for string content
95///
96/// This utility function provides a quick way to generate a hash from string content
97/// for use in caching mechanisms. It uses Rust's built-in DefaultHasher.
98///
99/// # Arguments
100///
101/// * `content` - The string content to hash
102///
103/// # Returns
104///
105/// A 64-bit hash value derived from the content
106pub fn fast_hash(content: &str) -> u64 {
107    let mut hasher = DefaultHasher::new();
108    content.hash(&mut hasher);
109    hasher.finish()
110}