/crates/winrt-deps/src/cargo.rs

https://github.com/microsoft/winrt-rs · Rust · 141 lines · 119 code · 21 blank · 1 comment · 7 complexity · 9789400b95fda54d99501d0d6a9e73d8 MD5 · raw file

  1. use anyhow::Context;
  2. use thiserror::Error;
  3. use std::path::PathBuf;
  4. use std::process::{Command, Output, Stdio};
  5. use crate::error::{self, Error};
  6. use crate::manifest::Manifest;
  7. pub fn run() -> anyhow::Result<()> {
  8. Cargo::new()?.args(&["run"]).execute()
  9. }
  10. pub fn build() -> anyhow::Result<()> {
  11. Cargo::new()?.args(&["build"]).execute()
  12. }
  13. pub fn package_manifest() -> anyhow::Result<Manifest> {
  14. let path = package_manifest_path()?;
  15. let bytes = std::fs::read(&path).map_err(|_| Error::NoCargoToml)?;
  16. Ok(Manifest::from_slice(&bytes, path).map_err(|e| Error::MalformedManifest(e.into()))?)
  17. }
  18. pub(crate) fn metadata() -> anyhow::Result<Metadata> {
  19. let result = Cargo::new()?.args(&["metadata"]).output()?;
  20. if !result.status.success() {
  21. let err = String::from_utf8_lossy(&result.stderr);
  22. return if err.contains("package believes it's in a workspace")
  23. || err.contains("could not find `Cargo.toml`")
  24. {
  25. Err(CargoError::NotInWorkspace.into())
  26. } else {
  27. anyhow::bail!("{}", err)
  28. };
  29. }
  30. Ok(Metadata::parse(&result.stdout))
  31. }
  32. pub fn package_manifest_path() -> anyhow::Result<PathBuf> {
  33. let _ = metadata()?;
  34. let current =
  35. std::env::current_dir().context("failed to get current directory in search of manifest")?;
  36. let mut current = current.as_path();
  37. loop {
  38. let manifest = current.join("Cargo.toml");
  39. if manifest.exists() {
  40. return Ok(manifest);
  41. }
  42. current = current
  43. .parent()
  44. .expect("Current directory has no parent, but it must");
  45. }
  46. }
  47. pub fn workspace_target_path() -> anyhow::Result<PathBuf> {
  48. Ok(metadata()?.target_directory)
  49. }
  50. #[derive(Error, Debug)]
  51. pub enum CargoError {
  52. #[error("you are not currently in cargo workspace")]
  53. NotInWorkspace,
  54. }
  55. impl std::convert::From<CargoError> for error::Error {
  56. fn from(cargo_error: CargoError) -> Self {
  57. error::Error::CargoError(cargo_error)
  58. }
  59. }
  60. pub(crate) struct Metadata {
  61. target_directory: PathBuf,
  62. }
  63. impl Metadata {
  64. fn parse(data: &[u8]) -> Self {
  65. let value: serde_json::Value =
  66. serde_json::from_slice(&data).expect("`cargo metadata` did not return json.");
  67. let td = value
  68. .as_object()
  69. .expect("`cargo metadata` was not an object");
  70. let string = td
  71. .get("target_directory")
  72. .and_then(|td| td.as_str())
  73. .expect("`cargo metadata` 'target_directory' was not a string")
  74. .to_owned();
  75. Metadata {
  76. target_directory: PathBuf::from(string),
  77. }
  78. }
  79. }
  80. struct Cargo {
  81. command: Command,
  82. }
  83. impl Cargo {
  84. fn new() -> anyhow::Result<Self> {
  85. // TODO: check that cargo is installed and display nice error to user when not
  86. Ok(Self {
  87. command: Command::new("cargo"),
  88. })
  89. }
  90. fn args<I: IntoIterator<Item = S>, S: AsRef<std::ffi::OsStr>>(mut self, args: I) -> Self {
  91. self.command.args(args);
  92. self
  93. }
  94. fn output(mut self) -> anyhow::Result<Output> {
  95. Ok(self.command.output()?)
  96. }
  97. fn execute(mut self) -> anyhow::Result<()> {
  98. self.command.args(&["--color", "always"]);
  99. let output = self
  100. .command
  101. .stdout(Stdio::piped())
  102. .stderr(Stdio::piped())
  103. .spawn()?;
  104. let mut o = output
  105. .stdout
  106. .expect("Child process's stdout was not configured");
  107. let t1: std::thread::JoinHandle<anyhow::Result<()>> = std::thread::spawn(move || {
  108. let mut stdout = std::io::stdout();
  109. std::io::copy(&mut o, &mut stdout)?;
  110. Ok(())
  111. });
  112. let mut e = output
  113. .stderr
  114. .expect("Child process's stderr was not configured");
  115. let mut stdout = std::io::stderr();
  116. std::io::copy(&mut e, &mut stdout)?;
  117. t1.join().unwrap()?;
  118. Ok(())
  119. }
  120. }