pub(super) fn is_package_manifest(path: &str) -> Option<FoundPackageManifest<'_>> {
let mut iter = ReverseSegmentsIter::new(path);
let package = iter
.next()
.and_then(|s| s.strip_suffix(".json"))
.filter(|s| !s.is_empty())?;
let product = iter.next()?;
iter.next().filter(|s| *s == "criticaltrust")?;
iter.next().filter(|s| *s == "share")?;
Some(FoundPackageManifest {
package,
product,
prefix: iter.remaining(),
})
}
#[derive(Debug, PartialEq, Eq)]
pub(super) struct FoundPackageManifest<'a> {
pub(super) package: &'a str,
pub(super) product: &'a str,
pub(super) prefix: Option<&'a str>,
}
struct ReverseSegmentsIter<'a> {
first: bool,
cursor: &'a str,
}
impl<'a> ReverseSegmentsIter<'a> {
fn new(input: &'a str) -> Self {
Self {
first: true,
cursor: input,
}
}
fn remaining(&self) -> Option<&'a str> {
none_if_empty(self.cursor)
}
}
impl<'a> Iterator for ReverseSegmentsIter<'a> {
type Item = &'a str;
fn next(&mut self) -> Option<Self::Item> {
let result = loop {
let end = self.cursor.len().saturating_sub(!self.first as usize);
self.first = false;
if let Some(pos) = self.cursor[..end].rfind('/') {
let (new_cursor, result) = self.cursor[..end].split_at(pos + 1);
self.cursor = new_cursor;
if !result.is_empty() {
break result;
}
} else {
let result = &self.cursor[..end];
self.cursor = "";
break result;
}
};
none_if_empty(result)
}
}
fn none_if_empty(input: &str) -> Option<&str> {
if input.is_empty() {
None
} else {
Some(input)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_is_package_manifest() {
const CASES: &[(&str, Option<FoundPackageManifest>)] = &[
(
"share/criticaltrust/product/package.json",
Some(FoundPackageManifest {
package: "package",
product: "product",
prefix: None,
}),
),
(
"share/criticaltrust/other-product/other-package.json",
Some(FoundPackageManifest {
package: "other-package",
product: "other-product",
prefix: None,
}),
),
(
"product/share/criticaltrust/product/package.json",
Some(FoundPackageManifest {
package: "package",
product: "product",
prefix: Some("product/"),
}),
),
(
"/usr/share/criticaltrust/product/package.json",
Some(FoundPackageManifest {
package: "package",
product: "product",
prefix: Some("/usr/"),
}),
),
(
"/usr/local/share/criticaltrust/product/package.json",
Some(FoundPackageManifest {
package: "package",
product: "product",
prefix: Some("/usr/local/"),
}),
),
(
"/home/pietro/.criticalup/files/share/criticaltrust/product/package.json",
Some(FoundPackageManifest {
package: "package",
product: "product",
prefix: Some("/home/pietro/.criticalup/files/"),
}),
),
("criticaltrust/product/package.json", None),
("share/product/package.json", None),
("share/criticaltrust/package.json", None),
("share/criticaltrust/product", None),
("share/criticaltrust/product/package.toml", None),
("share/criticaltrust/product/package", None),
("share/criticaltrust/product/.json", None),
("share/criticaltrust//package.json", None),
];
for (case, expected) in CASES {
assert_eq!(*expected, is_package_manifest(case), "\n case: `{case}`\n");
}
}
#[test]
fn test_reverse_segments_iter_relative() {
let mut iter = ReverseSegmentsIter::new("foo/ba\\r//baz.json");
assert_eq!(Some("foo/ba\\r//baz.json"), iter.remaining());
assert_eq!(Some("baz.json"), iter.next());
assert_eq!(Some("foo/ba\\r//"), iter.remaining());
assert_eq!(Some("ba\\r"), iter.next());
assert_eq!(Some("foo/"), iter.remaining());
assert_eq!(Some("foo"), iter.next());
assert_eq!(None, iter.remaining());
assert_eq!(None, iter.next());
assert_eq!(None, iter.remaining());
}
#[test]
fn test_reverse_segments_iter_absolute() {
let mut iter = ReverseSegmentsIter::new("/foo/ba\\r//baz.json");
assert_eq!(Some("/foo/ba\\r//baz.json"), iter.remaining());
assert_eq!(Some("baz.json"), iter.next());
assert_eq!(Some("/foo/ba\\r//"), iter.remaining());
assert_eq!(Some("ba\\r"), iter.next());
assert_eq!(Some("/foo/"), iter.remaining());
assert_eq!(Some("foo"), iter.next());
assert_eq!(Some("/"), iter.remaining());
assert_eq!(None, iter.next());
assert_eq!(None, iter.remaining());
}
}