packages feed

ema-0.8.0.0: src/Ema/Route/Generic/Example.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE UndecidableInstances #-}

module Ema.Route.Generic.Example where

import Data.Generics.Sum.Any (AsAny (_As))
import Data.SOP (I (..), NP (..))
import Ema
import Ema.Asset qualified as Asset
import Ema.Route.Generic
import Ema.Route.Generic.TH
import Generics.SOP qualified as SOP
import Generics.SOP.TH qualified as SOP
import Optics.Core ((%))
import Optics.Prism (prism')

-- ----------
-- Examples
-- ----------

type M = (Int, Int, String)

data R = R_Index | R_Foo | R_Bar NumRoute | R_Bar2 NumRoute
  deriving stock (Show, Eq, Generic)
  deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
  deriving (HasSubRoutes, IsRoute) via (GenericRoute R '[WithModel M])

-- We must derive this manually due to ambiguity in field types (Int)
instance HasSubModels R where
  subModels (a, b, _) =
    I () :* I () :* I a :* I b :* Nil

data NumRoute = NumRoute
  deriving stock (Show, Eq, Generic)

instance IsRoute NumRoute where
  type RouteModel NumRoute = Int
  routePrism n =
    let fp = show n <> ".html"
     in toPrism_ $
          prism' (const fp) $ \s -> do
            guard $ s == fp
            pure NumRoute
  routeUniverse _ = [NumRoute]

instance EmaSite R where
  siteInput _ () = pure $ pure (42, 21, "inner")
  siteOutput _ m r = pure $ Asset.AssetGenerated Asset.Html $ show r <> show m

-- --warnings -c "cabal repl ema -f with-examples" -T Ema.Route.Generic.main  --setup ":set args gen /tmp"
main :: IO ()
main = Ema.runSite_ @R ()

-- ---
-- Let's try defining a top-level route using `R` to see how EmaSite instances compose.
-- --

type TM = (M, String)

data TR = TR_Index | TR_Inner R
  deriving stock (Show, Eq, Generic)
  deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
  deriving (HasSubRoutes, HasSubModels, IsRoute) via (GenericRoute TR '[WithModel TM])

instance EmaSite TR where
  siteInput x () = do
    m1 <- siteInput @R x ()
    pure $ fmap (,"TOP") m1
  siteOutput rp m = \case
    r@TR_Index ->
      pure $ Asset.AssetGenerated Asset.Html $ show r <> show m
    TR_Inner r ->
      -- Might as well provide a `innerSiteOutput (_As @TR_Inner)`?
      siteOutput @R (rp % _As @"TR_Inner") (trInnerModel m) r
    where
      trInnerModel model =
        let I () :* I m' :* Nil = subModels @TR model
         in m'

mainTop :: IO ()
mainTop = Ema.runSite_ @TR ()

-- ---
-- Ensure that the constant model case (simple one) still works
-- --

type M2 = (Int, String)

data R2 = R2_Index | R2_Foo | R2_Bar BarRoute | R2_Bar2 BarRoute
  deriving stock (Show, Eq, Generic)
  deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
  deriving (HasSubRoutes, HasSubModels, IsRoute) via (GenericRoute R2 '[WithModel M2])

data BarRoute = BarRoute
  deriving stock (Show, Eq, Generic)
  deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
  deriving (HasSubRoutes, HasSubModels, IsRoute) via (GenericRoute BarRoute '[WithModel M2])

instance EmaSite R2 where
  siteInput _ () = pure $ pure (21, "inner")
  siteOutput _ m r = pure $ Asset.AssetGenerated Asset.Html $ show r <> show m

mainConst :: IO ()
mainConst = Ema.runSite_ @R2 ()

-- ---
-- Ensure deriveIsRoute works with (partial) subroutes
-- --

data M3
  = M3
  deriving stock (Eq, Show, Generic)

data R3
  = R3_Sub R3_SubR
  | R3_Index

data R3_SubR
  = R3_SubR
  deriving stock (Show, Eq, Ord, Generic)
  deriving anyclass (SOP.Generic, SOP.HasDatatypeInfo)
  deriving
    (HasSubRoutes, HasSubModels, IsRoute)
    via ( GenericRoute
            R3_SubR
            '[ WithSubRoutes
                '[ FileRoute "example.html"
                 ]
             ]
        )

SOP.deriveGeneric ''R3
deriveIsRoute
  ''R3
  [t|
    [ WithModel M3
    , WithSubRoutes
        [ R3_SubR
        , ()
        ]
    ]
    |]

-- ---
-- Ensure deriveIsRoute works in a no-subroute case
-- --

data M4
  = M4
  deriving stock (Eq, Show, Generic)

data R4
  = R4_Index
  | R3_About

SOP.deriveGeneric ''R4
deriveIsRoute ''R4 [t|'[WithModel M4]|]