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}