tidalcycles_rs/
install.rs1use std::cmp::Ordering;
2use std::fs;
3use std::process::Command;
4
5pub fn ensure_gh_installed() -> Option<std::path::PathBuf> {
6 if let Ok(path) = which::which("gh") {
7 Some(path)
8 } else if std::path::Path::new(r"C:\Program Files\GitHub CLI\gh.exe").exists() {
9 Some(std::path::PathBuf::from(
10 r"C:\Program Files\GitHub CLI\gh.exe",
11 ))
12 } else {
13 let status = Command::new("powershell")
15 .args([
16 "-Command",
17 "Start-Process winget -ArgumentList 'install --id=GitHub.cli -e --accept-source-agreements --accept-package-agreements' -Verb RunAs -Wait",
18 ])
19 .status();
20 match status {
21 Ok(s) if s.success() => {
22 if let Ok(path) = which::which("gh") {
23 Some(path)
24 } else if std::path::Path::new(r"C:\Program Files\GitHub CLI\gh.exe").exists() {
25 Some(std::path::PathBuf::from(
26 r"C:\Program Files\GitHub CLI\gh.exe",
27 ))
28 } else {
29 None
30 }
31 }
32 _ => None,
33 }
34 }
35}
36
37pub fn ensure_supercollider_installed() -> Option<std::path::PathBuf> {
38 if let Ok(path) = which::which("sclang") {
40 return Some(path);
41 }
42
43 let program_files =
45 std::env::var("ProgramFiles").unwrap_or_else(|_| r"C:\Program Files".to_string());
46 let sc_prefix = "SuperCollider-";
47 let mut latest_version: Option<(semver::Version, std::path::PathBuf)> = None;
48
49 if let Ok(entries) = fs::read_dir(&program_files) {
50 for entry in entries.flatten() {
51 let file_name = entry.file_name();
52 let file_name = file_name.to_string_lossy();
53 if file_name.starts_with(sc_prefix) {
54 let version_str = &file_name[sc_prefix.len()..];
55 if let Ok(version) = semver::Version::parse(version_str) {
56 let exe_path = entry.path().join("sclang.exe");
57 if exe_path.exists() {
58 match &latest_version {
59 Some((latest, _)) if version.cmp(latest) == Ordering::Greater => {
60 latest_version = Some((version, exe_path));
61 }
62 None => {
63 latest_version = Some((version, exe_path));
64 }
65 _ => {}
66 }
67 }
68 }
69 }
70 }
71 }
72
73 if let Some((_, path)) = latest_version {
74 return Some(path);
75 }
76
77 let status = Command::new("powershell")
79 .args([
80 "-Command",
81 "Start-Process winget -ArgumentList 'install --id=SuperCollider.SuperCollider -e --accept-source-agreements --accept-package-agreements' -Verb RunAs -Wait",
82 ])
83 .status();
84
85 match status {
86 Ok(s) if s.success() => {
87 if let Ok(path) = which::which("sclang") {
89 Some(path)
90 } else {
91 let mut latest_version: Option<(semver::Version, std::path::PathBuf)> = None;
93 if let Ok(entries) = fs::read_dir(&program_files) {
94 for entry in entries.flatten() {
95 let file_name = entry.file_name();
96 let file_name = file_name.to_string_lossy();
97 if file_name.starts_with(sc_prefix) {
98 let version_str = &file_name[sc_prefix.len()..];
99 if let Ok(version) = semver::Version::parse(version_str) {
100 let exe_path = entry.path().join("sclang.exe");
101 if exe_path.exists() {
102 match &latest_version {
103 Some((latest, _))
104 if version.cmp(latest) == Ordering::Greater =>
105 {
106 latest_version = Some((version, exe_path));
107 }
108 None => {
109 latest_version = Some((version, exe_path));
110 }
111 _ => {}
112 }
113 }
114 }
115 }
116 }
117 }
118 latest_version.map(|(_, path)| path)
119 }
120 }
121 _ => None,
122 }
123}
124
125pub fn ensure_ghcup_installed() -> Option<std::path::PathBuf> {
126 if let Ok(path) = which::which("ghcup") {
127 Some(path)
128 } else if std::path::Path::new(r"C:\ghcup\bin\ghcup.exe").exists() {
129 Some(std::path::PathBuf::from(r"C:\ghcup\bin\ghcup.exe"))
130 } else {
131 let ps_script = r#"Set-ExecutionPolicy Bypass -Scope Process -Force; [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.ServicePointManager]::SecurityProtocol -bor 3072; & ([ScriptBlock]::Create((Invoke-WebRequest https://www.haskell.org/ghcup/sh/bootstrap-haskell.ps1 -UseBasicParsing))) -Interactive -DisableCurl"#;
132 let status = Command::new("powershell")
133 .args([
134 "-Command",
135 &format!(
136 "Start-Process powershell -Verb runAs -ArgumentList '{}'",
137 ps_script.replace("'", "''")
138 ),
139 ])
140 .status();
141 match status {
142 Ok(s) if s.success() => {
143 if let Ok(path) = which::which("ghcup") {
144 Some(path)
145 } else if std::path::Path::new(r"C:\ghcup\bin\ghcup.exe").exists() {
146 Some(std::path::PathBuf::from(r"C:\ghcup\bin\ghcup.exe"))
147 } else {
148 None
149 }
150 }
151 _ => None,
152 }
153 }
154}
155
156pub fn ensure_cabal_installed() -> Option<std::path::PathBuf> {
157 if let Ok(path) = which::which("cabal") {
158 Some(path)
159 } else if std::path::Path::new(r"C:\ghcup\bin\cabal.exe").exists() {
160 Some(std::path::PathBuf::from(r"C:\ghcup\bin\cabal.exe"))
161 } else {
162 let _ = ensure_ghcup_installed();
164 if let Ok(path) = which::which("cabal") {
165 Some(path)
166 } else if std::path::Path::new(r"C:\ghcup\bin\cabal.exe").exists() {
167 Some(std::path::PathBuf::from(r"C:\ghcup\bin\cabal.exe"))
168 } else {
169 None
170 }
171 }
172}