module Drasil.FileHandling.Legacy.FilePath (
  RelativeFile, relativeFile, relFileToStr
) where

import System.FilePath (isAbsolute, isValid, hasExtension, splitDirectories)

-- | A valid, relative file path with an extension in canonical form.
newtype RelativeFile = RF { RelativeFile -> [Char]
relFileToStr :: String }
  deriving RelativeFile -> RelativeFile -> Bool
(RelativeFile -> RelativeFile -> Bool)
-> (RelativeFile -> RelativeFile -> Bool) -> Eq RelativeFile
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: RelativeFile -> RelativeFile -> Bool
== :: RelativeFile -> RelativeFile -> Bool
$c/= :: RelativeFile -> RelativeFile -> Bool
/= :: RelativeFile -> RelativeFile -> Bool
Eq

-- | Create a 'RelativeFile' given a 'String' that must be in canonical form, be
-- a valid file path, contain a file extension, and be relative (not absolute);
-- otherwise, an error is raised.
relativeFile :: String -> RelativeFile
relativeFile :: [Char] -> RelativeFile
relativeFile [Char]
fp
  | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> Bool
isCanonical [Char]
fp = [Char] -> RelativeFile
forall a. HasCallStack => [Char] -> a
error ([Char] -> RelativeFile) -> [Char] -> RelativeFile
forall a b. (a -> b) -> a -> b
$ [Char]
"`" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
fp [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"` is not in canonical form."
  | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> Bool
isValid [Char]
fp = [Char] -> RelativeFile
forall a. HasCallStack => [Char] -> a
error ([Char] -> RelativeFile) -> [Char] -> RelativeFile
forall a b. (a -> b) -> a -> b
$ [Char]
"`" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
fp [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"` is not a valid file path."
  | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ [Char] -> Bool
hasExtension [Char]
fp = [Char] -> RelativeFile
forall a. HasCallStack => [Char] -> a
error ([Char] -> RelativeFile) -> [Char] -> RelativeFile
forall a b. (a -> b) -> a -> b
$ [Char]
"`" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
fp [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"` does not contain a file extension."
  | [Char] -> Bool
isAbsolute [Char]
fp = [Char] -> RelativeFile
forall a. HasCallStack => [Char] -> a
error ([Char] -> RelativeFile) -> [Char] -> RelativeFile
forall a b. (a -> b) -> a -> b
$ [Char]
"`" [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
fp [Char] -> [Char] -> [Char]
forall a. Semigroup a => a -> a -> a
<> [Char]
"` is an absolute file path, but a relative file path was expected."
  | Bool
otherwise = [Char] -> RelativeFile
RF [Char]
fp

isCanonical :: String -> Bool
isCanonical :: [Char] -> Bool
isCanonical [Char]
fp = (Bool -> [Char] -> Bool) -> Bool -> [[Char]] -> Bool
forall b a. (b -> a -> b) -> b -> [a] -> b
forall (t :: * -> *) b a.
Foldable t =>
(b -> a -> b) -> b -> t a -> b
foldl' Bool -> [Char] -> Bool
step Bool
True ([Char] -> [[Char]]
splitDirectories [Char]
fp)
  where
    step :: Bool -> [Char] -> Bool
step Bool
_   [Char]
"."  = Bool
False -- Should not include current dir
    step Bool
_   [Char]
".." = Bool
False -- Should not "go up" directories
    step Bool
acc [Char]
_    = Bool
acc