module Utils.Drasil.FilePath (
  RelativeFile, relativeFile, relFileToStr
) where

import Data.List (foldl')
import System.FilePath (isAbsolute, isValid, hasExtension, splitDirectories)

-- | A valid, relative file path with an extension in canonical form.
newtype RelativeFile = RF { RelativeFile -> String
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 :: String -> RelativeFile
relativeFile String
fp
  | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
isCanonical String
fp = String -> RelativeFile
forall a. HasCallStack => String -> a
error (String -> RelativeFile) -> String -> RelativeFile
forall a b. (a -> b) -> a -> b
$ String
"`" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fp String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` is not in canonical form."
  | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
isValid String
fp = String -> RelativeFile
forall a. HasCallStack => String -> a
error (String -> RelativeFile) -> String -> RelativeFile
forall a b. (a -> b) -> a -> b
$ String
"`" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fp String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` is not a valid file path."
  | Bool -> Bool
not (Bool -> Bool) -> Bool -> Bool
forall a b. (a -> b) -> a -> b
$ String -> Bool
hasExtension String
fp = String -> RelativeFile
forall a. HasCallStack => String -> a
error (String -> RelativeFile) -> String -> RelativeFile
forall a b. (a -> b) -> a -> b
$ String
"`" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fp String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` does not contain a file extension."
  | String -> Bool
isAbsolute String
fp = String -> RelativeFile
forall a. HasCallStack => String -> a
error (String -> RelativeFile) -> String -> RelativeFile
forall a b. (a -> b) -> a -> b
$ String
"`" String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
fp String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
"` is an absolute file path, but a relative file path was expected."
  | Bool
otherwise = String -> RelativeFile
RF String
fp

isCanonical :: String -> Bool
isCanonical :: String -> Bool
isCanonical String
fp = (Bool -> String -> Bool) -> Bool -> [String] -> 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 -> String -> Bool
step Bool
True (String -> [String]
splitDirectories String
fp)
  where
    step :: Bool -> String -> Bool
step Bool
_   String
"."  = Bool
False -- Should not include current dir
    step Bool
_   String
".." = Bool
False -- Should not "go up" directories
    step Bool
acc String
_    = Bool
acc