content: {-# LANGUAGE CPP #-}
  -- |
  -- Module      : Streamly.Internal.FileSystem.Path
  -- Copyright   : (c) 2023 Composewell Technologies
  -- License     : BSD3
  -- Maintainer  : streamly@composewell.com
  -- Portability : GHC
  --
  -- == References
  --
  --  * https://en.wikipedia.org/wiki/Path_(computing)
  --  * https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
  --  * https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-dtyp/62e862f4-2a51-452e-8eeb-dc4ff5ee33cc
  --
  -- == Windows and Posix Paths
  --
  -- We should be able to manipulate windows paths on posix and posix paths on
  -- windows as well. Therefore, we have WindowsPath and PosixPath types which
  -- are supported on both platforms. However, the Path module aliases Path to
  -- WindowsPath on Windows and PosixPath on Posix.
  --
  -- == File System as Tree vs Graph
  --
  -- A file system is a tree when there are no hard links or symbolic links. But
  -- in the presence of symlinks it could be a DAG or a graph, because directory
  -- symlinks can create cycles.
  --
  -- == Rooted and Branch paths
  --
  -- We make two distinctions for paths, a path may a specific filesystem root
  -- attached to it or it may be a free branch without a root attached.
  --
  -- A path that has a root attached to it is called a rooted path e.g. /usr is a
  -- rooted path, . is a rooted path, ./bin is a rooted path. A rooted path could
  -- be absolute e.g. /usr or it could be relative e.g. ./bin . A rooted path
  -- always has two components, a specific "root" which could be explicit or
  -- implicit, and a path segment relative to the root. A rooted path with a
  -- fixed root is known as an absolute path whereas a rooted path with an
  -- implicit root e.g. "./bin" is known as a relative path.
  --
  -- A path that does not have a root attached but defines steps to go from some
  -- place to another is a path branch. For example, "local/bin" is a path branch
  -- whereas "./local/bin" is a rooted path.
  --
  -- Rooted paths can never be appended to any other path whereas a branch can be
  -- appended.
  --
  -- == Comparing Paths
  --
  -- We can compare two absolute rooted paths or path branches but we cannot
  -- compare two relative rooted paths. If each component of the path is the same
  -- then the paths are considered to be equal.
  --
  -- == Implicit Roots (.)
  --
  -- On Posix and Windows "." implicitly refers to the current directory. On
  -- Windows a path like @/Users/@ has the drive reference implicit. Such
  -- references are contextual and may have different meanings at different
  -- times.
  --
  -- @./bin@ may refer to a different location depending on what "." is
  -- referring to. Thus we should not allow @./bin@ to be appended to another
  -- path, @bin@ can be appended though. Similarly, we cannot compare @./bin@
  -- with @./bin@ and say that they are equal because they may be referring to
  -- different locations depending on in what context the paths were created.
  --
  -- The same arguments apply to paths with implicit drive on Windows.
  --
  -- We can treat @.\/bin\/ls@ as an absolute path with "." as an implicit root.
  -- The relative path is "bin/ls" which represents steps from somewhere to
  -- somewhere else rather than a particular location. We can also call @./bin@
  -- as a "rooted path" as it starts from particular location rather than
  -- defining "steps" to go from one place to another. If we want to append such
  -- paths we need to first make them explicitly relative by dropping the
  -- implicit root. Or we can use unsafeAppend to force it anyway or unsafeCast
  -- to convert absolute to relative.
  --
  -- On these absolute (Rooted) paths if we use takeRoot, it should return
  -- RootCurDir, RootCurDrive and @Root Path@ to distinguish @./@, @/@, @C:/@. We
  -- could represent them by different types but that would make the types even
  -- more complicated. So runtime checks are are a good balance.
  --
  -- Path comparison should return EqTrue, EqFalse or EqUnknown. If we compare
  -- these absolute/located paths having implicit roots then result should be
  -- EqUnknown or maybe we can just return False?. @./bin@ and @./bin@ should be
  -- treated as paths with different roots/drives but same relative path. The
  -- programmer can explicitly drop the root and compare the relative paths if
  -- they want to check literal equality.
  --
  -- Note that a trailing . or a . in the middle of a path is different as it
  -- refers to a known name.
  --
  -- == Ambiguous References (..)
  --
  -- ".." in a path refers to the parent directory relative to the current path.
  -- For an absolute root directory ".." refers to the root itself because you
  -- cannot go further up.
  --
  -- When resolving ".." it always resolves to the parent of a directory as
  -- stored in the directory entry. So if we landed in a directory via a symlink,
  -- ".." can take us back to a different directory and not to the symlink
  -- itself. Thus @a\/b/..@ may not be the same as @a/@. Shells like bash keep
  -- track of the old paths explicitly, so you may not see this behavior when
  -- using a shell.
  --
  -- For this reason we cannot process ".." in the path statically. However, if
  -- the components of two paths are exactly the same then they will always
  -- resolve to the same target. But two paths with different components could
  -- also point to the same target. So if there are ".." in the path we cannot
  -- definitively say if they are the same without resolving them.
  --
  -- == Exception Handling
  --
  -- Path creation routines use MonadThrow which can be interpreted as an Either
  -- type. It is rare to actually handle exceptions in path creation functions,
  -- we would rather fix the issue, so partial functions should also be fine. But
  -- there may be some cases where we are parsing paths from external inputs,
  -- reading from a file etc where we may want to handle exceptions. We can
  -- always create partial wrappers from these if that is convenient to use.
  --
  
  #define IS_PORTABLE
  
  #if defined(mingw32_HOST_OS) || defined(__MINGW32__)
  #define IS_WINDOWS
  #include "Streamly/Internal/FileSystem/WindowsPath.hs"
  #else
  #include "Streamly/Internal/FileSystem/PosixPath.hs"
  #endif
  