git_spawn/parse/notes.rs
1//! Parser for `git notes list`.
2//!
3//! `git notes list` emits one `<note-object-sha> <annotated-object-sha>` pair
4//! per line (e.g. `5b8d8870... 04190777...`). This parser turns that into
5//! `(note_sha, object_sha)` tuples. It is permissive: blank lines are skipped
6//! and lines without a second field are ignored rather than erroring.
7
8/// Parse the output of `git notes list`.
9///
10/// Returns one `(note_sha, object_sha)` tuple per well-formed line. Lines that
11/// do not contain two whitespace-separated fields are skipped.
12///
13/// # Example
14/// ```
15/// use git_spawn::parse::parse_notes_list;
16/// let input = "5b8d8870 04190777\nabc123 def456\n";
17/// let pairs = parse_notes_list(input);
18/// assert_eq!(pairs.len(), 2);
19/// assert_eq!(pairs[0], ("5b8d8870".to_string(), "04190777".to_string()));
20/// assert_eq!(pairs[1].1, "def456");
21/// ```
22#[must_use]
23pub fn parse_notes_list(input: &str) -> Vec<(String, String)> {
24 input
25 .lines()
26 .filter_map(|line| {
27 let mut parts = line.split_whitespace();
28 let note = parts.next()?;
29 let object = parts.next()?;
30 Some((note.to_string(), object.to_string()))
31 })
32 .collect()
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 #[test]
40 fn parses_pairs() {
41 let input = "note1 obj1\nnote2 obj2\n";
42 let pairs = parse_notes_list(input);
43 assert_eq!(pairs.len(), 2);
44 assert_eq!(pairs[0], ("note1".to_string(), "obj1".to_string()));
45 assert_eq!(pairs[1], ("note2".to_string(), "obj2".to_string()));
46 }
47
48 #[test]
49 fn skips_blank_and_malformed_lines() {
50 let input = "\nnote1 obj1\nlonely\n";
51 let pairs = parse_notes_list(input);
52 assert_eq!(pairs.len(), 1);
53 assert_eq!(pairs[0].0, "note1");
54 }
55
56 #[test]
57 fn empty_input() {
58 assert!(parse_notes_list("").is_empty());
59 }
60}