hglib/commands/
summary.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this file,
3// You can obtain one at http://mozilla.org/MPL/2.0/.
4
5use std::collections::HashMap;
6
7use crate::client::{Client, HglibError, Runner};
8use crate::{runcommand, MkArg};
9
10pub struct Arg {
11    pub remote: bool,
12}
13
14impl Default for Arg {
15    fn default() -> Self {
16        Self { remote: false }
17    }
18}
19
20impl Arg {
21    fn run(&self, client: &mut Client) -> Result<(Vec<u8>, i32), HglibError> {
22        runcommand!(client, "summary", &[""], "--remote", self.remote)
23    }
24}
25
26#[derive(Debug, Default, PartialEq)]
27pub struct Revision {
28    pub rev: i64,
29    pub node: String,
30    pub tags: String,
31    pub message: String,
32}
33
34#[derive(Debug, Default, PartialEq)]
35pub struct Remote {
36    pub outgoing: u64,
37    pub incoming: u64,
38    pub outgoing_bookmarks: u64,
39    pub incoming_bookmarks: u64,
40}
41
42#[derive(Debug, Default, PartialEq)]
43pub struct Mq {
44    pub applied: u64,
45    pub unapplied: u64,
46}
47
48#[derive(Debug, Default, PartialEq)]
49pub struct Summary {
50    pub parent: Vec<Revision>,
51    pub branch: String,
52    pub commit: bool,
53    pub update: u32,
54    pub remote: Option<Remote>,
55    pub mq: Mq,
56    pub others: HashMap<String, String>,
57}
58
59impl Client {
60    pub fn summary(&mut self, x: Arg) -> Result<Summary, HglibError> {
61        let (data, _) = x.run(self)?;
62
63        let mut summary = Summary::default();
64        let mut wait_message = false;
65
66        for line in data.split(|x| *x == b'\n').filter(|x| !x.is_empty()) {
67            if wait_message {
68                let message = String::from_utf8(line[1..].to_vec()).unwrap();
69                summary.parent.last_mut().unwrap().message = message;
70                wait_message = false;
71                continue;
72            }
73
74            let i = line.iter().position(|x| *x == b':').unwrap();
75            let name = &line[..i];
76            let value = &line[i + 2..];
77
78            match name {
79                b"parent" => {
80                    let iter = &mut value.iter();
81                    let first = iter.next().unwrap();
82                    let (neg, init) = if *first == b'-' {
83                        (true, 0)
84                    } else {
85                        (false, i64::from(*first - b'0'))
86                    };
87
88                    let rev = iter
89                        .take_while(|x| **x != b':')
90                        .fold(init, |r, x| r * 10 + i64::from(*x - b'0'));
91                    let rev = if neg { -rev } else { rev };
92
93                    let node = iter
94                        .take_while(|x| **x != b' ')
95                        .map(|x| *x as char)
96                        .collect::<String>();
97
98                    let mut tags = iter.map(|x| *x as char).collect::<String>();
99                    if !tags.is_empty() {
100                        let empty = " (empty repository)";
101                        if tags.ends_with(empty) {
102                            tags.replace_range(tags.len() - empty.len().., "")
103                        }
104                    }
105
106                    summary.parent.push(Revision {
107                        rev,
108                        node,
109                        tags,
110                        message: "".to_string(),
111                    });
112
113                    wait_message = rev != -1;
114                }
115                b"branch" => {
116                    summary.branch = String::from_utf8(value.to_vec()).unwrap();
117                }
118                b"commit" => {
119                    let clean = b"(clean)";
120                    summary.commit = line.windows(clean.len()).any(|w| w == clean);
121                }
122                b"update" => {
123                    summary.update = if value == b"(current)" {
124                        0
125                    } else {
126                        value
127                            .iter()
128                            .take_while(|x| **x != b' ')
129                            .fold(0, |r, x| r * 10 + u32::from(*x - b'0'))
130                    };
131                }
132                b"remote" => {
133                    if !x.remote {
134                        continue;
135                    }
136
137                    summary.remote = Some(if value == b"(synced)" {
138                        Remote::default()
139                    } else {
140                        let mut rem = Remote::default();
141                        let iter = &mut value.iter();
142                        loop {
143                            let n = iter
144                                .take_while(|x| **x != b' ')
145                                .fold(0, |r, x| r * 10 + u64::from(*x - b'0'));
146
147                            let typ: Vec<_> = iter.take_while(|x| **x != b',').copied().collect();
148                            match typ.as_slice() {
149                                b"outgoing" => {
150                                    rem.outgoing = n;
151                                }
152                                b"outgoing bookmarks" => {
153                                    rem.outgoing_bookmarks = n;
154                                }
155                                b"incoming bookmarks" => {
156                                    rem.incoming_bookmarks = n;
157                                }
158                                _ => {
159                                    if typ.ends_with(b"incoming") {
160                                        rem.incoming = n;
161                                    }
162                                }
163                            }
164
165                            if iter.next().is_none() {
166                                break;
167                            }
168                        }
169                        rem
170                    });
171                }
172                b"mq" => {}
173                _ => {
174                    let name = String::from_utf8(name.to_vec()).unwrap();
175                    let value = String::from_utf8(value.to_vec()).unwrap();
176
177                    summary.others.insert(name, value);
178                }
179            }
180        }
181
182        Ok(summary)
183    }
184}