oma_contents/
parser.rs

1use winnow::{ModalResult, Parser, combinator::separated, token::take_till};
2
3use crate::OmaContentsError;
4
5/// Parse Contents file from input
6pub fn parse_contetns(input: &str) -> Result<Vec<(&str, Vec<&str>)>, OmaContentsError> {
7    input
8        .lines()
9        .enumerate()
10        .map(|x| match parse_contents_single_line(x.1) {
11            Ok(v) => Ok(v),
12            Err(e) => Err(OmaContentsError::InvalidContentsWithLine(
13                e.to_string(),
14                x.0,
15            )),
16        })
17        .collect::<Result<Vec<_>, OmaContentsError>>()
18}
19
20/// Parse single line Contents
21pub fn parse_contents_single_line(input: &str) -> Result<(&str, Vec<&str>), OmaContentsError> {
22    // https://wiki.debian.org/DebianRepository/Format#A.22Contents.22_indices
23    // 最后一个空格是分隔符
24    let (file, pkgs) = input
25        .rsplit_once(|c: char| c.is_whitespace() && c != '\n')
26        .ok_or(OmaContentsError::InvalidContents(
27            "Failed to get last space".to_string(),
28        ))?;
29
30    let file = file.trim();
31    let mut pkgs = pkgs.trim();
32    let pkgs = multi_packages(&mut pkgs)
33        .map_err(|e| OmaContentsError::InvalidContents(format!("Failed to get packages: {e}")))?;
34
35    Ok((file, pkgs))
36}
37
38#[inline]
39fn single_package<'a>(input: &mut &'a str) -> ModalResult<&'a str> {
40    take_till(0.., |c| c == ',' || c == '\n').parse_next(input)
41}
42
43#[inline]
44fn multi_packages<'a>(input: &mut &'a str) -> ModalResult<Vec<&'a str>> {
45    separated(0.., single_package, ',').parse_next(input)
46}
47
48#[test]
49fn test_single_line() {
50    let s = "etc/dpkg/dpkg.cfg.d/pk4\t\t\t\t\t    universe/utils/pk4\n";
51    let res = parse_contents_single_line(s);
52
53    assert_eq!(
54        res.unwrap(),
55        ("etc/dpkg/dpkg.cfg.d/pk4", vec!["universe/utils/pk4"])
56    )
57}
58
59#[test]
60fn test_single_line_multi_packages() {
61    let s = "opt/32/libexec   devel/gcc+32,devel/llvm+32,gnome/gconf+32,libs/gdk-pixbuf+32\n";
62    let res = parse_contents_single_line(s);
63
64    assert_eq!(
65        res.unwrap(),
66        (
67            "opt/32/libexec",
68            vec![
69                "devel/gcc+32",
70                "devel/llvm+32",
71                "gnome/gconf+32",
72                "libs/gdk-pixbuf+32"
73            ]
74        )
75    )
76}
77
78#[test]
79fn test_single_line_file_multi_space() {
80    let s = "/etc/i have multi space foo/bar/abc\n";
81    let res = parse_contents_single_line(s);
82
83    assert_eq!(
84        res.unwrap(),
85        ("/etc/i have multi space", vec!["foo/bar/abc"])
86    )
87}