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
//! What:
//! Exact repository path bytes as the operating system takes them,
//! and as text for messages.
//! Why:
//! Git allows any bytes in a file name.
//! The commit path keeps a path as `Vec<u8>` from Git's listing to Git's next argument,
//! so a name that is not UTF-8 reaches Git unchanged;
//! only a message for a person shows the text with replacement characters.
//!
//! In TS you'd write (pseudocode):
//! ```ts
//! // const argument = Buffer.from(bytes); const shown = bytes.toString('utf8');
//! ```
use std::ffi::OsString;
/// What:
/// Raw path bytes as an operating-system string for a command line or a file name.
/// `&[u8]` borrows the bytes;
/// the result owns a copy.
/// Why:
/// A Git path is bytes,
/// and only the platform decides how to pass them on:
/// Unix takes them as they are,
/// other platforms have text names, so the text with replacement characters is the closest name.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// Buffer.from(bytes)
/// ```
pub fn os_from_path_bytes(bytes: &[u8]) -> OsString {
#[cfg(unix)]
{
/// The Unix conversion from raw bytes.
use std::os::unix::ffi::OsStringExt;
return OsString::from_vec(bytes.to_vec());
}
#[cfg(not(unix))]
{
return OsString::from(String::from_utf8_lossy(bytes).into_owned());
}
}
/// What:
/// The readable text of path bytes for a message,
/// with each invalid sequence replaced by U+FFFD.
/// Why:
/// Messages are text,
/// and the exact bytes travel in the field next to them.
///
/// In TS you'd write (pseudocode):
/// ```ts
/// bytes.toString('utf8')
/// ```
pub fn path_message_text(bytes: &[u8]) -> String {
// `.into_owned()` copies the borrowed text only when no replacement was needed.
return String::from_utf8_lossy(bytes).into_owned();
}
/// Conversion controls stay out of the release executable.
#[cfg(test)]
#[path = "path_bytes_tests.rs"]
mod tests;