{-# LANGUAGE ExistentialQuantification #-}
-- | This module contains data types and combinators for defining a
-- 'Schema' for your 'Resource'. A 'Schema' has three type parameters,
-- specifying the identifiers for a single resource, a listing, and a
-- top-level (static) action. After routing, these identifiers will be
-- passed to the 'Handler'.
module Rest.Schema
  ( -- * A set of combinators for creating schemas.

    -- ** Top level
    withListing
  , noListing
  , singleton
    -- ** Named endpoints
  , named
  , static
  , single
  , singleBy
  , singleRead
  , listing
  , listingBy
  , listingRead
    -- ** Unnamed endpoints
  , unnamedSingle
  , unnamedSingleRead
  , unnamedListing
  , unnamedListingRead

    -- * The core schema data types.

  , Schema (..)
  , Step (..)
  , Cardinality (..)
  , Getter (..)
  , Id (..)
  , Endpoint
  ) where

import Rest.Dictionary (Ident (..))
import Rest.Info (Info)

-- * A set of combinators for creating schemas.

-- ** Top level

-- | A schema with a top level listing.

withListing :: mid -> Step sid mid aid -> Schema sid mid aid
withListing mid = Schema (Just (Many mid))

-- | A schema with no top level listing.

noListing :: Step sid mid aid -> Schema sid mid aid
noListing = Schema Nothing

-- | A schema with a singleton at the top level.

singleton :: sid -> Step sid mid aid -> Schema sid mid aid
singleton sid = Schema (Just (Single sid))

-- ** Named endpoints

-- | A list of named endpoints.

named :: [(String, Endpoint sid mid aid)] -> Step sid mid aid
named = Named

-- | A top level action endpoint for this resource.

static :: aid -> Endpoint sid mid aid
static = Left

-- | A singleton resource endpoint.

single :: sid -> Endpoint sid mid aid
single = Right . Single . Singleton

-- | A single resource endpoint with a string identifier.

singleBy :: (String -> sid) -> Endpoint sid mid aid
singleBy = singleIdent StringId

-- | A single resource endpoint with an identifier that can be read.

singleRead :: (Show a, Read a, Info a) => (a -> sid) -> Endpoint sid mid aid
singleRead = singleIdent ReadId

-- | A single resource identified as specified by the 'Ident'.

singleIdent :: Ident a -> (a -> sid) -> Endpoint sid mid aid
singleIdent ident = Right . Single . By . Id ident

-- | A listing endpoint.

listing :: mid -> Endpoint sid mid aid
listing = Right . Many . Singleton

-- | A listing endpoint with a string identifier.

listingBy :: (String -> mid) -> Endpoint sid mid aid
listingBy = listingIdent StringId

-- | A listing with an identifier that can be read.

listingRead :: (Show a, Read a, Info a) => (a -> mid) -> Endpoint sid mid aid
listingRead = listingIdent ReadId

-- | A listing identified as specified by the 'Ident'.
listingIdent :: Ident a -> (a -> mid) -> Endpoint sid mid aid
listingIdent ident = Right . Many . By . Id ident

-- ** Unnamed endpoints

-- | An unnamed single resource with a string identifier.

unnamedSingle :: (String -> sid) -> Step sid mid aid
unnamedSingle = unnamedSingleIdent StringId

-- | An unnamed single resource with an identifier that can be read.

unnamedSingleRead :: (Show a, Read a, Info a) => (a -> sid) -> Step sid mid aid
unnamedSingleRead = unnamedSingleIdent ReadId

-- | An unnamed single resource identified as specified by the
-- 'Ident'.

unnamedSingleIdent :: Ident a -> (a -> sid) -> Step sid mid aid
unnamedSingleIdent ident = Unnamed . Single . Id ident

-- | An unnamed listing with a string identifier.

unnamedListing :: (String -> mid) -> Step sid mid aid
unnamedListing = unnamedListingIdent StringId

-- | An unnamed listing with an identifier that can be read.
unnamedListingRead :: (Show a, Read a, Info a) => (a -> mid) -> Step sid mid aid
unnamedListingRead = unnamedListingIdent ReadId

-- | An unnamed listing identified as specified by the 'Ident'.

unnamedListingIdent :: Ident a -> (a -> mid) -> Step sid mid aid
unnamedListingIdent ident = Unnamed . Many . Id ident

-- * The core schema data types.

-- | A 'Schema' describes how (part of the) route to a resource looks,
-- and returns an identifier for a single resource ('sid'), many
-- resources ('mid') or a static action ('aid').
-- The first argument specifies the top level resource (no path
-- segments). The second specifies a what happens at the first step in
-- the path.

data Schema sid mid aid = Schema (Maybe (Cardinality sid mid)) (Step sid mid aid)

-- | A step in the routing of a resource. A part of the uri either
-- identifies a 'Named' resource, or an 'Unnamed' resource. Named
-- resources can be static actions ('Left') or one or many singletons or
-- by's.

data Step sid mid aid = Named   [(String, Endpoint sid mid aid)]
                      | Unnamed (Cardinality (Id sid) (Id mid))

-- | Specifies if we're identifying a single resource, or many (a
-- listing).
data Cardinality s m = Single s
                     | Many   m

-- | A 'Getter' can either be a 'Singleton' (there is only one) or it
-- can be identified 'By' an 'Id'entifier.

data Getter id = Singleton id | By (Id id)

-- | An identification of an item in a resource. It contains a
-- dictionary describing how to identify the resource, and a function
-- for this identification type to an @id@.

data Id id = forall a. Id (Ident a) (a -> id)

-- | A named endpoint: an static action, a single item of many items.

type Endpoint sid mid aid = Either aid (Cardinality (Getter sid) (Getter mid))