cargo_docs_md/
utils.rs

1//! Shared utility functions used across the documentation generator.
2//!
3//! This module contains small, general-purpose helpers that are used
4//! by multiple components and don't belong to any specific domain.
5//!
6//! # Organization
7//!
8//! Utilities are organized into unit structs by category:
9//! - [`PathUtils`]: Rust path manipulation utilities
10
11/// Utilities for working with Rust paths (e.g., `std::vec::Vec`).
12///
13/// Rust paths use `::` as a separator. This struct provides methods
14/// for common path operations used throughout the documentation generator.
15///
16/// # Design Note
17///
18/// This is a unit struct (no fields) that serves as a namespace for
19/// related utility functions. All methods are associated functions
20/// that don't require an instance.
21///
22/// # Examples
23///
24/// ```
25/// use cargo_docs_md::utils::PathUtils;
26///
27/// // Extract the short name from a qualified path
28/// assert_eq!(PathUtils::short_name("std::vec::Vec"), "Vec");
29/// assert_eq!(PathUtils::short_name("Clone"), "Clone");
30/// ```
31pub struct PathUtils;
32
33impl PathUtils {
34    /// Extract the short name (last segment) from a qualified Rust path.
35    ///
36    /// Rust paths use `::` as a separator. This function returns the final
37    /// segment, which is typically the item's simple name without module prefix.
38    ///
39    /// # Examples
40    ///
41    /// ```
42    /// use cargo_docs_md::utils::PathUtils;
43    ///
44    /// assert_eq!(PathUtils::short_name("std::vec::Vec"), "Vec");
45    /// assert_eq!(PathUtils::short_name("std::collections::HashMap"), "HashMap");
46    /// assert_eq!(PathUtils::short_name("Clone"), "Clone");
47    /// assert_eq!(PathUtils::short_name(""), "");
48    /// ```
49    ///
50    /// # Edge Cases
51    ///
52    /// - Empty string returns empty string
53    /// - Path ending with `::` returns empty string (e.g., `"foo::"` -> `""`)
54    /// - Single segment returns itself (e.g., `"Vec"` -> `"Vec"`)
55    #[inline]
56    #[must_use]
57    pub fn short_name(path: &str) -> &str {
58        // Using rsplit().next() is more efficient than split().last()
59        // because it doesn't need to traverse the entire iterator
60        path.rsplit("::").next().unwrap_or(path)
61    }
62}
63
64#[cfg(test)]
65mod tests {
66    use super::*;
67
68    #[test]
69    fn short_name_qualified_path() {
70        assert_eq!(PathUtils::short_name("std::vec::Vec"), "Vec");
71        assert_eq!(
72            PathUtils::short_name("std::collections::HashMap"),
73            "HashMap"
74        );
75        assert_eq!(PathUtils::short_name("tokio::sync::mpsc::Sender"), "Sender");
76    }
77
78    #[test]
79    fn short_name_simple() {
80        assert_eq!(PathUtils::short_name("Vec"), "Vec");
81        assert_eq!(PathUtils::short_name("Clone"), "Clone");
82        assert_eq!(PathUtils::short_name("u32"), "u32");
83    }
84
85    #[test]
86    fn short_name_edge_cases() {
87        assert_eq!(PathUtils::short_name(""), "");
88        assert_eq!(PathUtils::short_name("::"), "");
89        assert_eq!(PathUtils::short_name("foo::"), "");
90        assert_eq!(PathUtils::short_name("::foo"), "foo");
91    }
92
93    #[test]
94    fn short_name_with_turbofish() {
95        // Turbofish syntax `Type::<T>` contains `::` so it gets split
96        // This extracts the generic part `<T>` since `::` is the separator
97        assert_eq!(PathUtils::short_name("Type::<T>"), "<T>");
98
99        // A single colon is NOT a Rust path separator
100        assert_eq!(PathUtils::short_name("Type:T"), "Type:T");
101    }
102}