module Taskwarrior.IO
( getTasks
, saveTasks
, createTask
, getUUIDs
, onAdd
, onAddPure
, onModify
, onModifyPure
)
where
import Taskwarrior.Task ( Task
, makeTask
)
import Data.Text ( Text )
import qualified Data.Text as Text
import qualified Data.ByteString as BS
import qualified Data.ByteString.Lazy as LBS
hiding ( putStrLn )
import qualified Data.ByteString.Lazy.Char8 as LBS
import qualified Data.Aeson as Aeson
import System.Process ( withCreateProcess
, CreateProcess(..)
, proc
, StdStream(..)
, waitForProcess
)
import System.IO ( hClose )
import System.Exit ( ExitCode(..) )
import Control.Monad ( when )
import System.Random ( getStdRandom
, random
)
import Data.Time ( getCurrentTime )
import Data.UUID ( UUID )
import qualified Data.UUID as UUID
getTasks :: [Text] -> IO [Task]
getTasks args =
withCreateProcess
((proc "task" (fmap Text.unpack . (++ ["export"]) $ args))
{ std_out = CreatePipe
}
)
$ \_ stdoutMay _ _ -> do
stdout <- maybe
(fail "Couldn‘t create stdout handle for `task export`")
pure
stdoutMay
input <- LBS.hGetContents stdout
either fail return . Aeson.eitherDecode $ input
getUUIDs :: [Text] -> IO [UUID]
getUUIDs args =
withCreateProcess
((proc "task" (fmap Text.unpack . (++ ["_uuid"]) $ args)) { std_out = CreatePipe
}
)
$ \_ stdoutMay _ _ -> do
stdout <- maybe
(fail "Couldn‘t create stdout handle for `task _uuid`")
pure
stdoutMay
input <- LBS.hGetContents stdout
maybe (fail "Couldn't parse UUIDs") return
. traverse UUID.fromLazyASCIIBytes
. LBS.lines
$ input
saveTasks :: [Task] -> IO ()
saveTasks tasks =
withCreateProcess ((proc "task" ["import"]) { std_in = CreatePipe })
$ \stdinMay _ _ process -> do
stdin <- maybe (fail "Couldn‘t create stdin handle for `task import`")
pure
stdinMay
LBS.hPut stdin . Aeson.encode $ tasks
hClose stdin
exitCode <- waitForProcess process
when (exitCode /= ExitSuccess) $ fail . show $ exitCode
createTask :: Text -> IO Task
createTask description = do
uuid <- getStdRandom random
entry <- getCurrentTime
pure $ makeTask uuid entry description
onModifyPure :: (Task -> Task -> Task) -> IO ()
onModifyPure f = onModify (\x y -> pure (f x y))
onModifyError :: String
onModifyError = "OnModify hook couldn‘t parse task."
onModify :: (Task -> Task -> IO Task) -> IO ()
onModify f = do
original <- readTaskLine onModifyError
modified <- readTaskLine onModifyError
LBS.putStrLn . Aeson.encode =<< f original modified
readTaskLine :: String -> IO Task
readTaskLine errorMsg =
maybe (fail errorMsg) pure . Aeson.decode' . LBS.fromStrict =<< BS.getLine
onAddPure :: (Task -> Task) -> IO ()
onAddPure f = onAdd (pure . f)
onAdd :: (Task -> IO Task) -> IO ()
onAdd f = LBS.putStrLn . Aeson.encode =<< f =<< readTaskLine
"OnAdd hook couldn‘t parse task."