1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
//! What:
//! Random identifiers in the layout of the incumbent's `randomUUID`:
//! lock tokens and the unique names of worktree-copy and auto-push scratch files,
//! and the check for that layout.
//! Why:
//! A lock token must be unguessable and unique per acquisition,
//! and the startup step recognizes the directories an earlier release named with a transaction ID in the same canonical
//! layout (`is_canonical_uuid`, `startup_gate.rs`).
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! import { randomUUID } from 'node:crypto'; const token = randomUUID();
//! ```
/// What:
/// Why no identifier could be made:
/// the operating system's random source failed.
/// Why:
/// Without randomness no unguessable token exists,
/// so the caller must stop.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// class RandomSourceError extends Error {}
/// ```
#[derive(Debug)]
pub struct RandomSourceError(pub String);
/// Lowercase hexadecimal digits,
/// the alphabet `randomUUID` prints.
const HEX_DIGITS: &[u8; 16] = b"0123456789abcdef";
/// What:
/// Format 16 bytes as a version-4 UUID in canonical lowercase layout.
/// `[u8; 16]` is a fixed array of sixteen bytes.
/// Why:
/// RFC 9562 version 4:
/// the version nibble is 4 and the variant bits are `10`,
/// exactly
/// what `randomUUID` produces;
/// the formatting is pure so tests can fix the input.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function formatUuid(bytes: Uint8Array): string;
/// ```
pub fn format_uuid_v4(bytes: [u8; 16]) -> String {
// `mut` allows setting the version and variant bits in place.
let mut value: [u8; 16] = bytes;
value[6] = (value[6] & 0x0f) | 0x40;
value[8] = (value[8] & 0x3f) | 0x80;
let mut text: String = String::with_capacity(36);
for (index, byte) in value.iter().enumerate() {
if index == 4 || index == 6 || index == 8 || index == 10 {
text.push('-');
}
// Each byte is two digits: the high nibble, then the low nibble.
text.push(char::from(HEX_DIGITS[usize::from(byte >> 4)]));
text.push(char::from(HEX_DIGITS[usize::from(byte & 0x0f)]));
}
return text;
}
/// What:
/// A fresh random version-4 UUID from the operating system's source.
/// `Result<String, RandomSourceError>` is "the identifier,
/// or why not".
/// Why:
/// Tokens and transaction IDs must not be predictable or collide between processes.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// function randomUuid(): string { return randomUUID(); }
/// ```
pub fn random_uuid() -> Result<String, RandomSourceError> {
// `mut` allows the random source to fill the buffer.
let mut bytes: [u8; 16] = [0; 16];
// `map_err` turns the source's error into this module's error, keeping its text.
getrandom::fill(&mut bytes).map_err(random_source_error)?;
return Ok(format_uuid_v4(bytes));
}
/// What:
/// Describe a random-source failure.
/// Why:
/// A named function keeps the conversion out of a closure.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// const randomSourceError = (error: Error) => new RandomSourceError(String(error));
/// ```
fn random_source_error(error: getrandom::Error) -> RandomSourceError {
return RandomSourceError(format!(
"the operating system's random source failed: {error}"
));
}
/// What:
/// Whether a name is exactly one canonical lowercase UUID, as `randomUUID` prints it.
/// Why:
/// The scratch directories of a commit are named by such an identifier, and a path is recognized as one of them only by that
/// layout.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/u.test(name)
/// ```
pub fn is_canonical_uuid(name: &str) -> bool {
let bytes: &[u8] = name.as_bytes();
if bytes.len() != 36 {
return false;
}
for (index, byte) in bytes.iter().enumerate() {
let separator: bool = index == 8 || index == 13 || index == 18 || index == 23;
let valid: bool = if separator {
*byte == b'-'
} else {
byte.is_ascii_digit() || (b'a'..=b'f').contains(byte)
};
if !valid {
return false;
}
}
return true;
}
/// Formatting controls stay out of the release executable.
#[cfg(test)]
#[path = "random_id_tests.rs"]
mod tests;