dev_kit/command/json/
difftool.rs1use crate::command::json::{DiffTool, JetbrainsIDE};
2use anyhow::anyhow;
3use itertools::Itertools;
4use std::path::Path;
5use std::process::{exit, Command};
6use std::str::FromStr;
7use strum::IntoEnumIterator;
8use which::which;
9
10impl FromStr for DiffTool {
11 type Err = anyhow::Error;
12
13 fn from_str(s: &str) -> Result<Self, Self::Err> {
14 let s = s.to_lowercase();
15 match s.as_str() {
16 "zed" => Ok(DiffTool::Zed),
17 "vscode" | "code" => Ok(DiffTool::VSCode),
18 val => JetbrainsIDE::iter().find(|it|
19 it.to_string().eq(val)
20 ).map(DiffTool::JetbrainsIDE).ok_or_else(||
21 anyhow!("invalid diff tool: {}", val)
22 )
23 }
24 }
25}
26
27impl Default for DiffTool {
28 fn default() -> Self {
29 let difftool = Self::list_available_diff_tools().into_iter()
30 .filter(|it| it.is_available())
31 .next();
32 if let Some(difftool) = difftool {
33 difftool
34 } else {
35 eprintln!("no diff tool available, you can install one of the following:");
36 for it in Self::iter() {
37 println!("{}", it.how_to_install())
38 }
39 exit(0)
40 }
41 }
42}
43
44impl DiffTool {
45 pub fn diff<L: AsRef<Path>, R: AsRef<Path>>(&self, left: L, right: R) -> crate::Result<()> {
46 let left = left.as_ref();
47 let right = right.as_ref();
48 match self {
49 DiffTool::JetbrainsIDE(ide) => {
50 let program = which(ide.to_string()).map_err(|_| anyhow!("JetbrainsIDE {} not found", ide))?;
51 let status = Command::new(program)
52 .arg("diff")
53 .arg(left.display().to_string())
54 .arg(right.display().to_string())
55 .status()
56 .map_err(|err| anyhow!(
57r#"
58failed to execute idea diff {} {}
59error: {}"#,
60 left.display(), right.display(), err
61 ))?;
62 if status.success() {
63 Ok(())
64 } else {
65 Err(anyhow!("idea diff command failed with status: {}", status))
66 }
67 }
68 DiffTool::Zed => {
69 let program = which("zed").map_err(|_| anyhow!("zed not found"))?;
70 let status = Command::new(program)
71 .arg("--diff")
72 .arg(left.display().to_string())
73 .arg(right.display().to_string())
74 .status()
75 .map_err(|err| anyhow!(
76r#"
77failed to execute zed --diff {} {}
78error: {}"#,
79 left.display(), right.display(), err
80 ))?;
81 if status.success() {
82 Ok(())
83 } else {
84 Err(anyhow!("zed --diff command failed with status: {}", status))
85 }
86 }
87 &DiffTool::VSCode => {
88 let program = which("code").or_else(|_|
89 which("vscode")
90 ).map_err(|_|
91 anyhow!("code/vscode not found")
92 )?;
93 let status = Command::new(program)
94 .arg("--diff")
95 .arg(left.display().to_string())
96 .arg(right.display().to_string())
97 .status()
98 .map_err(|err| anyhow!(
99r#"
100failed to execute code --diff {} {}
101error: {}"#,
102 left.display(), right.display(), err
103 ))?;
104 if status.success() {
105 Ok(())
106 } else {
107 Err(anyhow!("code --diff command failed with status: {}", status))
108 }
109 }
110 }
111 }
112
113 pub fn is_available(&self) -> bool {
114 match self {
115 DiffTool::JetbrainsIDE(ide) => which(ide.to_string()).is_ok(),
116 DiffTool::Zed => which("zed").is_ok(),
117 DiffTool::VSCode => which("code").is_ok() || which("vscode").is_ok(),
118 }
119 }
120
121 pub fn how_to_install(&self) -> String {
122 match self {
123 DiffTool::JetbrainsIDE(ide) => format!("https://www.jetbrains.com/help/{}/working-with-the-ide-features-from-command-line.html", ide.to_string()),
124 DiffTool::Zed => "https://zed.dev/docs/command-line-interface".to_string(),
125 DiffTool::VSCode => "https://code.visualstudio.com/docs/configure/command-line".to_string(),
126 }
127 }
128
129 pub fn list_available_diff_tools() -> Vec<DiffTool> {
130 vec![
131 JetbrainsIDE::iter().map(|it| Self::JetbrainsIDE(it)).collect_vec(),
132 vec![DiffTool::Zed, DiffTool::VSCode]
133 ].into_iter().flatten().filter(|it| it.is_available()).collect_vec()
134 }
135}
136
137#[cfg(test)]
138mod tests {
139 use super::*;
140
141 #[test]
142 fn test_list_available_diff_tools() {
143 let available_diff_tools = DiffTool::list_available_diff_tools();
144 for available_diff_tool in available_diff_tools {
145 println!("{}", available_diff_tool);
146 }
147 }
148}