Repository: rust
Issue: Correcting Path::components on non-"Unix" platforms
Labels: O-windows,T-libs-api,C-bug,T-libs,A-io,O-UEFI,O-nintendo3ds

`Path::components` is incorrect on Redox. I would like to develop the solution
here: https://github.com/rust-lang/rust/pull/51537.

The following is a description of the problem.

Suppose you have the following path:

`file:/home/user/foo/bar.txt`

You split the path into components using `Path::components`

```rust
Path::new("file:/home/user/foo/bar.txt").components().collect::<Vec<()>>()
```

In Linux, this would be equivalent to the following:

```rust
vec![
    Component::Normal("file:"),
    Component::Normal("home"),
    Component::Normal("user"),
    Component::Normal("foo"),
    Component::Normal("bar.txt"),
]
```

Joining the components with the current directory would give you a path such as
this:

`./file:/home/user/foo/bar.txt`

In Redox, we want to be able to get from the original path to components back to
the original path without any modifications. Here are examples of this usage of
`Path::components`:

https://github.com/uutils/coreutils/search?q=components

In Redox, we have the following options for the `file:` component:

1. `Component::Normal("file:")`
2. `Component::Normal("file:")` followed by `Component::RootDir`
3. `Component::Prefix(Prefix::Verbatim("file:"))`
4. `Component::Prefix(Prefix::Scheme("file:"))`

**Option 1**

`Component::Normal("file:")`

The path mentioned above would end up being the following after being rebuilt
from its components:

`./file:/home/user/foo/bar.txt`

This is the old way of doing things. It not only makes `Path::components`
useless on Redox. Canonicalizing a path will always add a scheme like `file:`
to the beginning, so it is likely that path handling will be incorrect.
Absolute paths would always be interpreted as relative.

:x: This option is unusable for Redox.

**Option 2**

`Component::Normal("file:")` followed by `Component::RootDir`

This would preserve the original meaning of the path, such that it could be
rebuilt from its components as follows:

`file:/home/user/foo/bar.txt`

However, this may require a large amount of work to handle, as it seems likely
that code exists that only checks the first component for being
`Component::RootDir` or `Component::Prefix` in order to identify an absolute
path.

The documentation for `Prefix` provides one such example, which has likely
inspired similar usage:

https://doc.rust-lang.org/std/path/enum.Prefix.html#examples

:x: This option would likely break the expectations of many consumers of the
`Prefix` enum.

**Option 3**

`Component::Prefix(Prefix::Verbatim("file:"))`

This option preserves the original meaning of the path, a rebuilt path would be
this:

`file:/home/user/foo/bar.txt`

This, however, is overloading a variant meant to be used on Windows, for a path
looking like this:

`\\?\home\user\foo\bar.txt`

This means different code will be needed when running on Redox to correctly
parse paths to components and turn components into paths.

:heavy_check_mark: This does leave the enum untouched, while allowing for the
correct parsing of paths on Redox. The only downside is a possible lack of
clarity due to overloading the meaning of the `Prefix::Verbatim` variant.

**Option 4**

`Component::Prefix(Prefix::Scheme("file:"))`

This option also preserves the original meaning of the path, a rebuilt path
would be this:

`file:/home/user/foo/bar.txt`

This is the most clear option, having separate code to handle specifically the
Redox scheme abstraction.

This has the downside of changing a stable enum, `Prefix`. There is, however,
the possibility of having the extra enum variant be
`#[cfg(target_os = "redox")]`, so as to preserve the `Prefix` enum on other
platforms.

:heavy_check_mark: This option could be used to have correct path parsing
without affecting the stability of the `Prefix` enum on non-Redox platforms,
if a `cfg` attribute is used.

**Conclusion**

Potentially the `Prefix` enum would be done different after a major version bump,
perhaps using extension traits that are platform specific. I would think that
there would be an opaque `Prefix` struct, and something like `.into_os_prefix()`
would provide you with an os-specific enum to match against.

For the time being, options 3 and 4 seem to be possible, with some caveats, and
would allow code using stable Rust to quickly do-the-right-thing on Redox.