packages feed

type-of-html-1.1.0.0: src/Html/Type/Internal/GHC.hs

{-# LANGUAGE UndecidableInstances #-}
{-# LANGUAGE TypeOperators        #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE DataKinds            #-}
{-# LANGUAGE PolyKinds            #-}
{-# LANGUAGE CPP                  #-}

module Html.Type.Internal.GHC where

import Html.Type.Internal

import Data.Proxy
import Data.Type.Bool
import GHC.TypeLits

-- | We need efficient cons, snoc and append.  This API has cons(O1)
-- and snoc(O1) but append(On).  Optimal would be a real 2-3
-- FingerTree.
data FingerTree = FingerTree [Sym] Sym
type Empty = 'FingerTree '[] EmptySym
type Split = 'FingerTree '[EmptySym] EmptySym
type NoTail xs = 'FingerTree xs EmptySym
type Singleton = 'FingerTree '[]

type family (<|) s t :: FingerTree where
  (<|) l ('FingerTree (s ': ss) r) = 'FingerTree (AppendSymbol l s ': ss) r
  (<|) l ('FingerTree '[] r) = 'FingerTree '[] (AppendSymbol l r)

type family (|>) t s :: FingerTree where
  (|>) ('FingerTree ss r) rr = 'FingerTree ss (AppendSymbol r rr)

type family (><) t1 t2 :: FingerTree where
  (><) ('FingerTree ss r) ('FingerTree (s ': ss2) r2) = 'FingerTree (Append ss (AppendSymbol r s ': ss2)) r2
  (><) ('FingerTree ss r) ('FingerTree '[] r2) = 'FingerTree ss (AppendSymbol r r2)

#if __GLASGOW_HASKELL__ >= 802
type family OpenTag e where
  OpenTag e = AppendSymbol "<" (AppendSymbol (ShowElement e) ">")

type family CloseTag e where
  CloseTag e = AppendSymbol "</" (AppendSymbol (ShowElement e) ">")

-- | Flatten a document into a type list of tags.
type family ToTypeList a :: FingerTree where
  ToTypeList (a # b)         = ToTypeList a >< ToTypeList b
  ToTypeList ((a :@: ()) ()) = Singleton (If (HasContent (GetInfo a)) (AppendSymbol (OpenTag a) (CloseTag a)) (OpenTag a))
  ToTypeList ((a :@: b) ())  = AppendSymbol "<" (ShowElement a) <| ToTypeList b |> If (HasContent (GetInfo a)) (AppendSymbol ">" (CloseTag a)) ">"
  ToTypeList ((a :@: ()) b)  = OpenTag a <| ToTypeList b |> CloseTag a
  ToTypeList ((a :@: b) c)   = (AppendSymbol "<" (ShowElement a) <| ToTypeList b) >< (">" <| ToTypeList c |> CloseTag a)
  ToTypeList (a := b)        = ShowAttribute a <| ToTypeList b |> "\""
  ToTypeList ()              = Empty
  ToTypeList (Proxy x)       = Singleton x
  ToTypeList x               = Split

type Sym = Symbol
type EmptySym = ""
#else
type family OpenTag e where
  OpenTag e = ["<", ShowElement e, ">"]

type family CloseTag e where
  CloseTag e = ["</", ShowElement e, ">"]

-- | Flatten a document into a type list of tags.
type family ToTypeList a :: FingerTree where
  ToTypeList (a # b)         = ToTypeList a >< ToTypeList b
  ToTypeList ((a :@: ()) ()) = Singleton (If (HasContent (GetInfo a)) (Append (OpenTag a) (CloseTag a)) (OpenTag a))
  ToTypeList ((a :@: b) ())  = '["<", ShowElement a] <| ToTypeList b |> If (HasContent (GetInfo a)) (">" ': CloseTag a) '[">"]
  ToTypeList ((a :@: ()) b)  = OpenTag a <| ToTypeList b |> CloseTag a
  ToTypeList ((a :@: b) c)   = ('["<", ShowElement a] <| ToTypeList b) >< ('[">"] <| ToTypeList c |> CloseTag a)
  ToTypeList (a := b)        = '[ShowAttribute a] <| ToTypeList b |> '["\""]
  ToTypeList ()              = Empty
  ToTypeList (Proxy x)       = Singleton '[x]
  ToTypeList x               = Split

type AppendSymbol xs ys = Append xs ys
type Sym = [Symbol]
type EmptySym = '[]
#endif