Squashed 'third_party/rules_rust/' content from commit bf59038ca

git-subtree-dir: third_party/rules_rust
git-subtree-split: bf59038cac11798cbaef9f3bf965bad8182b97fa
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I5a20e403203d670df467ea97dde9a4ac40339a8d
diff --git a/crate_universe/src/utils/starlark/label.rs b/crate_universe/src/utils/starlark/label.rs
new file mode 100644
index 0000000..1716944
--- /dev/null
+++ b/crate_universe/src/utils/starlark/label.rs
@@ -0,0 +1,317 @@
+use std::fmt::{self, Display};
+use std::path::Path;
+use std::str::FromStr;
+
+use anyhow::{anyhow, bail, Context, Result};
+use regex::Regex;
+use serde::de::Visitor;
+use serde::{Deserialize, Serialize, Serializer};
+
+#[derive(Debug, Default, PartialEq, Eq, PartialOrd, Ord, Clone)]
+pub struct Label {
+    pub repository: Option<String>,
+    pub package: Option<String>,
+    pub target: String,
+}
+
+impl FromStr for Label {
+    type Err = anyhow::Error;
+
+    fn from_str(s: &str) -> Result<Self, Self::Err> {
+        let re = Regex::new(r"^(@[\w\d\-_\.]*)?/{0,2}([\w\d\-_\./]+)?:?([\+\w\d\-_\./]+)$")?;
+        let cap = re
+            .captures(s)
+            .with_context(|| format!("Failed to parse label from string: {}", s))?;
+
+        let repository = cap
+            .get(1)
+            .map(|m| m.as_str().trim_start_matches('@').to_owned());
+        let package = cap.get(2).map(|m| m.as_str().to_owned());
+        let mut target = cap.get(3).map(|m| m.as_str().to_owned());
+
+        if target.is_none() {
+            if let Some(pkg) = &package {
+                target = Some(pkg.clone());
+            } else if let Some(repo) = &repository {
+                target = Some(repo.clone())
+            } else {
+                bail!("The label is missing a label")
+            }
+        }
+
+        // The target should be set at this point
+        let target = target.unwrap();
+
+        Ok(Self {
+            repository,
+            package,
+            target,
+        })
+    }
+}
+
+impl Display for Label {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        let mut label = String::new();
+
+        // Add the repository
+        if let Some(repo) = &self.repository {
+            label = format!("@{}", repo);
+        }
+
+        // Add the package
+        if let Some(pkg) = &self.package {
+            label = format!("{}//{}", label, pkg);
+        }
+
+        write!(f, "{}:{}", &label, &self.target,)
+    }
+}
+
+impl Label {
+    /// Generates a label appropriate for the passed Path by walking the filesystem to identify its
+    /// workspace and package.
+    pub fn from_absolute_path(p: &Path) -> Result<Self, anyhow::Error> {
+        let mut workspace_root = None;
+        let mut package_root = None;
+        for ancestor in p.ancestors().skip(1) {
+            if package_root.is_none()
+                && (ancestor.join("BUILD").exists() || ancestor.join("BUILD.bazel").exists())
+            {
+                package_root = Some(ancestor);
+            }
+            if workspace_root.is_none()
+                && (ancestor.join("WORKSPACE").exists()
+                    || ancestor.join("WORKSPACE.bazel").exists())
+            {
+                workspace_root = Some(ancestor);
+                break;
+            }
+        }
+        match (workspace_root, package_root) {
+            (Some(workspace_root), Some(package_root)) => {
+                // These unwraps are safe by construction of the ancestors and prefix calls which set up these paths.
+                let target = p.strip_prefix(package_root).unwrap();
+                let workspace_relative = p.strip_prefix(workspace_root).unwrap();
+                let mut package_path = workspace_relative.to_path_buf();
+                for _ in target.components() {
+                    package_path.pop();
+                }
+
+                let package = if package_path.components().count() > 0 {
+                    Some(path_to_label_part(&package_path)?)
+                } else {
+                    None
+                };
+                let target = path_to_label_part(target)?;
+
+                Ok(Label {
+                    repository: None,
+                    package,
+                    target,
+                })
+            }
+            (Some(_workspace_root), None) => {
+                bail!(
+                    "Could not identify package for path {}. Maybe you need to add a BUILD.bazel file.",
+                    p.display()
+                );
+            }
+            _ => {
+                bail!("Could not identify workspace for path {}", p.display());
+            }
+        }
+    }
+}
+
+/// Converts a path to a forward-slash-delimited label-appropriate path string.
+fn path_to_label_part(path: &Path) -> Result<String, anyhow::Error> {
+    let components: Result<Vec<_>, _> = path
+        .components()
+        .map(|c| {
+            c.as_os_str().to_str().ok_or_else(|| {
+                anyhow!(
+                    "Found non-UTF8 component turning path into label: {}",
+                    path.display()
+                )
+            })
+        })
+        .collect();
+    Ok(components?.join("/"))
+}
+
+impl Serialize for Label {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: Serializer,
+    {
+        serializer.serialize_str(&self.repr())
+    }
+}
+
+struct LabelVisitor;
+impl<'de> Visitor<'de> for LabelVisitor {
+    type Value = Label;
+
+    fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
+        formatter.write_str("Expected string value of `{name} {version}`.")
+    }
+
+    fn visit_str<E>(self, v: &str) -> Result<Self::Value, E>
+    where
+        E: serde::de::Error,
+    {
+        Label::from_str(v).map_err(E::custom)
+    }
+}
+
+impl<'de> Deserialize<'de> for Label {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: serde::Deserializer<'de>,
+    {
+        deserializer.deserialize_str(LabelVisitor)
+    }
+}
+
+impl Label {
+    pub fn repr(&self) -> String {
+        self.to_string()
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use spectral::prelude::*;
+    use std::fs::{create_dir_all, File};
+    use tempfile::tempdir;
+
+    #[test]
+    fn full_label() {
+        let label = Label::from_str("@repo//package/sub_package:target").unwrap();
+        assert_eq!(label.repository.unwrap(), "repo");
+        assert_eq!(label.package.unwrap(), "package/sub_package");
+        assert_eq!(label.target, "target");
+    }
+
+    #[test]
+    fn no_repository() {
+        let label = Label::from_str("//package:target").unwrap();
+        assert_eq!(label.repository, None);
+        assert_eq!(label.package.unwrap(), "package");
+        assert_eq!(label.target, "target");
+    }
+
+    #[test]
+    fn no_slashes() {
+        let label = Label::from_str("package:target").unwrap();
+        assert_eq!(label.repository, None);
+        assert_eq!(label.package.unwrap(), "package");
+        assert_eq!(label.target, "target");
+    }
+
+    #[test]
+    fn root_label() {
+        let label = Label::from_str("@repo//:target").unwrap();
+        assert_eq!(label.repository.unwrap(), "repo");
+        assert_eq!(label.package, None);
+        assert_eq!(label.target, "target");
+    }
+
+    #[test]
+    fn root_label_no_repository() {
+        let label = Label::from_str("//:target").unwrap();
+        assert_eq!(label.repository, None);
+        assert_eq!(label.package, None);
+        assert_eq!(label.target, "target");
+    }
+
+    #[test]
+    fn root_label_no_slashes() {
+        let label = Label::from_str(":target").unwrap();
+        assert_eq!(label.repository, None);
+        assert_eq!(label.package, None);
+        assert_eq!(label.target, "target");
+    }
+
+    #[test]
+    fn full_label_with_slash_after_colon() {
+        let label = Label::from_str("@repo//package/sub_package:subdir/target").unwrap();
+        assert_eq!(label.repository.unwrap(), "repo");
+        assert_eq!(label.package.unwrap(), "package/sub_package");
+        assert_eq!(label.target, "subdir/target");
+    }
+
+    #[test]
+    fn invalid_double_colon() {
+        assert!(Label::from_str("::target").is_err());
+    }
+
+    #[test]
+    fn invalid_double_at() {
+        assert!(Label::from_str("@@repo//pkg:target").is_err());
+    }
+
+    #[test]
+    #[ignore = "This currently fails. The Label parsing logic needs to be updated"]
+    fn invalid_no_double_slash() {
+        assert!(Label::from_str("@repo:target").is_err());
+    }
+
+    #[test]
+    fn from_absolute_path_exists() {
+        let dir = tempdir().unwrap();
+        let workspace = dir.path().join("WORKSPACE.bazel");
+        let build_file = dir.path().join("parent").join("child").join("BUILD.bazel");
+        let subdir = dir.path().join("parent").join("child").join("grandchild");
+        let actual_file = subdir.join("greatgrandchild");
+        create_dir_all(subdir).unwrap();
+        {
+            File::create(&workspace).unwrap();
+            File::create(&build_file).unwrap();
+            File::create(&actual_file).unwrap();
+        }
+        let label = Label::from_absolute_path(&actual_file).unwrap();
+        assert_eq!(label.repository, None);
+        assert_eq!(label.package.unwrap(), "parent/child");
+        assert_eq!(label.target, "grandchild/greatgrandchild")
+    }
+
+    #[test]
+    fn from_absolute_path_no_workspace() {
+        let dir = tempdir().unwrap();
+        let build_file = dir.path().join("parent").join("child").join("BUILD.bazel");
+        let subdir = dir.path().join("parent").join("child").join("grandchild");
+        let actual_file = subdir.join("greatgrandchild");
+        create_dir_all(subdir).unwrap();
+        {
+            File::create(&build_file).unwrap();
+            File::create(&actual_file).unwrap();
+        }
+        let err = Label::from_absolute_path(&actual_file)
+            .unwrap_err()
+            .to_string();
+        assert_that(&err).contains("Could not identify workspace");
+        assert_that(&err).contains(format!("{}", actual_file.display()).as_str());
+    }
+
+    #[test]
+    fn from_absolute_path_no_build_file() {
+        let dir = tempdir().unwrap();
+        let workspace = dir.path().join("WORKSPACE.bazel");
+        let subdir = dir.path().join("parent").join("child").join("grandchild");
+        let actual_file = subdir.join("greatgrandchild");
+        create_dir_all(subdir).unwrap();
+        {
+            File::create(&workspace).unwrap();
+            File::create(&actual_file).unwrap();
+        }
+        let err = Label::from_absolute_path(&actual_file)
+            .unwrap_err()
+            .to_string();
+        assert_that(&err).contains("Could not identify package");
+        assert_that(&err).contains("Maybe you need to add a BUILD.bazel file");
+        assert_that(&err).contains(format!("{}", actual_file.display()).as_str());
+    }
+}