headergen-0.1.0.0: src/Main.hs
{- |
Module : $Header$
Description : The main module of the application.
Copyright : (c) 2015 Nils 'bash0r' Jonsson
License : MIT
Maintainer : aka.bash0r@gmail.com
Stability : unstable
Portability : non-portable (Portability is not tested.)
The main module is the entry point of the application.
-}
{-# LANGUAGE OverloadedStrings #-}
module Main
( main
) where
import Control.Applicative
import Control.Monad
import Data.Aeson
import Data.Aeson.Encode.Pretty
import qualified Data.ByteString.Lazy as BS
import Data.Time.Clock
import Data.Time.Calendar
import System.FilePath.Posix
import System.Console.Haskeline
import System.Directory
import System.Environment
import System.IO
date :: IO (Integer, Int, Int)
date = liftM (toGregorian . utctDay) getCurrentTime
-- | The configuration is used to read / write configuration to JSON.
data Configuration = Configuration
{ getModule :: String
, getDescription :: String
, getAuthor :: String
, getCopyright :: String
, getLicense :: String
, getMaintainer :: String
, getStability :: String
, getPortability :: String
}
defaultConfiguration = Configuration
{ getModule = "$Header$"
, getDescription = ""
, getAuthor = ""
, getCopyright = ""
, getLicense = ""
, getMaintainer = ""
, getStability = "unstable"
, getPortability = "non-portable (Portability is untested.)"
}
instance ToJSON Configuration where
toJSON (Configuration mod desc auth copyr lic maint stab portab) =
object [ "module" .= mod
, "author" .= auth
, "description" .= desc
, "copyright" .= copyr
, "license" .= lic
, "maintainer" .= maint
, "stability" .= stab
, "portability" .= portab
]
instance FromJSON Configuration where
parseJSON (Object o) =
Configuration <$> o .: "module"
<*> o .: "description"
<*> o .: "author"
<*> o .: "copyright"
<*> o .: "license"
<*> o .: "maintainer"
<*> o .: "stability"
<*> o .: "portability"
parseJSON _ =
empty
-- | Try to get the parent directory of a given path.
parentDirectory :: FilePath -> Maybe FilePath
parentDirectory path =
let splitted = splitDirectories path
reversed = reverse splitted
dropped = drop 1 reversed
rereversed = reverse dropped
newPath = joinPath rereversed
in case newPath of
[] -> Nothing
otherwise -> Just otherwise
-- | Traverses the path up to root directory.
traversePath :: String -> FilePath -> IO ()
traversePath mod cwd = do
putStrLn ("Checking " ++ cwd ++ " ...")
let headerGenDef = cwd </> ".headergen.def"
exists <- doesFileExist headerGenDef
if exists
then do
h <- openFile headerGenDef ReadMode
cs <- BS.hGetContents h
let hgd = decode cs :: Maybe Configuration
case hgd of
Just a -> createHeader mod a
Nothing -> printCorruptedMessage
hClose h
else case parentDirectory cwd of
Just a -> traversePath mod a
Nothing -> printExitMessage
-- | Print message about a corrupted .headergen.def.
printCorruptedMessage :: IO ()
printCorruptedMessage = do
putStrLn "The .headergen.def is corrupted."
putStrLn "Exitting now."
putStrLn ""
-- | Print the exit message.
printExitMessage :: IO ()
printExitMessage = do
putStrLn "There is no .headergen.def in any parent directory."
putStrLn "Exitting now."
putStrLn ""
requestForAllowance :: IO Bool
requestForAllowance = runInputT defaultSettings allowanceRequest
where
allowanceRequest = do
line <- getInputLine "File for module exists already. Override? [y/n] "
case line of
(Just "y") -> return True
(Just "n") -> return False
_ -> allowanceRequest
-- | Create the header.
createHeader :: String -> Configuration -> IO ()
createHeader mod conf = do
exists <- doesFileExist mod
if exists
then do
v <- requestForAllowance
if v
then printHeader mod conf
else printExit
else printHeader mod conf
printExit :: IO ()
printExit = do
putStrLn "No resources have been created."
putStrLn "Exiting now."
putStrLn ""
printHeader :: String -> Configuration -> IO ()
printHeader modFile (Configuration mod des aut cop lic mai sta por) = do
h <- openFile modFile WriteMode
hPutStrLn h "{- |"
hPutStrLn h ("Module : " ++ mod)
hPutStrLn h ("Description : " ++ des)
hPutStrLn h ("Author : " ++ aut)
hPutStrLn h ("Copyright : " ++ cop)
hPutStrLn h ("License : " ++ lic)
hPutStrLn h ""
hPutStrLn h ("Maintainer : " ++ mai)
hPutStrLn h ("Stability : " ++ sta)
hPutStrLn h ("Portability : " ++ por)
hPutStrLn h ""
hPutStrLn h "TODO: module description"
hPutStrLn h "-}"
hFlush h
hClose h
def :: Maybe String -> String -> String
def v d =
case v of
Just v ->
if null v
then d
else v
Nothing ->
d
alignText :: String -> String
alignText xs =
if length xs < 8
then alignText (xs ++ " ")
else xs
defaultText :: String -> String
defaultText crt =
if length crt >= 8
then take 8 crt
else alignText crt
requestConfiguration :: IO Configuration
requestConfiguration = do
(year, _, _) <- date
runInputT defaultSettings $ do
mod <- getInputLine "Module [$Header$]: "
desc <- getInputLine "Description [ ]: "
auth <- getInputLine "Author [ ]: "
let copyRightText = "(c) " ++ show year ++ " " ++ def auth ""
copyr <- getInputLine ("Copyright [" ++ defaultText copyRightText ++ "]: ")
lic <- getInputLine "License [MIT ]: "
maint <- getInputLine "Maintainer [ ]: "
stab <- getInputLine "Stability [unstable]: "
portab <- getInputLine "Portability [non-port]: "
return $ Configuration (def mod "$Header$")
(def desc "")
(def auth "")
(def copyr copyRightText)
(def lic "MIT")
(def maint "")
(def stab "unstable")
(def portab "non-portable (Portability is untested.)")
initializeDefinition :: IO ()
initializeDefinition = do
conf <- requestConfiguration
file <- openFile ".headergen.def" WriteMode
BS.hPutStr file (encodePretty conf)
hFlush file
hClose file
tryCreate :: String -> IO ()
tryCreate mod = do
cwd <- getCurrentDirectory
traversePath mod cwd
printHelpMessage :: IO ()
printHelpMessage = do
putStrLn "Usage: [init|create MODULE]"
putStrLn ""
-- | The entry point of the application.
main :: IO ()
main = do
args <- getArgs
handleParameters args
where
handleParameters ["init"] = initializeDefinition
handleParameters ["create", mod] = tryCreate (mod -<.> ".hs")
handleParameters _ = printHelpMessage