module WaiAppStatic.Types
    ( -- * Pieces
      Piece
    , toPiece
    , fromPiece
    , unsafeToPiece
    , Pieces
    , toPieces
      -- * Caching
    , MaxAge (..)
      -- * File\/folder serving
    , FolderName
    , Folder (..)
    , File (..)
    , LookupResult (..)
    , Listing
      -- * Settings
    , StaticSettings (..)
    ) where

import Data.Text (Text)
import qualified Network.HTTP.Types as H
import qualified Network.Wai as W
import Data.ByteString (ByteString)
import System.Posix.Types (EpochTime)
import qualified Data.Text as T
import Data.ByteString.Builder (Builder)
import Network.Mime (MimeType)

-- | An individual component of a path, or of a filepath.
--
-- This is the core type used by wai-app-static for doing lookups. It provides
-- a smart constructor to avoid the possibility of constructing unsafe path
-- segments (though @unsafeToPiece@ can get around that as necessary).
--
-- Individual file lookup backends must know how to convert from a @Piece@ to
-- their storage system.
newtype Piece = Piece { Piece -> Text
fromPiece :: Text }
    deriving (Int -> Piece -> ShowS
[Piece] -> ShowS
Piece -> String
(Int -> Piece -> ShowS)
-> (Piece -> String) -> ([Piece] -> ShowS) -> Show Piece
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [Piece] -> ShowS
$cshowList :: [Piece] -> ShowS
show :: Piece -> String
$cshow :: Piece -> String
showsPrec :: Int -> Piece -> ShowS
$cshowsPrec :: Int -> Piece -> ShowS
Show, Piece -> Piece -> Bool
(Piece -> Piece -> Bool) -> (Piece -> Piece -> Bool) -> Eq Piece
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
/= :: Piece -> Piece -> Bool
$c/= :: Piece -> Piece -> Bool
== :: Piece -> Piece -> Bool
$c== :: Piece -> Piece -> Bool
Eq, Eq Piece
Eq Piece
-> (Piece -> Piece -> Ordering)
-> (Piece -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> (Piece -> Piece -> Bool)
-> (Piece -> Piece -> Piece)
-> (Piece -> Piece -> Piece)
-> Ord Piece
Piece -> Piece -> Bool
Piece -> Piece -> Ordering
Piece -> Piece -> Piece
forall a.
Eq a
-> (a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
min :: Piece -> Piece -> Piece
$cmin :: Piece -> Piece -> Piece
max :: Piece -> Piece -> Piece
$cmax :: Piece -> Piece -> Piece
>= :: Piece -> Piece -> Bool
$c>= :: Piece -> Piece -> Bool
> :: Piece -> Piece -> Bool
$c> :: Piece -> Piece -> Bool
<= :: Piece -> Piece -> Bool
$c<= :: Piece -> Piece -> Bool
< :: Piece -> Piece -> Bool
$c< :: Piece -> Piece -> Bool
compare :: Piece -> Piece -> Ordering
$ccompare :: Piece -> Piece -> Ordering
$cp1Ord :: Eq Piece
Ord)

-- | Smart constructor for a @Piece@. Won\'t allow unsafe components, such as
-- pieces beginning with a period or containing a slash. This /will/, however,
-- allow null pieces.
toPiece :: Text -> Maybe Piece
toPiece :: Text -> Maybe Piece
toPiece Text
t
    | Text -> Bool
T.null Text
t = Piece -> Maybe Piece
forall a. a -> Maybe a
Just (Piece -> Maybe Piece) -> Piece -> Maybe Piece
forall a b. (a -> b) -> a -> b
$ Text -> Piece
Piece Text
t
    | Text -> Char
T.head Text
t Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'.' = Maybe Piece
forall a. Maybe a
Nothing
    | (Char -> Bool) -> Text -> Bool
T.any (Char -> Char -> Bool
forall a. Eq a => a -> a -> Bool
== Char
'/') Text
t = Maybe Piece
forall a. Maybe a
Nothing
    | Bool
otherwise = Piece -> Maybe Piece
forall a. a -> Maybe a
Just (Piece -> Maybe Piece) -> Piece -> Maybe Piece
forall a b. (a -> b) -> a -> b
$ Text -> Piece
Piece Text
t

-- | Construct a @Piece@ without input validation.
unsafeToPiece :: Text -> Piece
unsafeToPiece :: Text -> Piece
unsafeToPiece = Text -> Piece
Piece

-- | Call @toPiece@ on a list.
--
-- > toPieces = mapM toPiece
toPieces :: [Text] -> Maybe Pieces
toPieces :: [Text] -> Maybe [Piece]
toPieces = (Text -> Maybe Piece) -> [Text] -> Maybe [Piece]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
mapM Text -> Maybe Piece
toPiece

-- | Request coming from a user. Corresponds to @pathInfo@.
--
-- The root path is the empty list.
type Pieces = [Piece]

-- | Values for the max-age component of the cache-control response header.
data MaxAge = NoMaxAge -- ^ no cache-control set
            | MaxAgeSeconds Int -- ^ set to the given number of seconds
            | MaxAgeForever -- ^ essentially infinite caching; in reality, probably one year

-- | Just the name of a folder.
type FolderName = Piece

-- | Represent contents of a single folder, which can be itself either a file
-- or a folder.
data Folder = Folder
    { Folder -> [Either Piece File]
folderContents :: [Either FolderName File]
    }

-- | Information on an individual file.
data File = File
    { -- | Size of file in bytes
      File -> Integer
fileGetSize :: Integer
      -- | How to construct a WAI response for this file. Some files are stored
      -- on the filesystem and can use @ResponseFile@, while others are stored
      -- in memory and should use @ResponseBuilder@.
    , File -> Status -> ResponseHeaders -> Response
fileToResponse :: H.Status -> H.ResponseHeaders -> W.Response
      -- | Last component of the filename.
    , File -> Piece
fileName :: Piece
      -- | Calculate a hash of the contents of this file, such as for etag.
    , File -> IO (Maybe ByteString)
fileGetHash :: IO (Maybe ByteString)
      -- | Last modified time, used for both display in listings and if-modified-since.
    , File -> Maybe EpochTime
fileGetModified :: Maybe EpochTime
    }

-- | Result of looking up a file in some storage backend.
--
-- The lookup is either a file or folder, or does not exist.
data LookupResult = LRFile File
                  | LRFolder Folder
                  | LRNotFound

-- | How to construct a directory listing page for the given request path and
-- the resulting folder.
type Listing = Pieces -> Folder -> IO Builder

-- | All of the settings available to users for tweaking wai-app-static.
--
-- Note that you should use the settings type approach for modifying values.
-- See <http://www.yesodweb.com/book/settings-types> for more information.
data StaticSettings = StaticSettings
    {
      -- | Lookup a single file or folder. This is how you can control storage
      -- backend (filesystem, embedded, etc) and where to lookup.
      StaticSettings -> [Piece] -> IO LookupResult
ssLookupFile :: Pieces -> IO LookupResult

      -- | Determine the mime type of the given file. Note that this function
      -- lives in @IO@ in case you want to perform more complicated mimetype
      -- analysis, such as via the @file@ utility.
    , StaticSettings -> File -> IO ByteString
ssGetMimeType :: File -> IO MimeType

      -- | Ordered list of filenames to be used for indices. If the user
      -- requests a folder, and a file with the given name is found in that
      -- folder, that file is served. This supercedes any directory listing.
    , StaticSettings -> [Piece]
ssIndices :: [Piece]

      -- | How to perform a directory listing. Optional. Will be used when the
      -- user requested a folder.
    , StaticSettings -> Maybe Listing
ssListing :: Maybe Listing

      -- | Value to provide for max age in the cache-control.
    , StaticSettings -> MaxAge
ssMaxAge :: MaxAge

      -- | Given a requested path and a new destination, construct a string
      -- that will go there. Default implementation will use relative paths.
    , StaticSettings -> [Piece] -> ByteString -> ByteString
ssMkRedirect :: Pieces -> ByteString -> ByteString

      -- | If @True@, send a redirect to the user when a folder is requested
      -- and an index page should be displayed. When @False@, display the
      -- content immediately.
    , StaticSettings -> Bool
ssRedirectToIndex :: Bool

      -- | Prefer usage of etag caching to last-modified caching.
    , StaticSettings -> Bool
ssUseHash :: Bool

      -- | Force a trailing slash at the end of directories
    , StaticSettings -> Bool
ssAddTrailingSlash :: Bool

      -- | Optional `W.Application` to be used in case of 404 errors
      --
      -- Since 3.1.3
    , StaticSettings -> Maybe Application
ss404Handler :: Maybe W.Application
    }