--------------------------------------------------------------------------------
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
-- | Core types used internally
module Text.Digestive.Types
    ( Result (..)
    , resultMapError
    , Path
    , toPath
    , fromPath
    , Method (..)
    , FormInput (..)
    , Env
    ) where


--------------------------------------------------------------------------------
import           Control.Applicative (Applicative (..))
import           Data.Monoid         (Monoid, mappend)


--------------------------------------------------------------------------------
import           Data.Text           (Text)
import qualified Data.Text           as T


--------------------------------------------------------------------------------
-- | A mostly internally used type for representing Success/Error, with a
-- special applicative instance
data Result v a
    = Success a
    | Error v
    deriving (Show)


--------------------------------------------------------------------------------
instance Functor (Result v) where
    fmap f (Success x) = Success (f x)
    fmap _ (Error x)   = Error x


--------------------------------------------------------------------------------
instance Monoid v => Applicative (Result v) where
    pure x                  = Success x
    Error x   <*> Error y   = Error $ mappend x y
    Error x   <*> Success _ = Error x
    Success _ <*> Error y   = Error y
    Success x <*> Success y = Success (x y)


--------------------------------------------------------------------------------
instance Monoid v => Monad (Result v) where
    return x          = Success x
    (Error x)   >>= _ = Error x
    (Success x) >>= f = f x


--------------------------------------------------------------------------------
-- | Map over the error type of a 'Result'
resultMapError :: (v -> w) -> Result v a -> Result w a
resultMapError f (Error x)   = Error (f x)
resultMapError _ (Success x) = Success x


--------------------------------------------------------------------------------
-- | Describes a path to a subform
type Path = [Text]


--------------------------------------------------------------------------------
-- | Create a 'Path' from some text
toPath :: Text -> Path
toPath = filter (not . T.null) . T.split (== '.')


--------------------------------------------------------------------------------
-- | Serialize a 'Path' to 'Text'
fromPath :: Path -> Text
fromPath = T.intercalate "."


--------------------------------------------------------------------------------
-- | The HTTP methods
data Method = Get | Post
    deriving (Eq, Ord, Show)


--------------------------------------------------------------------------------
-- | The different input types sent by the browser
data FormInput
    = TextInput Text
    | FileInput FilePath
    deriving (Show)


--------------------------------------------------------------------------------
-- | An environment (e.g. a server) from which we can read input parameters. A
-- single key might be associated with multiple text values (multi-select).
type Env m = Path -> m [FormInput]