pub(crate) fn is_in_subtree(module: &str, ancestor: &str) -> bool {
ancestor.is_empty()
|| module == ancestor
|| module
.strip_prefix(ancestor)
.is_some_and(|rest| rest.starts_with("::"))
}
pub(crate) fn split_parent(module: &str) -> (&str, &str) {
module.rsplit_once("::").unwrap_or(("", module))
}
pub(crate) fn parent_module(module: &str) -> &str {
split_parent(module).0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn is_in_subtree_semantics() {
assert!(is_in_subtree("", ""));
assert!(is_in_subtree("foo", ""));
assert!(is_in_subtree("foo::bar", ""));
assert!(is_in_subtree("foo", "foo"));
assert!(is_in_subtree("foo::bar", "foo"));
assert!(is_in_subtree("foo::bar::baz", "foo"));
assert!(!is_in_subtree("foobar", "foo"));
assert!(!is_in_subtree("baz", "foo"));
assert!(!is_in_subtree("", "foo"));
}
#[test]
fn is_in_subtree_matches_on_multi_segment_ancestor() {
assert!(is_in_subtree("foo::bar::baz", "foo::bar"));
assert!(!is_in_subtree("foo::barbaz", "foo::bar"));
}
#[test]
fn is_in_subtree_is_not_symmetric() {
assert!(is_in_subtree("foo::bar", "foo"));
assert!(!is_in_subtree("foo", "foo::bar"));
}
#[test]
fn split_parent_separates_nested_path() {
assert_eq!(split_parent("foo::bar"), ("foo", "bar"));
assert_eq!(split_parent("foo::bar::baz"), ("foo::bar", "baz"));
}
#[test]
fn split_parent_roots_top_level_module() {
assert_eq!(split_parent("foo"), ("", "foo"));
}
#[test]
fn split_parent_of_root_is_empty() {
assert_eq!(split_parent(""), ("", ""));
}
#[test]
fn parent_module_handles_root_and_nested() {
assert_eq!(parent_module("foo::bar"), "foo");
assert_eq!(parent_module("foo::bar::baz"), "foo::bar");
assert_eq!(parent_module("foo"), "");
assert_eq!(parent_module(""), "");
}
#[test]
fn parent_is_an_ancestor_of_its_child() {
let child = "foo::bar::baz";
assert!(is_in_subtree(child, parent_module(child)));
}
}