1pub fn extension_from_ascii_or_utf8_bytes(
7 path: &[u8],
8 path_separator: u8,
9) -> Option<&str> {
10 let dot_pos = path.iter().rposition(|b| *b == b'.')?;
11 if let Some(separator_pos) = path.iter().rposition(|b| *b == path_separator)
12 {
13 if !(separator_pos + 1 < dot_pos) {
14 return None;
15 }
16 } else {
17 if dot_pos < 1 {
18 return None;
19 }
20 }
21 let slice = &path[dot_pos + 1..];
22 if slice.is_empty() {
23 None
24 } else {
25 std::str::from_utf8(slice).ok()
26 }
27}
28
29#[test]
30fn t_extension_from_ascii_or_utf8_bytes() {
31 fn t(s: &str) -> Option<&str> {
32 extension_from_ascii_or_utf8_bytes(s.as_bytes(), b'/')
33 }
34 assert_eq!(t("foo.bar"), Some("bar"));
35 assert_eq!(t("foobar"), None);
36 assert_eq!(t("foo/bar.xz"), Some("xz"));
37 assert_eq!(t("foo.bar/xz"), None);
38 assert_eq!(t("foo.bar\\xz"), Some("bar\\xz")); assert_eq!(t("foo/bar."), None);
40 assert_eq!(t(".bah"), None);
41 assert_eq!(t("a.b"), Some("b"));
42 assert_eq!(t("ah/.b"), None);
43 assert_eq!(t("ah/.b.a"), Some("a"));
44 assert_eq!(t("ah/a.b"), Some("b"));
45 let mut bs = [0x61, 0x2f, 0x61, 0x2e, 0x62];
46 let e = extension_from_ascii_or_utf8_bytes;
47 assert_eq!(e(&bs, b'/'), Some("b"));
48 bs[1] = 0x90;
49 assert_eq!(e(&bs, b'/'), Some("b"));
50 bs[1] = 0xf0;
51 assert_eq!(e(&bs, b'/'), Some("b"));
52 bs[4] = 0x42;
53 assert_eq!(e(&bs, b'/'), Some("B"));
54 bs[4] = 0xf0;
55 assert_eq!(e(&bs, b'/'), None);
56}