packages feed

simple-1.0.0: src/smpl.hs

{-# LANGUAGE DeriveDataTypeable, OverloadedStrings #-}

-- | The `smpl` utility for helping a user setup a Simple web project.
module Main (main) where

import Prelude hiding (writeFile, FilePath, all)
import Control.Monad (when)
import Data.Aeson
import Data.Char
import qualified Data.ByteString.Char8 as S8
import qualified Data.Text.Encoding as T
import Data.Version
import System.Console.CmdArgs
import System.Directory
import System.FilePath
import System.Environment (getEnvironment)
import System.SetEnv (setEnv)
import System.Exit
import System.Process
import Web.Simple.Templates.Language

import Paths_simple

data Smpl =
    Server
      { port :: Int
      , moduleName :: String
      } |
    Create { appDir :: FilePath
           , includeTemplates :: Bool
           , includePostgresql :: Bool
           , includeSessions :: Bool
           , includeAll :: Bool }
    deriving (Show, Data, Typeable)

main :: IO ()
main = do
    setEnv "ENV" "development"
    myenv <- getEnvironment
    let myport = maybe 3000 read $ lookup "PORT" myenv
    let develModes = modes
                  [ Server { port = myport &= typ "PORT"
                           , moduleName = "Application" &= typ "MODULE"
                                        &= explicit &= name "module"
                           } &= auto &= help "Run a development server"
                           &= details [
                            "You must have wai-handler-devel installed " ++
                            "to run this command"]
                  , Create { appDir = "" &= argPos 0 &= typ "app_dir"
                           , includeTemplates = False
                                     &= help "include templates"
                                     &= explicit &= name "templates"
                                     &= groupname "Plugins"
                           , includePostgresql = False
                                     &= help "include postgresql-orm"
                                     &= explicit &= name "postgresql"
                           , includeSessions = False
                                     &= help "include cookie-based sessions"
                                     &= explicit &= name "sessions"
                           , includeAll = False
                                     &= help
                                          ("include templates, cookie-based " ++
                                           "sessions and postgresql")
                                     &= explicit &= name "all"}
                          &= help "Create a new application in app_dir"]
    smpl <- cmdArgsRun $ cmdArgsMode $
      develModes &= (summary $
        "Simple web framework " ++ (showVersion version))
    case smpl of
      Server p m -> do
        exitCode <- rawSystem "wai-handler-devel" [show p, m, "app"]
        case exitCode of
          ExitFailure 127 -> do
            putStrLn "You must install wai-handler devel first"
            exitWith $ ExitFailure 1
          _ -> exitWith exitCode
      Create dir tmpls pg sess all ->
        createApplication dir (all || tmpls) (all || sess) (all || pg)

humanize :: String -> String
humanize = capitalize
  where go [] = []
        go ('_':xs) = ' ':(capitalize xs)
        go (x:xs) = x:(go xs)
        capitalize [] = []
        capitalize x@('_':_) = go x
        capitalize (x:xs) = (toUpper x):(go xs)

moduleCase :: String -> String
moduleCase = capitalize
  where go [] = []
        go ('_':xs) = capitalize xs
        go (x:xs) = x:(go xs)
        capitalize [] = []
        capitalize ('_':xs) = go xs
        capitalize (x:xs) = (toUpper x):(go xs)

createApplication :: FilePath -> Bool -> Bool -> Bool -> IO ()
createApplication dir tmpls sessions postgresql = do
  let myAppName = takeBaseName $ dropTrailingPathSeparator dir
      modName = moduleCase myAppName
      mappings = object
                  [ "appname" .= myAppName
                  , "name" .= humanize myAppName
                  , "module" .= modName
                  , "include_templates" .= tmpls
                  , "include_sessions" .= sessions
                  , "include_postgresql" .= postgresql]

  createDirectory dir
  createDirectory $ dir </> modName
  copyTemplate ("template" </> "Main_hs.tmpl")
               (dir </> "Main.hs") mappings
  copyTemplate ("template" </> "Application_hs.tmpl")
               (dir </> "Application.hs") mappings
  copyTemplate ("template" </> "package_cabal.tmpl")
               (dir </> myAppName ++ ".cabal") mappings
  copyTemplate ("template" </> "Common_hs.tmpl")
               (dir </> modName </> "Common.hs") mappings

  when postgresql $ do
    createDirectory $ dir </> "db"
    createDirectory $ dir </> "db" </> "migrations"

  when tmpls $ do
    createDirectory $ dir </> "views"
    createDirectory $ dir </> "layouts"
    copyTemplate ("template" </> "main_html.tmpl")
                 (dir </> "layouts" </> "main.html") mappings
    copyTemplate ("template" </> "index_html.tmpl")
                 (dir </> "views" </> "index.html") mappings

copyTemplate :: FilePath -> FilePath -> Value -> IO ()
copyTemplate orig target mappings = do
  etmpl <- compileTemplate <$> T.decodeUtf8 <$>
    (S8.readFile =<< getDataFileName orig)
  case etmpl of
    Left err -> fail err
    Right tmpl -> S8.writeFile target $ T.encodeUtf8 $
      renderTemplate tmpl mempty mappings