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(Option<Remote>),
 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(remote) => {
 38            if output.stderr.is_empty() {
 39                SuccessMessage {
 40                    message: "Already up to date".into(),
 41                    style: SuccessStyle::Toast,
 42                }
 43            } else {
 44                let message = match remote {
 45                    Some(remote) => format!("Synchronized with {}", remote.name),
 46                    None => "Synchronized with remotes".into(),
 47                };
 48                SuccessMessage {
 49                    message,
 50                    style: SuccessStyle::ToastWithLog { output },
 51                }
 52            }
 53        }
 54        RemoteAction::Pull(remote_ref) => {
 55            let get_changes = |output: &RemoteCommandOutput| -> anyhow::Result<u32> {
 56                let last_line = output
 57                    .stdout
 58                    .lines()
 59                    .last()
 60                    .context("Failed to get last line of output")?
 61                    .trim();
 62
 63                let files_changed = last_line
 64                    .split_whitespace()
 65                    .next()
 66                    .context("Failed to get first word of last line")?
 67                    .parse()?;
 68
 69                Ok(files_changed)
 70            };
 71
 72            if output.stderr.starts_with("Everything up to date") {
 73                SuccessMessage {
 74                    message: output.stderr.trim().to_owned(),
 75                    style: SuccessStyle::Toast,
 76                }
 77            } else if output.stdout.starts_with("Updating") {
 78                let files_changed = get_changes(&output).log_err();
 79                let message = if let Some(files_changed) = files_changed {
 80                    format!(
 81                        "Received {} file change{} from {}",
 82                        files_changed,
 83                        if files_changed == 1 { "" } else { "s" },
 84                        remote_ref.name
 85                    )
 86                } else {
 87                    format!("Fast forwarded from {}", remote_ref.name)
 88                };
 89                SuccessMessage {
 90                    message,
 91                    style: SuccessStyle::ToastWithLog { output },
 92                }
 93            } else if output.stdout.starts_with("Merge") {
 94                let files_changed = get_changes(&output).log_err();
 95                let message = if let Some(files_changed) = files_changed {
 96                    format!(
 97                        "Merged {} file change{} from {}",
 98                        files_changed,
 99                        if files_changed == 1 { "" } else { "s" },
100                        remote_ref.name
101                    )
102                } else {
103                    format!("Merged from {}", remote_ref.name)
104                };
105                SuccessMessage {
106                    message,
107                    style: SuccessStyle::ToastWithLog { output },
108                }
109            } else if output.stdout.contains("Successfully rebased") {
110                SuccessMessage {
111                    message: format!("Successfully rebased from {}", remote_ref.name),
112                    style: SuccessStyle::ToastWithLog { output },
113                }
114            } else {
115                SuccessMessage {
116                    message: format!("Successfully pulled from {}", remote_ref.name),
117                    style: SuccessStyle::ToastWithLog { output },
118                }
119            }
120        }
121        RemoteAction::Push(branch_name, remote_ref) => {
122            if output.stderr.contains("* [new branch]") {
123                let style = if output.stderr.contains("Create a pull request") {
124                    let finder = LinkFinder::new();
125                    let first_link = finder
126                        .links(&output.stderr)
127                        .filter(|link| *link.kind() == LinkKind::Url)
128                        .map(|link| link.start()..link.end())
129                        .next();
130                    if let Some(link) = first_link {
131                        let link = output.stderr[link].to_string();
132                        SuccessStyle::PushPrLink { link }
133                    } else {
134                        SuccessStyle::ToastWithLog { output }
135                    }
136                } else {
137                    SuccessStyle::ToastWithLog { output }
138                };
139                SuccessMessage {
140                    message: format!("Published {} to {}", branch_name, remote_ref.name),
141                    style,
142                }
143            } else if output.stderr.starts_with("Everything up to date") {
144                SuccessMessage {
145                    message: output.stderr.trim().to_owned(),
146                    style: SuccessStyle::Toast,
147                }
148            } else {
149                SuccessMessage {
150                    message: format!("Pushed {} to {}", branch_name, remote_ref.name),
151                    style: SuccessStyle::ToastWithLog { output },
152                }
153            }
154        }
155    }
156}