Enhance label.rs to support implicit targets and expose absolute-ness. (#1046)
Co-authored-by: cfredric <cfredric@chromium.org>
diff --git a/util/label/label.rs b/util/label/label.rs
index e25770d..22d2bb4 100644
--- a/util/label/label.rs
+++ b/util/label/label.rs
@@ -13,6 +13,16 @@
let (input, repository_name) = consume_repository_name(input, label)?;
let (input, package_name) = consume_package_name(input, label)?;
let name = consume_name(input, label)?;
+ let name = match (package_name, name) {
+ (None, None) => {
+ return Err(LabelError(err(
+ label,
+ "labels must have a package and/or a name.",
+ )))
+ }
+ (Some(package_name), None) => name_from_package(package_name),
+ (_, Some(name)) => name,
+ };
Ok(Label::new(repository_name, package_name, name))
}
@@ -93,38 +103,40 @@
}
fn consume_package_name<'s>(input: &'s str, label: &'s str) -> Result<(&'s str, Option<&'s str>)> {
- let colon_pos = input.find(':');
- let start_pos;
- let mut is_absolute = false;
- if input.starts_with("//") {
- start_pos = 2;
- is_absolute = true;
- } else {
- start_pos = 0;
- if colon_pos.is_none() {
- if input.contains("//") {
- return Err(LabelError(err(
- label,
- "'//' cannot appear in the middle of the label.",
- )));
- }
- return Ok((input, None));
+ let is_absolute = match input.rfind("//") {
+ None => false,
+ Some(0) => true,
+ Some(_) => {
+ return Err(LabelError(err(
+ label,
+ "'//' cannot appear in the middle of the label.",
+ )));
}
};
- let (package_name, rest) = match colon_pos {
- Some(colon_pos) => (&input[start_pos..colon_pos], &input[colon_pos..]),
- None => (&input[start_pos..], ""),
+ let (package_name, rest) = match (is_absolute, input.find(':')) {
+ (false, colon_pos) if colon_pos.map_or(true, |pos| pos != 0) => {
+ return Err(LabelError(err(
+ label,
+ "relative packages are not permitted.",
+ )));
+ }
+ (_, colon_pos) => {
+ let (input, colon_pos) = if is_absolute {
+ (&input[2..], colon_pos.map(|cp| cp - 2))
+ } else {
+ (input, colon_pos)
+ };
+ match colon_pos {
+ Some(colon_pos) => (&input[0..colon_pos], &input[colon_pos..]),
+ None => (input, ""),
+ }
+ }
};
+
if package_name.is_empty() {
return Ok((rest, None));
}
- if package_name.contains("//") {
- return Err(LabelError(err(
- label,
- "'//' cannot appear in the middle of the label.",
- )));
- }
if !package_name.chars().all(|c| {
c.is_ascii_alphanumeric()
@@ -165,26 +177,31 @@
Ok((rest, Some(package_name)))
}
-fn consume_name<'s>(input: &'s str, label: &'s str) -> Result<&'s str> {
+fn consume_name<'s>(input: &'s str, label: &'s str) -> Result<Option<&'s str>> {
if input.is_empty() {
+ return Ok(None);
+ }
+ if input == ":" {
return Err(LabelError(err(label, "empty target name.")));
}
- let name = if let Some(stripped) = input.strip_prefix(':') {
- stripped
- } else {
- input
- };
- if name.is_empty() {
- return Err(LabelError(err(label, "empty target name.")));
- }
+ let name = input
+ .strip_prefix(':')
+ .or_else(|| input.strip_prefix('/'))
+ .unwrap_or(input);
if name.starts_with('/') {
return Err(LabelError(err(
label,
"target names may not start with '/'.",
)));
}
+ Ok(Some(name))
+}
- Ok(name)
+fn name_from_package(package_name: &str) -> &str {
+ package_name
+ .rsplit_once('/')
+ .map(|tup| tup.1)
+ .unwrap_or(package_name)
}
#[cfg(test)]
@@ -198,7 +215,7 @@
Label {
repository_name: Some("repo"),
package_name: Some("foo/bar"),
- name: "baz"
+ name: "baz",
}
);
}
@@ -209,6 +226,27 @@
assert_eq!(analyze("@//:foo")?.repository_name, None);
assert_eq!(analyze("//:foo")?.repository_name, None);
assert_eq!(analyze(":foo")?.repository_name, None);
+
+ assert_eq!(analyze("@repo//foo/bar")?.repository_name, Some("repo"));
+ assert_eq!(analyze("@//foo/bar")?.repository_name, None);
+ assert_eq!(analyze("//foo/bar")?.repository_name, None);
+ assert_eq!(
+ analyze("foo/bar"),
+ Err(LabelError(
+ "foo/bar must be a legal label; relative packages are not permitted.".to_string()
+ ))
+ );
+
+ assert_eq!(analyze("@repo//foo")?.repository_name, Some("repo"));
+ assert_eq!(analyze("@//foo")?.repository_name, None);
+ assert_eq!(analyze("//foo")?.repository_name, None);
+ assert_eq!(
+ analyze("foo"),
+ Err(LabelError(
+ "foo must be a legal label; relative packages are not permitted.".to_string()
+ ))
+ );
+
assert_eq!(
analyze("@foo:bar"),
Err(LabelError(
@@ -246,10 +284,21 @@
assert_eq!(analyze("//foo:baz/qux")?.package_name, Some("foo"));
assert_eq!(analyze("//foo/bar:baz/qux")?.package_name, Some("foo/bar"));
- assert_eq!(analyze("foo:baz/qux")?.package_name, Some("foo"));
- assert_eq!(analyze("foo/bar:baz/qux")?.package_name, Some("foo/bar"));
+ assert_eq!(
+ analyze("foo:baz/qux"),
+ Err(LabelError(
+ "foo:baz/qux must be a legal label; relative packages are not permitted."
+ .to_string()
+ ))
+ );
+ assert_eq!(
+ analyze("foo/bar:baz/qux"),
+ Err(LabelError(
+ "foo/bar:baz/qux must be a legal label; relative packages are not permitted."
+ .to_string()
+ ))
+ );
- assert_eq!(analyze("foo")?.package_name, None);
assert_eq!(analyze("//foo")?.package_name, Some("foo"));
assert_eq!(
@@ -260,12 +309,26 @@
))
);
assert_eq!(
+ analyze("//foo//bar"),
+ Err(LabelError(
+ "//foo//bar must be a legal label; '//' cannot appear in the middle of the label."
+ .to_string()
+ ))
+ );
+ assert_eq!(
analyze("foo//bar:baz"),
Err(LabelError(
"foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
.to_string()
))
);
+ assert_eq!(
+ analyze("//foo//bar:baz"),
+ Err(LabelError(
+ "//foo//bar:baz must be a legal label; '//' cannot appear in the middle of the label."
+ .to_string()
+ ))
+ );
assert_eq!(
analyze("//azAZ09/-. $()_:baz")?.package_name,
@@ -286,6 +349,24 @@
))
);
+ assert_eq!(analyze("@repo//foo/bar")?.package_name, Some("foo/bar"));
+ assert_eq!(analyze("//foo/bar")?.package_name, Some("foo/bar"));
+ assert_eq!(
+ analyze("foo/bar"),
+ Err(LabelError(
+ "foo/bar must be a legal label; relative packages are not permitted.".to_string()
+ ))
+ );
+
+ assert_eq!(analyze("@repo//foo")?.package_name, Some("foo"));
+ assert_eq!(analyze("//foo")?.package_name, Some("foo"));
+ assert_eq!(
+ analyze("foo"),
+ Err(LabelError(
+ "foo must be a legal label; relative packages are not permitted.".to_string()
+ ))
+ );
+
Ok(())
}
@@ -310,6 +391,24 @@
))
);
+ assert_eq!(analyze("@repo//foo/bar")?.name, "bar");
+ assert_eq!(analyze("//foo/bar")?.name, "bar");
+ assert_eq!(
+ analyze("foo/bar"),
+ Err(LabelError(
+ "foo/bar must be a legal label; relative packages are not permitted.".to_string()
+ ))
+ );
+
+ assert_eq!(analyze("@repo//foo")?.name, "foo");
+ assert_eq!(analyze("//foo")?.name, "foo");
+ assert_eq!(
+ analyze("foo"),
+ Err(LabelError(
+ "foo must be a legal label; relative packages are not permitted.".to_string()
+ ))
+ );
+
Ok(())
}