    {-# LANGUAGE OverloadedStrings #-}
    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 Blaze.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 { fromPiece :: Text }
        deriving (Show, Eq, 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 t
        | T.null t = Just $ Piece t
        | T.head t == '.' = Nothing
        | T.any (== '/') t = Nothing
        | otherwise = Just $ Piece t

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

    -- | Call @toPiece@ on a list.
    --
    -- > toPieces = mapM toPiece
    toPieces :: [Text] -> Maybe Pieces
    toPieces = mapM toPiece

    -- | Request coming from a user. Corresponds to @pathInfo@.
    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
        { folderContents :: [Either FolderName File]
        }

    -- | Information on an individual file.
    data File = File
        { -- | Size of file in bytes
          fileGetSize :: Int
          -- | 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@.
        , fileToResponse :: H.Status -> H.ResponseHeaders -> W.Response
          -- | Last component of the filename.
        , fileName :: Piece
          -- | Calculate a hash of the contents of this file, such as for etag.
        , fileGetHash :: IO (Maybe ByteString)
          -- | Last modified time, used for both display in listings and if-modified-since.
        , 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.
          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.
        , 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.
        , ssIndices :: [Piece]

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

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

          -- | Given a requested path and a new destination, construct a string
          -- that will go there. Default implementation will use relative paths.
        , 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.
        , ssRedirectToIndex :: Bool

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

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

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