1#[doc(hidden)]
2pub mod __private {
3 pub const fn module_path_without_crate_impl(full: &str) -> &str {
14 let mut bytes = full.as_bytes();
15
16 while let [first, rest @ ..] = bytes {
17 bytes = rest;
18 if *first == b':' {
19 match rest {
20 [second, rest @ ..] if *second == b':' => {
21 return unsafe { str::from_utf8_unchecked(rest) };
27 }
28 _ => (),
29 }
30 }
31 }
32
33 panic!("expected a module path containing '::', like 'crate::module'");
34 }
35}
36
37#[macro_export]
46macro_rules! module_path_without_crate {
47 () => {
48 const { $crate::module_path::__private::module_path_without_crate_impl(module_path!()) }
49 };
50}
51
52#[cfg(test)]
53mod tests {
54 use super::__private::module_path_without_crate_impl;
55
56 #[test]
57 fn impl_strips_first_segment() {
58 assert_eq!("b", const { module_path_without_crate_impl("a::b") });
59 assert_eq!("b::c", const { module_path_without_crate_impl("a::b::c") });
60 assert_eq!(
61 "nested::module::item",
62 const { module_path_without_crate_impl("crate::nested::module::item") }
63 );
64 }
65
66 #[test]
67 fn impl_allows_non_ascii_after_separator() {
68 assert_eq!(
69 "mödule::ty",
70 const { module_path_without_crate_impl("crate::mödule::ty") }
71 );
72 }
73
74 #[test]
75 #[should_panic(expected = "expected a module path containing '::'")]
76 fn impl_panics_when_no_separator_exists() {
77 let _ = module_path_without_crate_impl("crate");
78 }
79
80 #[test]
81 fn macro_matches_module_path_suffix_here() {
82 assert_eq!("module_path::tests", crate::module_path_without_crate!());
83 }
84
85 #[test]
86 fn macro_matches_module_path_suffix_in_nested_module() {
87 mod nested {
88 pub fn value() -> &'static str {
89 crate::module_path_without_crate!()
90 }
91 }
92
93 assert_eq!("module_path::tests::nested", nested::value());
94 }
95}