Haskell Persistent note recording program

10 April, 2018

Here's a version of the previous notes program that uses Persistent.

The imports are:

  • text
  • cmdargs
  • friendly-time
  • time
  • persistent
  • persistent-sqlite
  • persistent-template
  • transformers

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}

module Main where

import System.Console.CmdArgs
import Control.Monad (when, void)
import Database.Persist
import Database.Persist.TH
import Database.Persist.Sqlite
import Control.Monad.IO.Class (liftIO)
import Control.Monad.Trans.Reader
import Data.Time
import Data.List (intercalate)
import Data.Time.Format.Human

share
    [mkPersist sqlSettings, mkMigrate "migrateAll"]
    [persistLowerCase|
Note
    message String
    tags String Maybe
    createdAt UTCTime default=CURRENT_TIME
    deriving Show
|]

data Options = Options
    { inputMode :: Bool
    , message :: String
    , delete :: Integer
    , humanTime :: Bool
    , tags :: Maybe String
    , update :: Integer
    , showTag :: Maybe String
    } deriving (Data, Typeable)

options :: Options
options =
    Options
    { inputMode = False &= typ "Input mode" &= help "Toggle input mode"
    , message = def &= typ "message" &= opt ("" :: String) &= args
    , Main.delete = def &= typ "[number]" &= help "Delete a note."
    , humanTime =
        False &= typ "Relative time" &= help "Show time relative to now in human readable format."
    , tags = def &= typ "tags" &= help "Tag a note."
    , Main.update = def &= typ "[number]" &= help "Modify a note."
    , showTag = def &= typ "TAG" &= help "Show entries with given tag."
    } &=
    summary "n notes" &=
    program "n"

showNote :: Bool -> Entity Note -> IO ()
showNote humanizeTime note = do
    let n = entityVal note
    let t' = noteCreatedAt n
    let key = show $ fromSqlKey $ entityKey note
    time <-
        if humanizeTime
            then humanReadableTime t'
            else return $ show t'
    t <-
        case noteTags n of
            Nothing -> return ""
            Just xs -> return $ "#" ++ xs
    let o = intercalate " | " [key, time, show (noteMessage n), t]
    print o


asSqlBackendReader :: ReaderT SqlBackend m a -> ReaderT SqlBackend m a
asSqlBackendReader = id

main :: IO ()
main =
    runSqlite "testn.db" $
    asSqlBackendReader $
    do opts <- liftIO $ cmdArgs options
       runMigration migrateAll
       time <- liftIO getCurrentTime
       when (message opts /= "") $ void $ insert $ Note (message opts) (tags opts) time
       when
           (inputMode opts)
           (do content <- liftIO getContents
               void $ insert $ Note content (tags opts) time
               return ())
       when
           (Main.delete opts /= 0)
           (do let key = toSqlKey $ fromIntegral (Main.delete opts) :: Key Note
               Database.Persist.Sqlite.delete key)
       notes <-
           case showTag opts of
               Nothing -> selectList [] [Asc NoteCreatedAt]
               Just x -> selectList [NoteTags ==. Just x] [Asc NoteCreatedAt]
       if humanTime opts
           then liftIO $ mapM_ (showNote True) notes
           else liftIO $ mapM_ (showNote False) notes