metadata.rs 1.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. //! Utilities for working with cargo and rust files
  2. use std::error::Error;
  3. use std::{
  4. env,
  5. fmt::{Display, Formatter},
  6. fs,
  7. path::{Path, PathBuf},
  8. };
  9. #[derive(Debug, Clone)]
  10. pub struct CargoError {
  11. msg: String,
  12. }
  13. impl CargoError {
  14. pub fn new(msg: String) -> Self {
  15. Self { msg }
  16. }
  17. }
  18. impl Display for CargoError {
  19. fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
  20. write!(f, "CargoError: {}", self.msg)
  21. }
  22. }
  23. impl Error for CargoError {}
  24. /// How many parent folders are searched for a `Cargo.toml`
  25. const MAX_ANCESTORS: u32 = 10;
  26. /// Returns the root of the crate that the command is run from
  27. ///
  28. /// If the command is run from the workspace root, this will return the top-level Cargo.toml
  29. pub(crate) fn crate_root() -> Result<PathBuf, CargoError> {
  30. // From the current directory we work our way up, looking for `Cargo.toml`
  31. env::current_dir()
  32. .ok()
  33. .and_then(|mut wd| {
  34. for _ in 0..MAX_ANCESTORS {
  35. if contains_manifest(&wd) {
  36. return Some(wd);
  37. }
  38. if !wd.pop() {
  39. break;
  40. }
  41. }
  42. None
  43. })
  44. .ok_or_else(|| {
  45. CargoError::new("Failed to find directory containing Cargo.toml".to_string())
  46. })
  47. }
  48. /// Checks if the directory contains `Cargo.toml`
  49. fn contains_manifest(path: &Path) -> bool {
  50. fs::read_dir(path)
  51. .map(|entries| {
  52. entries
  53. .filter_map(Result::ok)
  54. .any(|ent| &ent.file_name() == "Cargo.toml")
  55. })
  56. .unwrap_or(false)
  57. }