#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct SourceLoc {
pub file: &'static str,
pub line: u32,
}
impl SourceLoc {
pub const fn new(file: &'static str, line: u32) -> Self {
Self { file, line }
}
pub fn matches_path(&self, target: &str) -> bool {
fn norm(s: &str) -> String {
s.replace('\\', "/")
}
fn suffix_on_boundary(hay: &str, needle: &str) -> bool {
hay == needle || hay.strip_suffix(needle).is_some_and(|p| p.ends_with('/'))
}
let a = norm(self.file);
let b = norm(target);
suffix_on_boundary(&a, &b) || suffix_on_boundary(&b, &a)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn matches_path_handles_suffix_matches() {
let loc = SourceLoc::new("crates/teksilo-widgets/src/button.rs", 42);
assert!(loc.matches_path("crates/teksilo-widgets/src/button.rs"));
assert!(loc.matches_path("button.rs"));
assert!(loc.matches_path("src/button.rs"));
assert!(!loc.matches_path("crates/teksilo-widgets/src/checkbox.rs"));
}
#[test]
fn suffix_match_respects_component_boundaries() {
let radio = SourceLoc::new("crates/teksilo-widgets/src/radio_button.rs", 1);
assert!(!radio.matches_path("button.rs"));
assert!(radio.matches_path("radio_button.rs"));
assert!(radio.matches_path("src/radio_button.rs"));
}
#[test]
fn matches_path_normalises_separators() {
let loc = SourceLoc::new("crates\\teksilo-widgets\\src\\button.rs", 1);
assert!(loc.matches_path("button.rs"));
assert!(loc.matches_path("src/button.rs"));
}
}