summaryrefslogtreecommitdiffstats
path: root/src/Notmuch.hs
blob: 310657af2e0fb94f3cdda88cdb0d1fffb99da81f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedStrings #-}
module Notmuch where

import qualified Data.ByteString.Lazy as LBS
import qualified Data.ByteString.Lazy.Char8 as LBS8
import qualified Data.Text.Lazy as LT
import qualified Data.Text.Lazy.Encoding as LT
import qualified Network.Mail.Mime as M
import Data.Aeson.Extends
import Data.Either.Combinators (mapRight)
import Data.Functor ((<&>))
import Data.Tree
import Notmuch.Class
import Notmuch.Message
import Notmuch.SearchResult
import Much.ParseMail (readMail)
import System.Exit
import System.Process.ByteString.Lazy (readProcessWithExitCode)
import Much.TagUtils


notmuch :: [String] -> IO LBS.ByteString
notmuch args = do
    (_exitCode, out, _err) <- notmuch' args
    return out


notmuch' :: [String] -> IO (ExitCode, LBS.ByteString, LBS.ByteString)
notmuch' args =
    readProcessWithExitCode "notmuch" args ""


search :: [String] -> IO (Either String [SearchResult])
search args =
    eitherDecodeLenient' <$>
        notmuch ("search" : "--format=json" : "--format-version=2" : args)


data ReplyTo = ToAll | ToSender
instance Show ReplyTo where
    show ToAll = "all"
    show ToSender = "sender"

--notmuchReply :: String -> IO (Either String [SearchResult])
notmuchReply :: ReplyTo -> String -> IO LBS.ByteString
notmuchReply replyTo term =
    notmuch
        [ "reply"
        , "--reply-to=" ++ show replyTo
        , term
        ]
 --       >>= return . eitherDecodeLenient'


notmuchShow :: String -> IO (Forest Message)
notmuchShow term = do
    c' <- notmuch [ "show", "--format=json", "--format-version=2"
                   , term ]
    -- TODO why head?
    return $ threadForest $ head $
        either error id (eitherDecodeLenient' c')


notmuchShowPartRaw :: String -> Int -> IO (Either String LBS8.ByteString)
notmuchShowPartRaw term partId = do
    (exitCode, out, err) <-
        notmuch' [ "show", "--format=raw"
                 , "--part=" <> show partId
                 , term ]
    return $ case exitCode of
        ExitSuccess -> Right out
        _ -> Left $ show exitCode <> ": " <> LBS8.unpack err

notmuchShowPart :: String -> Int -> IO (Either String MessagePart)
notmuchShowPart term partId = do
    -- TODO handle partId == 0 and partId > N
    (exitCode, out, err) <-
        notmuch' [ "show", "--format=json", "--format-version=2"
                 , "--part=" <> show partId
                 , term ]
    case exitCode of
        ExitSuccess ->
          case eitherDecodeLenient' out of
            Right mp -> do
              case partContent mp of
                ContentRaw "" contentLength ->
                  notmuchShowPartRaw term partId <&> mapRight (\raw ->
                      mp { partContent = ContentRaw raw contentLength }
                    )
                _ ->
                  return $ Right mp
            Left err2 ->
              return $ Left err2
        _ ->
          return $ Left $ show exitCode <> ": " <> LBS8.unpack err


notmuchShowMail :: String -> IO (Either String M.Mail)
notmuchShowMail term =
    notmuch' [ "show", "--format=raw", "--format-version=2", term ]
    >>= return . \case
      (ExitSuccess, out, _) ->
          case LT.decodeUtf8' out of
              Right x -> Right (readMail $ LT.toStrict x)
              Left ex -> Left $ "meh" ++ show ex
      (exitCode, _, err) ->
          Left $ "notmuch failed with exit code " ++ show exitCode ++
                 ": " ++ LBS8.unpack err


notmuchTag :: HasNotmuchId a => [TagOp] -> a -> IO ()
notmuchTag tagOps x =
    notmuch ("tag" : tagOpsToArgs tagOps ++ [notmuchId x]) >> return ()