rust_docs_mcp/cache/
member_utils.rs1use anyhow::{Result, bail};
8
9pub fn normalize_member_path(member_path: &str) -> String {
11 member_path.replace('/', "-")
12}
13
14pub fn needs_normalization(member_path: &str) -> bool {
16 member_path.contains('/')
17}
18
19pub fn validate_member_path(member_path: &str) -> Result<()> {
24 if member_path.is_empty() {
26 bail!("Invalid member path: empty path not allowed");
27 }
28
29 if member_path.starts_with('/') || member_path.starts_with('\\') {
31 bail!(
32 "Invalid member path '{}': absolute paths not allowed",
33 member_path
34 );
35 }
36
37 if member_path.len() > 2 && member_path.chars().nth(1) == Some(':') {
39 bail!(
40 "Invalid member path '{}': absolute paths not allowed",
41 member_path
42 );
43 }
44
45 if member_path.contains("..") {
47 bail!(
48 "Invalid member path '{}': path traversal not allowed",
49 member_path
50 );
51 }
52
53 if member_path.contains('\\') {
55 bail!(
56 "Invalid member path '{}': backslashes not allowed",
57 member_path
58 );
59 }
60
61 Ok(())
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67
68 #[test]
69 fn test_normalize_member_path() {
70 assert_eq!(normalize_member_path("crates/rmcp"), "crates-rmcp");
71 assert_eq!(
72 normalize_member_path("crates/rmcp/submodule"),
73 "crates-rmcp-submodule"
74 );
75 assert_eq!(normalize_member_path("simple"), "simple");
76 assert_eq!(normalize_member_path("already-dashed"), "already-dashed");
77 }
78
79 #[test]
80 fn test_needs_normalization() {
81 assert!(needs_normalization("crates/rmcp"));
82 assert!(needs_normalization("path/to/member"));
83 assert!(!needs_normalization("simple"));
84 assert!(!needs_normalization("already-dashed"));
85 }
86
87 #[test]
88 fn test_validate_member_path() {
89 assert!(validate_member_path("crates/rmcp").is_ok());
91 assert!(validate_member_path("simple").is_ok());
92 assert!(validate_member_path("path/to/member").is_ok());
93
94 assert!(validate_member_path("").is_err());
96 assert!(validate_member_path("/absolute/path").is_err());
97 assert!(validate_member_path("\\windows\\path").is_err());
98 assert!(validate_member_path("C:\\Windows").is_err());
99 assert!(validate_member_path("../parent").is_err());
100 assert!(validate_member_path("path/../traversal").is_err());
101 assert!(validate_member_path("path\\with\\backslash").is_err());
102 }
103}