multi color filter tail

root / TailTypes.hs

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 CPP, Rank2Types, OverloadedStrings #-}
module TailTypes
  ( Interval
  , threadDelayInterval
  , TailTarget(..)
  , TailRuntime(..)
  , TailMatch(..)
  , TailAction(..)
  , TailMatches
  , Tail(..)
  , tailName
  , tailErrMsg
  , tailOutput
  ) where

import Control.Arrow (first)
import Control.Concurrent (threadDelay)
import qualified Data.ByteString.Char8 as BS
import qualified Data.Fixed as Fixed
import Data.Monoid ((<>))
#ifdef INOTIFY
import qualified System.INotify as INotify
#endif
import System.Posix.Types (Fd)
import System.Posix.IO (stdInput)
import Text.Regex.Posix (Regex)

import Display

newtype Interval = Interval { intervalMicroseconds :: Int }
  deriving (Eq, Ord, Bounded)

intervalToFixed :: Interval -> Fixed.Micro
intervalToFixed (Interval i) = Fixed.MkFixed (toInteger i)

intervalFromFixed :: Fixed.Micro -> Interval
intervalFromFixed (Fixed.MkFixed i)
  | i < 0 = error "intervalFromFixed: negative interval"
  | i > toInteger (intervalMicroseconds maxBound) = error "intervalFromFixed: interval too large"
  | otherwise = Interval (fromInteger i)

instance Show Interval where
  show = Fixed.showFixed True . intervalToFixed
instance Read Interval where
  readsPrec n s = map (first intervalFromFixed) $ readsPrec n s
instance Num Interval where
  Interval x + Interval y = Interval (x + y)
  x * y = intervalFromFixed $ intervalToFixed x * intervalToFixed y
  Interval x - Interval y = Interval (x - y)
  negate (Interval x) = Interval (negate x)
  abs (Interval x) = Interval (abs x)
  signum (Interval x) = Interval (signum x)
  fromInteger = intervalFromFixed . fromInteger

threadDelayInterval :: Interval -> IO ()
threadDelayInterval (Interval i) = threadDelay i

data TailTarget
  = TailPath !FilePath
  | TailFd !Fd
instance Show TailTarget where
  show (TailFd 0) = "-"
  show (TailFd x) = '&':(show x)
  show (TailPath path) = path
instance Read TailTarget where
  readsPrec _ "-" = [(TailFd stdInput, "")]
  readsPrec n ('&':s) = map (first TailFd) $ readsPrec n s
  readsPrec _ s = [(TailPath s, "")]

data TailMatch =
    MatchAll
  | MatchRegex !Regex
  | MatchNotRegex !Regex
data TailAction =
    ActionNone
  | ActionHide
  | ActionColor !TermColor
  | ActionSubst !BS.ByteString
  | ActionExecute !BS.ByteString
type TailMatches = [(TailMatch, TailAction)]

data Tail = Tail
  { tailTarg :: TailTarget
  , tailPollInterval :: !Interval
  , tailReopenInterval :: !Interval
#ifdef INOTIFY
  , tailPollINotify :: !Bool
  , tailReopenINotify :: !Bool
#endif
  , tailBegin :: !Bool -- start at beginning of file
  , tailFileTail :: !Bool -- enable this tail for non-directories (almost always True)
  , tailDirTail :: !Bool -- tail immediate children
  , tailDirList :: !Bool -- enable this tail for directory content
  , tailDirRecursive :: !Bool -- tail children recursively
  , tailTimeFmt :: !String
  , tailMatches :: !TailMatches
  }

tailName :: Tail -> BS.ByteString
tailName = BS.pack . show . tailTarg

data TailRuntime = TailRuntime
  { trOutput :: Output -> IO ()
  , trAddTail :: Tail -> IO ()
#ifdef INOTIFY
  , trINotify :: Maybe INotify.INotify
#endif
}

tailErrMsg :: TailRuntime -> Tail -> BS.ByteString -> IO ()
tailErrMsg r t = trOutput r . OutputError . (("ztail " <> tailName t <> ": ") <>)

tailOutput :: TailRuntime -> TermColor -> BS.ByteString -> IO ()
tailOutput tr c = trOutput tr . OutputLine c