module [ UnwrappedPath, InternalPath, InternalPathType, wrap, unwrap, to_bytes, from_arbitrary_bytes, from_os_bytes, ] InternalPath := UnwrappedPath implements [Inspect] UnwrappedPath : [ # We store these separately for two reasons: # 1. If I'm calling an OS API, passing a path I got from the OS is definitely safe. # However, passing a Path I got from a RocStr might be unsafe; it may contain \0 # characters, which would result in the operation happening on a totally different # path. As such, we need to check for \0s and fail without calling the OS API if we # find one in the path. # 2. If I'm converting the Path to a Str, doing that conversion on a Path that was # created from a RocStr needs no further processing. However, if it came from the OS, # then we need to know what charset to assume it had, in order to decode it properly. # These come from the OS (e.g. when reading a directory, calling `canonicalize`, # or reading an environment variable - which, incidentally, are nul-terminated), # so we know they are both nul-terminated and do not contain interior nuls. # As such, they can be passed directly to OS APIs. # # Note that the nul terminator byte is right after the end of the length (into the # unused capacity), so this can both be compared directly to other `List U8`s that # aren't nul-terminated, while also being able to be passed directly to OS APIs. FromOperatingSystem (List U8), # These come from userspace (e.g. Path.from_bytes), so they need to be checked for interior # nuls and then nul-terminated before the host can pass them to OS APIs. ArbitraryBytes (List U8), # This was created as a RocStr, so it might have interior nul bytes but it's definitely UTF-8. # That means we can `to_str` it trivially, but have to validate before sending it to OS # APIs that expect a nul-terminated `char*`. # # Note that both UNIX and Windows APIs will accept UTF-8, because on Windows the host calls # `_setmbcp(_MB_CP_UTF8);` to set the process's Code Page to UTF-8 before doing anything else. # See https://docs.microsoft.com/en-us/windows/apps/design/globalizing/use-utf8-code-page#-a-vs--w-apis # and https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/setmbcp?view=msvc-170 # for more details on the UTF-8 Code Page in Windows. FromStr Str, ] InternalPathType : { is_file : Bool, is_sym_link : Bool, is_dir : Bool, } wrap : UnwrappedPath -> InternalPath wrap = @InternalPath unwrap : InternalPath -> UnwrappedPath unwrap = |@InternalPath(raw)| raw ## TODO do this in the host, and iterate over the Str ## bytes when possible instead of always converting to ## a heap-allocated List. to_bytes : InternalPath -> List U8 to_bytes = |@InternalPath(path)| when path is FromOperatingSystem(bytes) -> bytes ArbitraryBytes(bytes) -> bytes FromStr(str) -> Str.to_utf8(str) from_arbitrary_bytes : List U8 -> InternalPath from_arbitrary_bytes = |bytes| @InternalPath(ArbitraryBytes(bytes)) from_os_bytes : List U8 -> InternalPath from_os_bytes = |bytes| @InternalPath(FromOperatingSystem(bytes))