Skip to content

Commit 9c41b5c

Browse files
dulinrileyfacebook-github-bot
authored andcommitted
Fix Reference::from_str v1::Name::Suffixed (#1364)
Summary: Pull Request resolved: #1364 Fixes #1352 The v1::Name format with a random uuid included a hyphen character that wasn't parseable as a rust identifier. This is used by the "Reference" parsing logic for a fully qualified world + proc + actor name. Change to underscore to fix this, as I don't think the choice of hyphen has any other special meaning. Reviewed By: mariusae Differential Revision: D83495465 fbshipit-source-id: 6992d9aeb57428d04ebc6fc9a6e24b8dddd116ca
1 parent c3e0fb0 commit 9c41b5c

File tree

2 files changed

+60
-3
lines changed

2 files changed

+60
-3
lines changed

hyperactor/src/reference.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,16 @@ mod tests {
13861386
)
13871387
.into(),
13881388
),
1389+
(
1390+
// References with v1::Name::Suffixed for actor names are parseable
1391+
"test[234].testactor_12345[6]",
1392+
ActorId(
1393+
ProcId::Ranked(WorldId("test".into()), 234),
1394+
"testactor_12345".into(),
1395+
6,
1396+
)
1397+
.into(),
1398+
),
13891399
];
13901400

13911401
for (s, expected) in cases {
@@ -1395,7 +1405,12 @@ mod tests {
13951405

13961406
#[test]
13971407
fn test_reference_parse_error() {
1398-
let cases: Vec<&str> = vec!["(blah)", "world(1, 2, 3)"];
1408+
let cases: Vec<&str> = vec![
1409+
"(blah)",
1410+
"world(1, 2, 3)",
1411+
// hyphen is not allowed in actor name
1412+
"test[234].testactor-12345[6]",
1413+
];
13991414

14001415
for s in cases {
14011416
let result: Result<Reference, ReferenceParsingError> = s.parse();

hyperactor_mesh/src/v1.rs

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,11 @@ pub enum Name {
177177
Reserved(String),
178178
}
179179

180+
// The delimiter between the name and the uuid when a Name::Suffixed is stringified.
181+
// Actor names must be parseable as a Rust identifier, so this delimiter must be
182+
// something that is part of a valid Rust identifier.
183+
static NAME_SUFFIX_DELIMITER: &str = "_";
184+
180185
impl Name {
181186
/// Create a new `Name` from a user-provided base name.
182187
pub fn new(name: impl Into<String>) -> Self {
@@ -238,7 +243,7 @@ impl FromStr for Name {
238243
type Err = NameParseError;
239244

240245
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
241-
if let Some((name, uuid)) = s.split_once('-') {
246+
if let Some((name, uuid)) = s.split_once(NAME_SUFFIX_DELIMITER) {
242247
if name.is_empty() {
243248
return Err(NameParseError::MissingName);
244249
}
@@ -260,7 +265,7 @@ impl std::fmt::Display for Name {
260265
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
261266
match self {
262267
Self::Suffixed(n, uuid) => {
263-
write!(f, "{}-", n)?;
268+
write!(f, "{}{}", n, NAME_SUFFIX_DELIMITER)?;
264269
uuid.format(f, true /*raw*/)
265270
}
266271
Self::Reserved(n) => write!(f, "{}", n),
@@ -281,7 +286,44 @@ mod tests {
281286

282287
#[test]
283288
fn test_name_roundtrip() {
289+
let uuid = "111111111111".parse::<ShortUuid>().unwrap();
290+
let name = Name::new_with_uuid("foo", Some(uuid));
291+
let str = name.to_string();
292+
assert_eq!(str, "foo_111111111111");
293+
assert_eq!(name, Name::from_str(&str).unwrap());
294+
}
295+
296+
#[test]
297+
fn test_name_roundtrip_with_underscore() {
298+
// A ShortUuid may have an underscore prefix if the first character is a digit.
299+
// Make sure this doesn't impact parsing.
300+
let uuid = "_1a2b3c4d5e6f".parse::<ShortUuid>().unwrap();
301+
let name = Name::new_with_uuid("foo", Some(uuid));
302+
let str = name.to_string();
303+
// Leading underscore is stripped as not needed.
304+
assert_eq!(str, "foo_1a2b3c4d5e6f");
305+
assert_eq!(name, Name::from_str(&str).unwrap());
306+
}
307+
308+
#[test]
309+
fn test_name_roundtrip_random() {
284310
let name = Name::new("foo");
285311
assert_eq!(name, Name::from_str(&name.to_string()).unwrap());
286312
}
313+
314+
#[test]
315+
fn test_name_roundtrip_reserved() {
316+
let name = Name::new_reserved("foo");
317+
let str = name.to_string();
318+
assert_eq!(str, "foo");
319+
assert_eq!(name, Name::from_str(&str).unwrap());
320+
}
321+
322+
#[test]
323+
fn test_name_parse() {
324+
// Multiple underscores are allowed in the name, as ShortUuid will discard
325+
// them.
326+
let name = Name::from_str("foo__1a2b3c4_d5e6f").unwrap();
327+
assert_eq!(format!("{}", name), "foo_1a2b3c4d5e6f");
328+
}
287329
}

0 commit comments

Comments
 (0)