remote_output.rs

  1use anyhow::Context as _;
  2use git::repository::{Remote, RemoteCommandOutput};
  3use linkify::{LinkFinder, LinkKind};
  4use ui::SharedString;
  5use util::ResultExt as _;
  6
  7#[derive(Clone)]
  8pub enum RemoteAction {
  9    Fetch,
 10    Pull(Remote),
 11    Push(SharedString, Remote),
 12}
 13
 14impl RemoteAction {
 15    pub fn name(&self) -> &'static str {
 16        match self {
 17            RemoteAction::Fetch => "fetch",
 18            RemoteAction::Pull(_) => "pull",
 19            RemoteAction::Push(_, _) => "push",
 20        }
 21    }
 22}
 23
 24pub enum SuccessStyle {
 25    Toast,
 26    ToastWithLog { output: RemoteCommandOutput },
 27    PushPrLink { link: String },
 28}
 29
 30pub struct SuccessMessage {
 31    pub message: String,
 32    pub style: SuccessStyle,
 33}
 34
 35pub fn format_output(action: &RemoteAction, output: RemoteCommandOutput) -> SuccessMessage {
 36    match action {
 37        RemoteAction::Fetch => {
 38            if output.stderr.is_empty() {
 39                SuccessMessage {
 40                    message: "Already up to date".into(),
 41                    style: SuccessStyle::Toast,
 42                }
 43            } else {
 44                SuccessMessage {
 45                    message: "Synchronized with remotes".into(),
 46                    style: SuccessStyle::ToastWithLog { output },
 47                }
 48            }
 49        }
 50        RemoteAction::Pull(remote_ref) => {
 51            let get_changes = |output: &RemoteCommandOutput| -> anyhow::Result<u32> {
 52                let last_line = output
 53                    .stdout
 54                    .lines()
 55                    .last()
 56                    .context("Failed to get last line of output")?
 57                    .trim();
 58
 59                let files_changed = last_line
 60                    .split_whitespace()
 61                    .next()
 62                    .context("Failed to get first word of last line")?
 63                    .parse()?;
 64
 65                Ok(files_changed)
 66            };
 67
 68            if output.stderr.starts_with("Everything up to date") {
 69                SuccessMessage {
 70                    message: output.stderr.trim().to_owned(),
 71                    style: SuccessStyle::Toast,
 72                }
 73            } else if output.stdout.starts_with("Updating") {
 74                let files_changed = get_changes(&output).log_err();
 75                let message = if let Some(files_changed) = files_changed {
 76                    format!(
 77                        "Received {} file change{} from {}",
 78                        files_changed,
 79                        if files_changed == 1 { "" } else { "s" },
 80                        remote_ref.name
 81                    )
 82                } else {
 83                    format!("Fast forwarded from {}", remote_ref.name)
 84                };
 85                SuccessMessage {
 86                    message,
 87                    style: SuccessStyle::ToastWithLog { output },
 88                }
 89            } else if output.stdout.starts_with("Merge") {
 90                let files_changed = get_changes(&output).log_err();
 91                let message = if let Some(files_changed) = files_changed {
 92                    format!(
 93                        "Merged {} file change{} from {}",
 94                        files_changed,
 95                        if files_changed == 1 { "" } else { "s" },
 96                        remote_ref.name
 97                    )
 98                } else {
 99                    format!("Merged from {}", remote_ref.name)
100                };
101                SuccessMessage {
102                    message,
103                    style: SuccessStyle::ToastWithLog { output },
104                }
105            } else if output.stdout.contains("Successfully rebased") {
106                SuccessMessage {
107                    message: format!("Successfully rebased from {}", remote_ref.name),
108                    style: SuccessStyle::ToastWithLog { output },
109                }
110            } else {
111                SuccessMessage {
112                    message: format!("Successfully pulled from {}", remote_ref.name),
113                    style: SuccessStyle::ToastWithLog { output },
114                }
115            }
116        }
117        RemoteAction::Push(branch_name, remote_ref) => {
118            if output.stderr.contains("* [new branch]") {
119                let style = if output.stderr.contains("Create a pull request") {
120                    let finder = LinkFinder::new();
121                    let first_link = finder
122                        .links(&output.stderr)
123                        .filter(|link| *link.kind() == LinkKind::Url)
124                        .map(|link| link.start()..link.end())
125                        .next();
126                    if let Some(link) = first_link {
127                        let link = output.stderr[link].to_string();
128                        SuccessStyle::PushPrLink { link }
129                    } else {
130                        SuccessStyle::ToastWithLog { output }
131                    }
132                } else {
133                    SuccessStyle::ToastWithLog { output }
134                };
135                SuccessMessage {
136                    message: format!("Published {} to {}", branch_name, remote_ref.name),
137                    style,
138                }
139            } else if output.stderr.starts_with("Everything up to date") {
140                SuccessMessage {
141                    message: output.stderr.trim().to_owned(),
142                    style: SuccessStyle::Toast,
143                }
144            } else {
145                SuccessMessage {
146                    message: format!("Pushed {} to {}", branch_name, remote_ref.name),
147                    style: SuccessStyle::ToastWithLog { output },
148                }
149            }
150        }
151    }
152}