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.ByteString (ByteString)
import Data.ByteString.Builder (Builder)
import Data.Text (Text)
import qualified Data.Text as T
import qualified Network.HTTP.Types as H
import Network.Mime (MimeType)
import qualified Network.Wai as W
import System.Posix.Types (EpochTime)

-- | 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
$cshowsPrec :: Int -> Piece -> ShowS
showsPrec :: Int -> Piece -> ShowS
$cshow :: Piece -> String
show :: Piece -> String
$cshowList :: [Piece] -> ShowS
showList :: [Piece] -> ShowS
Show, Piece -> Piece -> Bool
(Piece -> Piece -> Bool) -> (Piece -> Piece -> Bool) -> Eq Piece
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Piece -> Piece -> Bool
== :: Piece -> Piece -> Bool
$c/= :: Piece -> Piece -> Bool
/= :: 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
$ccompare :: Piece -> Piece -> Ordering
compare :: Piece -> Piece -> Ordering
$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
>= :: Piece -> Piece -> Bool
$cmax :: Piece -> Piece -> Piece
max :: Piece -> Piece -> Piece
$cmin :: Piece -> Piece -> Piece
min :: Piece -> Piece -> 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
    | HasCallStack => Text -> Char
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)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [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
    = -- | no cache-control set
      NoMaxAge
    | -- | set to the given number of seconds
      MaxAgeSeconds Int
    | -- | essentially infinite caching; in reality, probably one year
      MaxAgeForever
    | -- | set cache-control to no-store @since 3.1.8
      NoStore
    | -- | set cache-control to no-cache @since 3.1.9
      NoCache

-- | 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
    { File -> Integer
fileGetSize :: Integer
    -- ^ Size of file in bytes
    , File -> Status -> ResponseHeaders -> Response
fileToResponse :: H.Status -> H.ResponseHeaders -> W.Response
    -- ^ 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 -> Piece
fileName :: Piece
    -- ^ Last component of the filename.
    , File -> IO (Maybe ByteString)
fileGetHash :: IO (Maybe ByteString)
    -- ^ Calculate a hash of the contents of this file, such as for etag.
    , File -> Maybe EpochTime
fileGetModified :: Maybe EpochTime
    -- ^ Last modified time, used for both display in listings and if-modified-since.
    }

-- | 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
    { StaticSettings -> [Piece] -> IO LookupResult
ssLookupFile :: Pieces -> IO LookupResult
    -- ^ Lookup a single file or folder. This is how you can control storage
    -- backend (filesystem, embedded, etc) and where to lookup.
    , StaticSettings -> File -> IO ByteString
ssGetMimeType :: File -> IO MimeType
    -- ^ 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 -> [Piece]
ssIndices :: [Piece]
    -- ^ 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 -> Maybe Listing
ssListing :: Maybe Listing
    -- ^ How to perform a directory listing. Optional. Will be used when the
    -- user requested a folder.
    , StaticSettings -> MaxAge
ssMaxAge :: MaxAge
    -- ^ Value to provide for max age in the cache-control.
    , StaticSettings -> [Piece] -> ByteString -> ByteString
ssMkRedirect :: Pieces -> ByteString -> ByteString
    -- ^ Given a requested path and a new destination, construct a string
    -- that will go there. Default implementation will use relative paths.
    , StaticSettings -> Bool
ssRedirectToIndex :: Bool
    -- ^ 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
ssUseHash :: Bool
    -- ^ Prefer usage of etag caching to last-modified caching.
    , StaticSettings -> Bool
ssAddTrailingSlash :: Bool
    -- ^ Force a trailing slash at the end of directories
    , StaticSettings -> Maybe Application
ss404Handler :: Maybe W.Application
    -- ^ Optional `W.Application` to be used in case of 404 errors
    --
    -- Since 3.1.3
    }