packages feed

web-rep-0.2.0: app/example.hs

{-# LANGUAGE ApplicativeDo #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedLabels #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# OPTIONS_GHC -Wall #-}

import Prelude hiding (log, init)
import Control.Lens hiding (Wrapped, Unwrapped)
import Data.Attoparsec.Text (parseOnly, decimal)
import Lucid
import Network.Wai (rawPathInfo)
import Network.Wai.Middleware.RequestLogger (logStdoutDev)
import Network.Wai.Middleware.Static (addBase, noDots, staticPolicy, (>->))
import Options.Generic
import Web.Page
import Web.Page.Examples
import Web.Scotty (scotty, middleware)
import qualified Box
import qualified Data.Text as Text
import Data.Text (pack, Text)
import Control.Monad
import Data.Maybe

testPage :: Text -> Text -> [(Text, Html ())] -> Page
testPage title mid sections =
  bootstrapPage <>
  bridgePage &
  #htmlHeader .~ title_ "iroTestPage" &
  #htmlBody .~ divClass_ "container" (mconcat
    [ divClass_ "row" (h1_ (toHtml title))
    , divClass_ "row" (h2_ ("middleware: " <> toHtml mid))
    , divClass_ "row" $ mconcat $ (\(t,h) -> divClass_ "col" (h2_ (toHtml t) <> with div_ [id_ t] h)) <$> sections
    ])

-- | bridge testing without the SharedRep method
rangeTest :: Input Int
rangeTest =
  Input
  3
  (Just "range example")
  "rangeid"
  (Slider
  [ style_ "max-width:15rem;"
  , min_ "0"
  , max_ "5"
  , step_ "1"
  ])

textTest :: Input Text
textTest =
  Input
  "abc"
  (Just "label")
  "textid"
  TextBox

initBridgeTest :: (Int, Text)
initBridgeTest = (rangeTest ^. #inputVal, textTest ^. #inputVal)

stepBridgeTest :: Element -> (Int, Text) -> (Int,Text)
stepBridgeTest e s =
  case step e s of
    Left _ -> s
    Right x -> x
  where
    step (Element "rangeid" v) (_, t) = either
      (Left . Text.pack)
      (\x -> Right (x,t))
      (parseOnly decimal v)
    step (Element "textid" v) (n, _) = Right (n,v)
    step e' _ = Left $ "unknown id: " <> pack (show e')

sendBridgeTest :: (Show a) => Engine -> Either Text a -> IO ()
sendBridgeTest e (Left err) = append e "log" err
sendBridgeTest e (Right a) =
  replace e "output"
  (toText $ cardify (mempty, []) (Just "output was:")
    (toHtml (show a), []))

consumeBridgeTest :: Engine -> IO (Int, Text)
consumeBridgeTest e =
  valueConsume initBridgeTest stepBridgeTest
  ( (Box.liftC <$> Box.showStdout) <>
    pure (Box.Committer (\v -> sendBridgeTest e v >> pure True))
  ) (bridge e)

midBridgeTest :: (Show a) => Html () -> (Engine -> IO a) -> Application -> Application
midBridgeTest init eeio = start $ \ e -> do
  append e "input" (toText init)
  final <- eeio e `finally` putStrLn "midBridgeTest finalled"
  putStrLn $ "final value was: " <> show final

-- * SharedRep testing

-- | Middleware that shows the current shared values
midShow :: (Show a) => SharedRep IO a -> Application -> Application
midShow sr = midShared sr initShowRep (logResults (pack . show))

initShowRep
  :: (Show a)
  => Engine
  -> Rep a
  -> StateT (HashMap Text Text) IO ()
initShowRep e r =
  void $ oneRep r
  (\(Rep h fa) m -> do
      append e "input" (toText h)
      replace e "output" (pack . show $ fa m))

results :: (a -> Text) -> Engine -> a -> IO ()
results r e x = replace e "output" (r x)

logResults :: (a -> Text) -> Engine -> Either Text a -> IO ()
logResults _ e (Left err) = append e "log" (err <> "<br>")
logResults r e (Right x) = results r e x

-- | evaluate a Fiddle, without attempting to downstream bridging
midFiddle :: Concerns Text -> Application -> Application
midFiddle cs = midShared (fiddle cs) initFiddleRep (\e -> logFiddle e . second snd)

initFiddleRep
  :: Engine
  -> Rep a
  -> StateT (HashMap Text Text) IO ()
initFiddleRep e r =
  void $ oneRep r
  (\(Rep h _) _ ->
      append e "input" (toText h))

logFiddle :: Engine -> Either Text (Either Text (Concerns Text, Bool)) -> IO ()
logFiddle e (Left err) = append e "log" ("map error: " <> err)
logFiddle e (Right (Left err)) = append e "log" ("parse error: " <> err)
logFiddle e (Right (Right (c,u))) = bool (pure ()) (sendConcerns e "output" c) u

-- | evaluate a Fiddle, and any downstream bridging representation
midViaFiddle
  :: Show a
  => SharedRep IO a
  -> Application -> Application
midViaFiddle sr =
  midShared (viaFiddle sr) (initViaFiddleRep (pack . show)) (\e -> logViaFiddle e (pack . show) . second snd)

initViaFiddleRep
  :: (a -> Text)
  -> Engine
  -> Rep (Bool, Concerns Text, a)
  -> StateT (HashMap Text Text) IO ()
initViaFiddleRep rend e r =
  void $ oneRep r
  (\(Rep h fa) m -> do
      append e "input" (toText h)
      case (snd $ fa m) of
        Left err -> append e "log" ("map error: " <> err)
        Right (_,c,a) -> do
          sendConcerns e "representation" c
          replace e "output" (rend a))

logViaFiddle :: Engine -> (a -> Text) -> Either Text (Either Text (Bool, Concerns Text, a)) -> IO ()
logViaFiddle e _ (Left err) = append e "log" ("map error: " <> err)
logViaFiddle e _ (Right (Left err)) = append e "log" ("parse error: " <> err)
logViaFiddle e r (Right (Right (True,c,a))) = do
  sendConcerns e "representation" c
  replace e "output" (r a)
logViaFiddle e r (Right (Right (False,_,a))) = replace e "output" (r a)

data MidType = Dev | Prod | ChooseFileExample | DataListExample | SumTypeExample | SumType2Example | Bridge | ListExample | ListRepExample | Fiddle | ViaFiddle | NoMid deriving (Eq, Read, Show, Generic)

instance ParseField MidType
instance ParseRecord MidType
instance ParseFields MidType

data Opts w = Opts
  { midtype :: w ::: MidType <?> "type of middleware processing"
  , log :: w ::: Maybe Bool <?> "server log to stdout"
  , logPath :: w ::: Maybe Bool <?> "log raw path"
  } deriving (Generic)

instance ParseRecord (Opts Wrapped)

main :: IO ()
main = do
  o :: Opts Unwrapped <- unwrapRecord "examples for web-page"
  let tr = fromMaybe False
  scotty 3000 $ do
    middleware $ staticPolicy (noDots >-> addBase "other")
    middleware $ staticPolicy (noDots >-> addBase "saves")
    when (tr $ log o) $
      middleware logStdoutDev
    when (tr $ logPath o) $
      middleware $ \app req res ->
        putStrLn "raw path:" >>
        print (rawPathInfo req) >> app req res
  -- Only one middleware servicing the web socket can be run at a time.  Simply switching on based on paths doesn't work because socket comms comes through "/"
  -- so that the first bridge middleware consumes all the elements
    middleware $ case midtype o of
      NoMid -> id
      -- WebSocket connection to 'ws://localhost:3000/' failed: Error during WebSocket handshake: Unexpected response code: 200
      Prod -> midShow 
        (maybeRep (Just "maybe") True repExamples)
      Dev -> midShow
        (repSumTypeExample 2 "default text" SumOnly)
      ChooseFileExample -> midShow
        (chooseFile (Just "ChooseFile Label") "")
      DataListExample -> midShow
        (datalist (Just "label") ["first", "2", "3"] "2" "idlist")
      SumTypeExample -> midShow
        (repSumTypeExample 2 "default text" SumOnly)
      SumType2Example -> midShow
        (repSumType2Example 2 "default text" SumOnly (SumOutside 2))
      ListExample -> midShow (listExample 5)
      ListRepExample -> midShow (listRepExample 10)
      Bridge -> midBridgeTest (toHtml rangeTest <> toHtml textTest)
        consumeBridgeTest
      Fiddle -> midFiddle fiddleExample
      ViaFiddle -> midViaFiddle
        (slider Nothing 0 10 0.01 4)
    servePageWith "/simple" (defaultPageConfig "page1") page1
    servePageWith "/iro" (defaultPageConfig "iro")
      (testPage "iro" (pack . show $ midtype o)
        [ ("input", mempty)
        , ("representation", mempty)
        , ("output", mempty)
        ])
    servePageWith "/" (defaultPageConfig "prod")
      (testPage "prod" (pack . show $ midtype o)
       [ ("input", mempty)
       , ("output",
          (bool mempty
            (toHtml (show initBridgeTest))
            (midtype o == Bridge)))
       ])