{-# LANGUAGE OverloadedStrings #-} {- | Module : Main Description : A simple journal program Copyright : 2014, Peter Harpending. License : BSD3 Maintainer : Peter Harpending Stability : experimental Portability : archlinux -} module Main where import Control.Applicative import Data.Aeson hiding (encode) import Data.Aeson.Encode.Pretty import qualified Data.ByteString.Lazy as Bl import Data.Monoid import qualified Data.Text.Lazy as Tl import Data.Time import Options.Applicative import System.IO main :: IO () main = do inputBytes <- Bl.hGetContents stdin runInput inputBytes =<< execParser opts where opts = info (helper <*> inputParser) ( fullDesc <> header "tn - a simple journal program." ) inputParser :: Parser TnInput inputParser = TnInput <$> switch ( short 'n' <> long "new-journal" <> help "Create a new journal" ) <*> strOption ( short 'e' <> long "new-entry" <> help "Create a new entry with TEXT" <> metavar "TEXT" ) runInput :: Bl.ByteString -> TnInput -> IO () runInput bs ipt = Bl.hPut stdout . encode =<< processInput bs ipt processInput :: Bl.ByteString -> TnInput -> IO Journal processInput inputBytes (TnInput nj ne) = do ijournal <- if nj then return [] else getJournal mkNewEntry ne ijournal where getJournal :: IO Journal getJournal = case eitherDecode inputBytes of Left errorMsg -> fail errorMsg Right journal -> return journal mkNewEntry :: String -> Journal -> IO Journal mkNewEntry inputStr ijournal = do ct <- getZonedTime let inputText = Tl.pack inputStr entry = Entry ct inputText return $ entry:ijournal -- Boring stuff type Journal = [Entry] data Entry = Entry { date :: ZonedTime , entryText :: Tl.Text } instance FromJSON Entry where parseJSON (Object v) = Entry <$> v .: "date" <*> v .: "text" parseJSON _ = fail "Entry must be an object" instance ToJSON Entry where toJSON (Entry dte txt) = object [ "date" .= dte , "text" .= txt ] data TnInput = TnInput { newJournal :: Bool , newEntryStr :: String } encode :: ToJSON a => a -> Bl.ByteString encode = encodePretty' defConfig { confIndent = 2 }