packages feed

Shpadoinkle-isreal-0.0.0.2: Main.hs

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE KindSignatures        #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE RecordWildCards       #-}
{-# LANGUAGE TypeApplications      #-}


{-
  This file is part of Shpadoinkle Isreal.

  Shpadoinkle Isreal is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Shpadoinkle Isreal is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Shpadoinkle Isreal.  If not, see <https://www.gnu.org/licenses/>.
-}


module Main where


import           Control.Monad            (unless)
import           Control.Monad.IO.Class   (MonadIO (..))
import           Data.ByteString.Lazy     as BSL (writeFile)
import           Data.String              (fromString)
import           Data.Text                as T (Text, intercalate, unpack)
import           Data.Text.Lazy           as T (fromStrict, pack, unlines)
import qualified Data.Text.Lazy.IO        as T
import           Network.Wai.Handler.Warp (run)
import           Servant
import qualified System.Directory         as Dir
import           System.Environment       (getEnv)
import           System.Exit              (ExitCode (ExitFailure, ExitSuccess))
import           System.FilePath          ((</>))
import           System.Process           (proc, readCreateProcessWithExitCode)


import           Shpadoinkle.Isreal.Types


getDir :: Options -> SnowToken -> FilePath
getDir Options {..} snow = territory </> T.unpack (unSnowToken snow)


compile :: MonadIO m => Options -> SnowToken -> SnowNonce -> Code -> m (Either CompileError Text)
compile options@Options {..} snow nonce (Code code) = liftIO $ do
  let dir = getDir options snow
  Dir.createDirectoryIfMissing False dir
  BSL.writeFile (dir </> "Main.hs") code
  isCabal <- Dir.doesFileExist $ dir </> "swan.cabal"
  unless isCabal $ Dir.createFileLink (swan </> "swan.cabal") $ dir </> "swan.cabal"
  Dir.setCurrentDirectory dir
  (exit, _, err) <- readCreateProcessWithExitCode (proc "cabal" ["build", "--ghcjs"]) ""
  case exit of
    ExitSuccess   -> do
      T.writeFile (dir </> artifactPath </> "index.html") mkIndex
      return $ Right "Compiled!"
    ExitFailure _ -> return . Left . CompileError $ fromString err

  where
  mkIndex = T.unlines
    [ "<!DOCTYPE html>"
    , "<html>"
    , "  <head>"
    , "    <script language=\"javascript\" src=\"rts.js\"></script>"
    , "    <script language=\"javascript\" src=\"lib.js\"></script>"
    , "    <script language=\"javascript\" src=\"out.js"
      <> "?token=" <> fromStrict (toQueryParam snow)
      <> "&nonce=" <> pack (show nonce) <> "\"></script>"
    , "  </head>"
    , "  <body>"
    , "  </body>"
    , "  <script language=\"javascript\" src=\"runmain.js\" defer></script>"
    , "</html>"
    ]


clean :: MonadIO m => Options -> SnowToken -> m Text
clean options snow = liftIO $ do
  Dir.removePathForcibly $ getDir options snow
  return $ unSnowToken snow <> " is clean"


cleanAll :: MonadIO m => Options -> m Text
cleanAll Options {..} = liftIO $ do
  Dir.removePathForcibly territory
  Dir.createDirectory territory
  return "All is clean in Colorado"


static :: Options -> SnowToken -> ServerT Raw m
static options snow = serveDirectoryWebApp $ getDir options snow </> artifactPath


artifactPath :: FilePath
artifactPath = "dist-newstyle/build/x86_64-linux/ghcjs-8.6.0.1/swan-0.1.0.0/x/swan/build/swan/swan.jsexe/"


welcome :: Text
welcome = intercalate "\n"
  [ "Isreal Swan is a microservice for processing haskell files, and serving GHCJS artifacts."
  , "There is no UI."
  , "To learn more please visit the README.md located here: https://gitlab.com/platonic/shpadoinkle/-/blob/master/isreal/README.md"
  ]


api :: Int -> Options -> IO ()
api port options = run port $ Servant.serve (Proxy @API) $ pure
  :<|> compile  options
  :<|> clean    options
  :<|> cleanAll options
  :<|> static   options
  :<|> pure welcome


main :: IO ()
main = do
  Prelude.putStrLn "starting to build snowmen"
  options <- Options <$> getEnv "TERRITORY" <*> getEnv "SWAN"
  port <- read <$> getEnv "PORT"
  api port options