packages feed

typed-admin-0.1.0.0: src/TypedAdmin/Router.hs

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE FlexibleContexts      #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE StandaloneDeriving    #-}
{-# LANGUAGE TypeOperators         #-}

module TypedAdmin.Router where

import           Control.Monad
import           Data.String
import           Data.Text        (Text, intercalate, pack)
import           Data.Typeable    (Typeable)
import           TypedAdmin.Class
import           Web.HttpApiData

class PathParam a b where
  fromPath :: a -> [Text] -> Maybe b
  renderPath :: a -> b -> [Text]

instance PathParam (Path a) () where
  fromPath x y = f (Just Nothing) x y
    where
      f :: Maybe (Maybe ()) -> (Path b) -> [Text] -> Maybe ()
      f Nothing _ _ = Nothing
      f a EmptyP [] = join a
      f _ (StaticP p1 ps) (x:xs)
        | p1 == x = f (Just $ Just ()) ps xs
        | otherwise = Nothing
      f _ _ _ = Nothing
  renderPath path _ = f [] path
    where
      f :: [Text] -> (Path a) -> [Text]
      f xs EmptyP          = xs
      f xs (StaticP p1 ps) = f (p1:xs) ps

toText = ("/" <>) . intercalate "/"

instance (FromHttpApiData a, ToHttpApiData a) => PathParam (Path '[a]) a where
  fromPath x y = f (Just Nothing) x y
    where
      f :: Maybe (Maybe a) -> (Path b) -> [Text] -> Maybe a
      f Nothing _ _ = Nothing
      f a EmptyP [] = join a
      f a (StaticP p1 ps) (x:xs)
        | p1 == x = f a ps xs
        | otherwise = Nothing
      f (Just Nothing) (VarP ps) (x:xs) = f (Just $ parseUrlPieceMaybe x) ps xs
      f _ _ _ = Nothing
  renderPath path x = f [] path
    where
      f :: [Text] -> (Path b) -> [Text]
      f xs EmptyP          = xs
      f xs (StaticP p1 ps) = f (p1:xs) ps
      f xs (VarP ps)       = f (toUrlPiece x:xs) ps

data Route m where
  ListR :: (ListConsole m a b) => Path c -> p1 a -> p2 b -> Route m
  DetailR :: (DetailConsole m a, PathParam (Path c) (Ident a)) => Path c -> p1 a -> Route m
  CreateR :: CreateConsole m a b => Path c -> p1 a -> p2 b -> Route m
  EditR :: (EditConsole m a b, PathParam (Path c) (EditIdent a b)) => Path c -> p1 a -> p2 b -> Route m
  DeleteR :: (DeleteConsole m a, PathParam (Path c) (DeleteIdent a)) => Path c -> p1 a -> Route m

instance a ~ '[] => IsString (Path a) where
  fromString x = StaticP (pack x) EmptyP

instance a ~ '[] => IsString (Path a -> Path a) where
  fromString x = StaticP (pack x)

data Path (as :: [*]) where
  EmptyP :: Path '[]
  StaticP :: Text -> Path as -> Path as
  VarP :: (FromHttpApiData a, Typeable a) => Path as -> Path (a ': as)
deriving instance Show (Path i)

static = StaticP

var :: (FromHttpApiData a, Typeable a) => Path as -> Path (a : as)
var = VarP

(</>) :: Path as -> (Path as -> Path bs) -> Path bs
(</>) x y = y x