nix_query_tree_viewer/nix_query_tree/
parsing.rs

1use nom::character::complete::{newline, space1};
2use nom::combinator::complete;
3use nom::multi::{many0, many_m_n};
4use nom::{alt, do_parse, eof, map, named, opt, tag, take_till, IResult};
5
6use super::super::tree::Tree;
7use super::{NixQueryDrv, NixQueryEntry, NixQueryTree, Recurse};
8
9named!(parse_nix_query_drv<&str, NixQueryDrv>,
10    map!(take_till!(char::is_whitespace), NixQueryDrv::from));
11
12named!(parse_recurse<&str, &str>,
13    tag!("[...]"));
14
15named!(parse_nix_query_entry<&str, NixQueryEntry>,
16    do_parse!(
17        drv: parse_nix_query_drv >>
18        opt_recurse: opt!(
19            do_parse!(
20                space1 >>
21                parse_recurse >>
22                (Recurse::Yes)
23            )) >>
24        (NixQueryEntry(drv, opt_recurse.unwrap_or(Recurse::No)))
25    ));
26
27pub fn nix_query_entry_parser(
28    input: &str,
29) -> Result<NixQueryEntry, nom::Err<(&str, nom::error::ErrorKind)>> {
30    parse_nix_query_entry(input).map(|(_, nix_query_entry)| nix_query_entry)
31}
32
33named!(parse_branch_start<&str, &str>,
34    alt!(tag!("+---") | tag!("├───") | tag!("└───")));
35
36named!(parse_extra_level<&str, &str>,
37    alt!(tag!("|   ") | tag!("    ") | tag!("│   ")));
38
39/// Run `parse_extra_level` exactly `level` times.
40fn parse_extra_levels(level: usize) -> impl Fn(&str) -> IResult<&str, ()> {
41    move |input| {
42        let (input, _) = many_m_n(level, level, parse_extra_level)(input)?;
43        Ok((input, ()))
44    }
45}
46
47/// Parse a single line of `nix-store --query --entry` output.
48fn parse_single_branch(
49    level: usize,
50) -> impl Fn(&str) -> IResult<&str, NixQueryEntry> {
51    move |input| {
52        let (input, _) = parse_extra_levels(level)(input)?;
53        let (input, _) = parse_branch_start(input)?;
54        let (input, nix_query_entry) = parse_nix_query_entry(input)?;
55        let (input, _) = newline(input)?;
56        Ok((input, nix_query_entry))
57    }
58}
59
60/// Parse a branch with all of its children.
61fn parse_branch_with_children(
62    level: usize,
63) -> impl Fn(&str) -> IResult<&str, Tree<NixQueryEntry>> {
64    move |input| {
65        let (input, nix_query_entry) = parse_single_branch(level)(input)?;
66        let (input, children) = parse_branches(level + 1)(input)?;
67        Ok((input, Tree::new(nix_query_entry, children)))
68    }
69}
70
71fn parse_branches(
72    level: usize,
73) -> impl Fn(&str) -> IResult<&str, Vec<Tree<NixQueryEntry>>> {
74    move |input| {
75        let (input, children) =
76            many0(complete(parse_branch_with_children(level)))(input)?;
77        Ok((input, children))
78    }
79}
80
81fn parse_nix_query_tree(input: &str) -> IResult<&str, NixQueryTree> {
82    let (input, top_drv): (&str, NixQueryDrv) = parse_nix_query_drv(input)?;
83    let (input, _) = newline(input)?;
84    let top_entry = NixQueryEntry(top_drv, Recurse::No);
85    let (input, children) = parse_branches(0)(input)?;
86    let tree = Tree::new(top_entry, children);
87    Ok((input, NixQueryTree(tree)))
88}
89
90named!(parse_nix_query_tree_final<&str, NixQueryTree>,
91    do_parse!(
92        nix_query_tree: parse_nix_query_tree >>
93        eof!() >>
94        (nix_query_tree)));
95
96/// Parse all output from `nix-store --query --tree`.
97pub fn nix_query_tree_parser(
98    input: &str,
99) -> Result<NixQueryTree, nom::Err<(&str, nom::error::ErrorKind)>> {
100    parse_nix_query_tree(input).map(|(_, nix_query_tree)| nix_query_tree)
101}
102
103#[cfg(test)]
104mod tests {
105    use super::*;
106
107    use indoc::indoc;
108
109    #[test]
110    fn test_parse_nix_query_drv() {
111        let raw_input =
112            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]";
113        let raw_path = "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10";
114        let nix_query_drv: NixQueryDrv = raw_path.into();
115        let r = parse_nix_query_drv(raw_input);
116        assert_eq!(r, Ok((" [...]", nix_query_drv)));
117    }
118
119    #[test]
120    fn test_parse_nix_query_entry_no_recurse() {
121        let raw_input =
122            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10\n";
123        let raw_path = "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10";
124        let nix_query_entry: NixQueryEntry =
125            NixQueryEntry(raw_path.into(), Recurse::No);
126        let r = parse_nix_query_entry(raw_input);
127        assert_eq!(r, Ok(("\n", nix_query_entry)));
128    }
129
130    #[test]
131    fn test_parse_nix_query_entry_recurse() {
132        let raw_input =
133            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]\n";
134        let raw_path = "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10";
135        let nix_query_entry: NixQueryEntry =
136            NixQueryEntry(raw_path.into(), Recurse::Yes);
137        let r = parse_nix_query_entry(raw_input);
138        assert_eq!(r, Ok(("\n", nix_query_entry)));
139    }
140
141    #[test]
142    fn test_parse_nix_query_tree_simple() {
143        let raw_input = indoc!(
144            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10
145            +---/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
146            "
147        );
148        let hello_drv: NixQueryDrv =
149            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
150        let actual_tree = Tree::new(
151            NixQueryEntry(hello_drv.clone(), Recurse::No),
152            vec![Tree::singleton(NixQueryEntry(hello_drv, Recurse::Yes))],
153        );
154
155        let r = parse_nix_query_tree(raw_input);
156        assert_eq!(r, Ok(("", NixQueryTree(actual_tree))));
157    }
158
159    #[test]
160    fn test_parse_nix_query_tree_simple_unicode() {
161        let raw_input = indoc!(
162            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10
163            └───/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
164            "
165        );
166        let hello_drv: NixQueryDrv =
167            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
168        let actual_tree = Tree::new(
169            NixQueryEntry(hello_drv.clone(), Recurse::No),
170            vec![Tree::singleton(NixQueryEntry(hello_drv, Recurse::Yes))],
171        );
172
173        let r = parse_nix_query_tree(raw_input);
174        assert_eq!(r, Ok(("", NixQueryTree(actual_tree))));
175    }
176
177    #[test]
178    fn test_parse_nix_query_tree_simple_unicode2() {
179        let raw_input = indoc!(
180            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10
181            ├───/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
182            "
183        );
184        let hello_drv: NixQueryDrv =
185            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
186        let actual_tree = Tree::new(
187            NixQueryEntry(hello_drv.clone(), Recurse::No),
188            vec![Tree::singleton(NixQueryEntry(hello_drv, Recurse::Yes))],
189        );
190
191        let r = parse_nix_query_tree(raw_input);
192        assert_eq!(r, Ok(("", NixQueryTree(actual_tree))));
193    }
194
195    #[test]
196    fn test_parse_branch_start() {
197        let raw_input = "+---";
198        let r = parse_branch_start(raw_input);
199        assert_eq!(r, Ok(("", "+---")));
200    }
201
202    #[test]
203    fn test_parse_single_branch() {
204        let raw_input = indoc!(
205            "
206                +---/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
207                "
208        );
209        let hello_drv: NixQueryDrv =
210            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
211        let actual_tree = NixQueryEntry(hello_drv.clone(), Recurse::Yes);
212
213        let r = parse_single_branch(0)(raw_input);
214        assert_eq!(r, Ok(("", actual_tree)));
215    }
216
217    #[test]
218    fn test_parse_branch_with_children_no_children() {
219        let raw_input = indoc!(
220            "
221                +---/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
222                "
223        );
224        let hello_drv: NixQueryDrv =
225            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
226        let actual_tree =
227            Tree::singleton(NixQueryEntry(hello_drv.clone(), Recurse::Yes));
228
229        let r = parse_branch_with_children(0)(raw_input);
230        assert_eq!(r, Ok(("", actual_tree)));
231    }
232
233    #[test]
234    fn test_parse_empty_branches() {
235        let raw_input = "foobar";
236        let actual_children = vec![];
237
238        let r = parse_branches(0)(raw_input);
239        assert_eq!(r, Ok(("foobar", actual_children)));
240    }
241
242    #[test]
243    fn test_parse_nix_query_tree_simple_multi_children() {
244        let raw_input = indoc!(
245            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10
246            +---/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27
247            +---/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27 [...]
248            +---/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
249            "
250        );
251        let hello_drv: NixQueryDrv =
252            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
253        let glibc_drv: NixQueryDrv =
254            "/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27".into();
255        let actual_tree = Tree::new(
256            NixQueryEntry(hello_drv.clone(), Recurse::No),
257            vec![
258                Tree::singleton(NixQueryEntry(glibc_drv.clone(), Recurse::No)),
259                Tree::singleton(NixQueryEntry(glibc_drv, Recurse::Yes)),
260                Tree::singleton(NixQueryEntry(hello_drv, Recurse::Yes)),
261            ],
262        );
263
264        let r = parse_nix_query_tree(raw_input);
265        assert_eq!(r, Ok(("", NixQueryTree(actual_tree))));
266    }
267
268    #[test]
269    fn test_parse_nix_query_tree_simple_multi_children_unicode() {
270        let raw_input = indoc!(
271            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10
272            ├───/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27
273            ├───/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27 [...]
274            └───/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
275            "
276        );
277        let hello_drv: NixQueryDrv =
278            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
279        let glibc_drv: NixQueryDrv =
280            "/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27".into();
281        let actual_tree = Tree::new(
282            NixQueryEntry(hello_drv.clone(), Recurse::No),
283            vec![
284                Tree::singleton(NixQueryEntry(glibc_drv.clone(), Recurse::No)),
285                Tree::singleton(NixQueryEntry(glibc_drv, Recurse::Yes)),
286                Tree::singleton(NixQueryEntry(hello_drv, Recurse::Yes)),
287            ],
288        );
289
290        let r = parse_nix_query_tree(raw_input);
291        assert_eq!(r, Ok(("", NixQueryTree(actual_tree))));
292    }
293
294    #[test]
295    fn test_parse_nix_query_tree_simple_multi_levels() {
296        let raw_input = indoc!(
297            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10
298            +---/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27
299            |   +---/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27 [...]
300            +---/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
301            "
302        );
303        let hello_drv: NixQueryDrv =
304            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
305        let glibc_drv: NixQueryDrv =
306            "/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27".into();
307        let actual_tree = Tree::new(
308            NixQueryEntry(hello_drv.clone(), Recurse::No),
309            vec![
310                Tree::new(
311                    NixQueryEntry(glibc_drv.clone(), Recurse::No),
312                    vec![Tree::singleton(NixQueryEntry(
313                        glibc_drv,
314                        Recurse::Yes,
315                    ))],
316                ),
317                Tree::singleton(NixQueryEntry(hello_drv, Recurse::Yes)),
318            ],
319        );
320
321        let r = parse_nix_query_tree(raw_input);
322        assert_eq!(r, Ok(("", NixQueryTree(actual_tree))));
323    }
324
325    #[test]
326    fn test_parse_nix_query_tree_simple_multi_levels_unicode() {
327        let raw_input = indoc!(
328            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10
329            ├───/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27
330            │   └───/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27 [...]
331            └───/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10 [...]
332            "
333        );
334        let hello_drv: NixQueryDrv =
335            "/nix/store/qy93dp4a3rqyn2mz63fbxjg228hffwyw-hello-2.10".into();
336        let glibc_drv: NixQueryDrv =
337            "/nix/store/pnd2kl27sag76h23wa5kl95a76n3k9i3-glibc-2.27".into();
338        let actual_tree = Tree::new(
339            NixQueryEntry(hello_drv.clone(), Recurse::No),
340            vec![
341                Tree::new(
342                    NixQueryEntry(glibc_drv.clone(), Recurse::No),
343                    vec![Tree::singleton(NixQueryEntry(
344                        glibc_drv,
345                        Recurse::Yes,
346                    ))],
347                ),
348                Tree::singleton(NixQueryEntry(hello_drv, Recurse::Yes)),
349            ],
350        );
351
352        let r = parse_nix_query_tree(raw_input);
353        assert_eq!(r, Ok(("", NixQueryTree(actual_tree))));
354    }
355}