Get the path type of a Win32 namespace path.
Similar to RtlDetermineDosPathNameType_U.
If T is u16, then path should be encoded as WTF-16LE.
pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType
pub fn getWin32PathType(comptime T: type, path: []const T) Win32PathType {
if (path.len < 1) return .relative;
const windows_path = std.fs.path.PathType.windows;
if (windows_path.isSep(T, path[0])) {
// \x
if (path.len < 2 or !windows_path.isSep(T, path[1])) return .rooted;
// \\. or \\?
if (path.len > 2 and (path[2] == mem.nativeToLittle(T, '.') or path[2] == mem.nativeToLittle(T, '?'))) {
// exactly \\. or \\? with nothing trailing
if (path.len == 3) return .root_local_device;
// \\.\x or \\?\x
if (windows_path.isSep(T, path[3])) return .local_device;
}
// \\x
return .unc_absolute;
} else {
// Some choice has to be made about how non-ASCII code points as drive-letters are handled, since
// path[0] is a different size for WTF-16 vs WTF-8, leading to a potential mismatch in classification
// for a WTF-8 path and its WTF-16 equivalent. For example, `€:\` encoded in WTF-16 is three code
// units `<0x20AC>:\` whereas `€:\` encoded as WTF-8 is 6 code units `<0xE2><0x82><0xAC>:\` so
// checking path[0], path[1] and path[2] would not behave the same between WTF-8/WTF-16.
//
// `RtlDetermineDosPathNameType_U` exclusively deals with WTF-16 and considers
// `€:\` a drive-absolute path, but code points that take two WTF-16 code units to encode get
// classified as a relative path (e.g. with U+20000 as the drive-letter that'd be encoded
// in WTF-16 as `<0xD840><0xDC00>:\` and be considered a relative path).
//
// The choice made here is to emulate the behavior of `RtlDetermineDosPathNameType_U` for both
// WTF-16 and WTF-8. This is because, while unlikely and not supported by the Disk Manager GUI,
// drive letters are not actually restricted to A-Z. Using `SetVolumeMountPointW` will allow you
// to set any byte value as a drive letter, and going through `IOCTL_MOUNTMGR_CREATE_POINT` will
// allow you to set any WTF-16 code unit as a drive letter.
//
// Non-A-Z drive letters don't interact well with most of Windows, but certain things do work, e.g.
// `cd /D €:\` will work, filesystem functions still work, etc.
//
// The unfortunate part of this is that this makes handling WTF-8 more complicated as we can't
// just check path[0], path[1], path[2].
const colon_i: usize = switch (T) {
u8 => i: {
const code_point_len = std.unicode.utf8ByteSequenceLength(path[0]) catch return .relative;
// Conveniently, 4-byte sequences in WTF-8 have the same starting code point
// as 2-code-unit sequences in WTF-16.
if (code_point_len > 3) return .relative;
break :i code_point_len;
},
u16 => 1,
else => @compileError("unsupported type: " ++ @typeName(T)),
};
// x
if (path.len < colon_i + 1 or path[colon_i] != mem.nativeToLittle(T, ':')) return .relative;
// x:\
if (path.len > colon_i + 1 and windows_path.isSep(T, path[colon_i + 1])) return .drive_absolute;
// x:
return .drive_relative;
}
}