rustup.rs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. use crate::Result;
  2. use anyhow::Context;
  3. use std::path::PathBuf;
  4. use tokio::process::Command;
  5. #[derive(Debug, Default)]
  6. pub struct RustupShow {
  7. pub default_host: String,
  8. pub rustup_home: PathBuf,
  9. pub installed_toolchains: Vec<String>,
  10. pub installed_targets: Vec<String>,
  11. pub active_rustc: String,
  12. pub active_toolchain: String,
  13. }
  14. impl RustupShow {
  15. /// Collect the output of `rustup show` and parse it
  16. pub async fn from_cli() -> Result<RustupShow> {
  17. let output = Command::new("rustup").args(["show"]).output().await?;
  18. let stdout =
  19. String::from_utf8(output.stdout).context("Failed to parse rustup show output")?;
  20. Ok(RustupShow::from_stdout(stdout))
  21. }
  22. /// Parse the output of `rustup show`
  23. pub fn from_stdout(output: String) -> RustupShow {
  24. // I apologize for this hand-rolled parser
  25. let mut result = RustupShow::default();
  26. let mut current_section = "";
  27. for line in output.lines() {
  28. let line = line.trim();
  29. if line.is_empty() {
  30. continue;
  31. }
  32. if line.starts_with("Default host: ") {
  33. result.default_host = line.strip_prefix("Default host: ").unwrap().to_string();
  34. } else if line.starts_with("rustup home: ") {
  35. result.rustup_home =
  36. PathBuf::from(line.strip_prefix("rustup home: ").unwrap().trim());
  37. } else if line == "installed toolchains" {
  38. current_section = "toolchains";
  39. } else if line == "installed targets for active toolchain" {
  40. current_section = "targets";
  41. } else if line == "active toolchain" {
  42. current_section = "active_toolchain";
  43. } else {
  44. if line.starts_with("---") || line.is_empty() {
  45. continue;
  46. }
  47. match current_section {
  48. "toolchains" => result
  49. .installed_toolchains
  50. .push(line.trim_end_matches(" (default)").to_string()),
  51. "targets" => result.installed_targets.push(line.to_string()),
  52. "active_toolchain" => {
  53. if result.active_toolchain.is_empty() {
  54. result.active_toolchain = line.to_string();
  55. } else if line.starts_with("rustc ") {
  56. result.active_rustc = line.to_string();
  57. }
  58. }
  59. _ => {}
  60. }
  61. }
  62. }
  63. result
  64. }
  65. pub fn has_wasm32_unknown_unknown(&self) -> bool {
  66. self.installed_targets
  67. .contains(&"wasm32-unknown-unknown".to_string())
  68. }
  69. }
  70. #[test]
  71. fn parses_rustup_show() {
  72. let output = r#"
  73. Default host: aarch64-apple-darwin
  74. rustup home: /Users/jonkelley/.rustup
  75. installed toolchains
  76. --------------------
  77. stable-aarch64-apple-darwin (default)
  78. nightly-2021-07-06-aarch64-apple-darwin
  79. nightly-2021-09-24-aarch64-apple-darwin
  80. nightly-2022-03-10-aarch64-apple-darwin
  81. nightly-2023-03-18-aarch64-apple-darwin
  82. nightly-2024-01-11-aarch64-apple-darwin
  83. nightly-aarch64-apple-darwin
  84. 1.58.1-aarch64-apple-darwin
  85. 1.60.0-aarch64-apple-darwin
  86. 1.68.2-aarch64-apple-darwin
  87. 1.69.0-aarch64-apple-darwin
  88. 1.71.1-aarch64-apple-darwin
  89. 1.72.1-aarch64-apple-darwin
  90. 1.73.0-aarch64-apple-darwin
  91. 1.74.1-aarch64-apple-darwin
  92. 1.77.2-aarch64-apple-darwin
  93. 1.78.0-aarch64-apple-darwin
  94. 1.79.0-aarch64-apple-darwin
  95. 1.49-aarch64-apple-darwin
  96. 1.55-aarch64-apple-darwin
  97. 1.56-aarch64-apple-darwin
  98. 1.57-aarch64-apple-darwin
  99. 1.66-aarch64-apple-darwin
  100. 1.69-aarch64-apple-darwin
  101. 1.70-aarch64-apple-darwin
  102. 1.74-aarch64-apple-darwin
  103. installed targets for active toolchain
  104. --------------------------------------
  105. aarch64-apple-darwin
  106. aarch64-apple-ios
  107. aarch64-apple-ios-sim
  108. aarch64-linux-android
  109. aarch64-unknown-linux-gnu
  110. armv7-linux-androideabi
  111. i686-linux-android
  112. thumbv6m-none-eabi
  113. thumbv7em-none-eabihf
  114. wasm32-unknown-unknown
  115. x86_64-apple-darwin
  116. x86_64-apple-ios
  117. x86_64-linux-android
  118. x86_64-pc-windows-msvc
  119. x86_64-unknown-linux-gnu
  120. active toolchain
  121. ----------------
  122. stable-aarch64-apple-darwin (default)
  123. rustc 1.79.0 (129f3b996 2024-06-10)
  124. "#;
  125. let show = RustupShow::from_stdout(output.to_string());
  126. assert_eq!(show.default_host, "aarch64-apple-darwin");
  127. assert_eq!(show.rustup_home, PathBuf::from("/Users/jonkelley/.rustup"));
  128. assert_eq!(
  129. show.active_toolchain,
  130. "stable-aarch64-apple-darwin (default)"
  131. );
  132. assert_eq!(show.active_rustc, "rustc 1.79.0 (129f3b996 2024-06-10)");
  133. assert_eq!(show.installed_toolchains.len(), 26);
  134. assert_eq!(show.installed_targets.len(), 15);
  135. assert_eq!(
  136. show.installed_targets,
  137. vec![
  138. "aarch64-apple-darwin".to_string(),
  139. "aarch64-apple-ios".to_string(),
  140. "aarch64-apple-ios-sim".to_string(),
  141. "aarch64-linux-android".to_string(),
  142. "aarch64-unknown-linux-gnu".to_string(),
  143. "armv7-linux-androideabi".to_string(),
  144. "i686-linux-android".to_string(),
  145. "thumbv6m-none-eabi".to_string(),
  146. "thumbv7em-none-eabihf".to_string(),
  147. "wasm32-unknown-unknown".to_string(),
  148. "x86_64-apple-darwin".to_string(),
  149. "x86_64-apple-ios".to_string(),
  150. "x86_64-linux-android".to_string(),
  151. "x86_64-pc-windows-msvc".to_string(),
  152. "x86_64-unknown-linux-gnu".to_string(),
  153. ]
  154. )
  155. }