module Systemd.Journalctl.Stream (
Entry (..)
, Cursor
, StreamStart (..)
, entryStream
, Exception (..)
) where
import System.IO (Handle)
import Data.Maybe (fromJust)
import Control.Exception qualified as Base
import System.Posix.Types (CPid (..), ProcessID)
import Data.Foldable (toList)
import Data.Text (Text)
import Data.Text.Encoding (encodeUtf8)
import Data.Text qualified as Text
import Data.ByteString (ByteString)
import Data.ByteString qualified as ByteString
import Data.Aeson (FromJSON, parseJSON, (.:), (.:?), ToJSON)
import Data.Aeson qualified as JSON
import Data.Aeson.Types qualified as JSON
import Data.Aeson.KeyMap qualified as KeyMap
import Data.Time.Clock (secondsToNominalDiffTime)
import Data.Time.Clock.POSIX (POSIXTime)
import Data.Time.LocalTime (LocalTime)
import Data.Time.Format (formatTime, defaultTimeLocale)
import System.Process qualified as System
import Conduit (MonadResource, MonadThrow, throwM)
import Data.Conduit (ConduitT, (.|))
import Data.Conduit.Combinators qualified as Conduit
newtype Cursor = Cursor Text deriving (Cursor -> Cursor -> Bool
(Cursor -> Cursor -> Bool)
-> (Cursor -> Cursor -> Bool) -> Eq Cursor
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Cursor -> Cursor -> Bool
== :: Cursor -> Cursor -> Bool
$c/= :: Cursor -> Cursor -> Bool
/= :: Cursor -> Cursor -> Bool
Eq, Eq Cursor
Eq Cursor =>
(Cursor -> Cursor -> Ordering)
-> (Cursor -> Cursor -> Bool)
-> (Cursor -> Cursor -> Bool)
-> (Cursor -> Cursor -> Bool)
-> (Cursor -> Cursor -> Bool)
-> (Cursor -> Cursor -> Cursor)
-> (Cursor -> Cursor -> Cursor)
-> Ord Cursor
Cursor -> Cursor -> Bool
Cursor -> Cursor -> Ordering
Cursor -> Cursor -> Cursor
forall a.
Eq a =>
(a -> a -> Ordering)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> Bool)
-> (a -> a -> a)
-> (a -> a -> a)
-> Ord a
$ccompare :: Cursor -> Cursor -> Ordering
compare :: Cursor -> Cursor -> Ordering
$c< :: Cursor -> Cursor -> Bool
< :: Cursor -> Cursor -> Bool
$c<= :: Cursor -> Cursor -> Bool
<= :: Cursor -> Cursor -> Bool
$c> :: Cursor -> Cursor -> Bool
> :: Cursor -> Cursor -> Bool
$c>= :: Cursor -> Cursor -> Bool
>= :: Cursor -> Cursor -> Bool
$cmax :: Cursor -> Cursor -> Cursor
max :: Cursor -> Cursor -> Cursor
$cmin :: Cursor -> Cursor -> Cursor
min :: Cursor -> Cursor -> Cursor
Ord, Int -> Cursor -> ShowS
[Cursor] -> ShowS
Cursor -> String
(Int -> Cursor -> ShowS)
-> (Cursor -> String) -> ([Cursor] -> ShowS) -> Show Cursor
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Cursor -> ShowS
showsPrec :: Int -> Cursor -> ShowS
$cshow :: Cursor -> String
show :: Cursor -> String
$cshowList :: [Cursor] -> ShowS
showList :: [Cursor] -> ShowS
Show)
instance FromJSON Cursor where
parseJSON :: Value -> Parser Cursor
parseJSON = String -> (Text -> Parser Cursor) -> Value -> Parser Cursor
forall a. String -> (Text -> Parser a) -> Value -> Parser a
JSON.withText String
"Cursor" ((Text -> Parser Cursor) -> Value -> Parser Cursor)
-> (Text -> Parser Cursor) -> Value -> Parser Cursor
forall a b. (a -> b) -> a -> b
$ Cursor -> Parser Cursor
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Cursor -> Parser Cursor)
-> (Text -> Cursor) -> Text -> Parser Cursor
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> Cursor
Cursor
instance ToJSON Cursor where
toJSON :: Cursor -> Value
toJSON (Cursor Text
t) = Text -> Value
JSON.String Text
t
data Entry = Entry
{
Entry -> Maybe ProcessID
entryPID :: Maybe ProcessID
, Entry -> Text
entryHostname :: Text
, Entry -> Maybe Text
entryNamespace :: Maybe Text
, Entry -> Maybe Text
entryProcess :: Maybe Text
, Entry -> Maybe String
entryExecutable :: Maybe FilePath
, Entry -> Cursor
entryCursor :: Cursor
, Entry -> POSIXTime
entryTimestamp :: POSIXTime
, Entry -> Maybe Text
entryUnit :: Maybe Text
, Entry -> Maybe (Either ByteString Text)
entryMessage :: Maybe (Either ByteString Text)
} deriving Int -> Entry -> ShowS
[Entry] -> ShowS
Entry -> String
(Int -> Entry -> ShowS)
-> (Entry -> String) -> ([Entry] -> ShowS) -> Show Entry
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Entry -> ShowS
showsPrec :: Int -> Entry -> ShowS
$cshow :: Entry -> String
show :: Entry -> String
$cshowList :: [Entry] -> ShowS
showList :: [Entry] -> ShowS
Show
newtype AsText a = AsText { forall a. AsText a -> a
asText :: a } deriving Int -> AsText a -> ShowS
[AsText a] -> ShowS
AsText a -> String
(Int -> AsText a -> ShowS)
-> (AsText a -> String) -> ([AsText a] -> ShowS) -> Show (AsText a)
forall a. Show a => Int -> AsText a -> ShowS
forall a. Show a => [AsText a] -> ShowS
forall a. Show a => AsText a -> String
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: forall a. Show a => Int -> AsText a -> ShowS
showsPrec :: Int -> AsText a -> ShowS
$cshow :: forall a. Show a => AsText a -> String
show :: AsText a -> String
$cshowList :: forall a. Show a => [AsText a] -> ShowS
showList :: [AsText a] -> ShowS
Show
instance FromJSON a => FromJSON (AsText a) where
parseJSON :: Value -> Parser (AsText a)
parseJSON = String -> (Text -> Parser (AsText a)) -> Value -> Parser (AsText a)
forall a. String -> (Text -> Parser a) -> Value -> Parser a
JSON.withText String
"AsText" ((Text -> Parser (AsText a)) -> Value -> Parser (AsText a))
-> (Text -> Parser (AsText a)) -> Value -> Parser (AsText a)
forall a b. (a -> b) -> a -> b
$
(String -> Parser (AsText a))
-> (a -> Parser (AsText a)) -> Either String a -> Parser (AsText a)
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either String -> Parser (AsText a)
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (AsText a -> Parser (AsText a)
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (AsText a -> Parser (AsText a))
-> (a -> AsText a) -> a -> Parser (AsText a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. a -> AsText a
forall a. a -> AsText a
AsText) (Either String a -> Parser (AsText a))
-> (Text -> Either String a) -> Text -> Parser (AsText a)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String a
forall a. FromJSON a => ByteString -> Either String a
JSON.eitherDecodeStrict (ByteString -> Either String a)
-> (Text -> ByteString) -> Text -> Either String a
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Text -> ByteString
encodeUtf8
instance FromJSON Entry where
parseJSON :: Value -> Parser Entry
parseJSON = String -> (Object -> Parser Entry) -> Value -> Parser Entry
forall a. String -> (Object -> Parser a) -> Value -> Parser a
JSON.withObject String
"Entry" ((Object -> Parser Entry) -> Value -> Parser Entry)
-> (Object -> Parser Entry) -> Value -> Parser Entry
forall a b. (a -> b) -> a -> b
$ \Object
o -> Maybe ProcessID
-> Text
-> Maybe Text
-> Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry
Entry
(Maybe ProcessID
-> Text
-> Maybe Text
-> Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
-> Parser (Maybe ProcessID)
-> Parser
(Text
-> Maybe Text
-> Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> ((AsText Int32 -> ProcessID)
-> Maybe (AsText Int32) -> Maybe ProcessID
forall a b. (a -> b) -> Maybe a -> Maybe b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (Int32 -> ProcessID
CPid (Int32 -> ProcessID)
-> (AsText Int32 -> Int32) -> AsText Int32 -> ProcessID
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AsText Int32 -> Int32
forall a. AsText a -> a
asText) (Maybe (AsText Int32) -> Maybe ProcessID)
-> Parser (Maybe (AsText Int32)) -> Parser (Maybe ProcessID)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser (Maybe (AsText Int32))
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"_PID")
Parser
(Text
-> Maybe Text
-> Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
-> Parser Text
-> Parser
(Maybe Text
-> Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser Text
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"_HOSTNAME"
Parser
(Maybe Text
-> Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
-> Parser (Maybe Text)
-> Parser
(Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"_NAMESPACE"
Parser
(Maybe Text
-> Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
-> Parser (Maybe Text)
-> Parser
(Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"_COMM"
Parser
(Maybe String
-> Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
-> Parser (Maybe String)
-> Parser
(Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (Maybe String)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"_EXE"
Parser
(Cursor
-> POSIXTime
-> Maybe Text
-> Maybe (Either ByteString Text)
-> Entry)
-> Parser Cursor
-> Parser
(POSIXTime
-> Maybe Text -> Maybe (Either ByteString Text) -> Entry)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser Cursor
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"__CURSOR"
Parser
(POSIXTime
-> Maybe Text -> Maybe (Either ByteString Text) -> Entry)
-> Parser POSIXTime
-> Parser (Maybe Text -> Maybe (Either ByteString Text) -> Entry)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> (Pico -> POSIXTime
secondsToNominalDiffTime (Pico -> POSIXTime)
-> (AsText Pico -> Pico) -> AsText Pico -> POSIXTime
forall b c a. (b -> c) -> (a -> b) -> a -> c
. (Pico -> Pico -> Pico
forall a. Fractional a => a -> a -> a
/Pico
1000000) (Pico -> Pico) -> (AsText Pico -> Pico) -> AsText Pico -> Pico
forall b c a. (b -> c) -> (a -> b) -> a -> c
. AsText Pico -> Pico
forall a. AsText a -> a
asText (AsText Pico -> POSIXTime)
-> Parser (AsText Pico) -> Parser POSIXTime
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Object
o Object -> Key -> Parser (AsText Pico)
forall a. FromJSON a => Object -> Key -> Parser a
.: Key
"__REALTIME_TIMESTAMP")
Parser (Maybe Text -> Maybe (Either ByteString Text) -> Entry)
-> Parser (Maybe Text)
-> Parser (Maybe (Either ByteString Text) -> Entry)
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object
o Object -> Key -> Parser (Maybe Text)
forall a. FromJSON a => Object -> Key -> Parser (Maybe a)
.:? Key
"UNIT"
Parser (Maybe (Either ByteString Text) -> Entry)
-> Parser (Maybe (Either ByteString Text)) -> Parser Entry
forall a b. Parser (a -> b) -> Parser a -> Parser b
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> Object -> Parser (Maybe (Either ByteString Text))
messageParser Object
o
messageParser :: JSON.Object -> JSON.Parser (Maybe (Either ByteString Text))
messageParser :: Object -> Parser (Maybe (Either ByteString Text))
messageParser Object
obj =
case Key -> Object -> Maybe Value
forall v. Key -> KeyMap v -> Maybe v
KeyMap.lookup Key
"MESSAGE" Object
obj of
Just (JSON.String Text
t) -> Maybe (Either ByteString Text)
-> Parser (Maybe (Either ByteString Text))
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Maybe (Either ByteString Text)
-> Parser (Maybe (Either ByteString Text)))
-> Maybe (Either ByteString Text)
-> Parser (Maybe (Either ByteString Text))
forall a b. (a -> b) -> a -> b
$ Either ByteString Text -> Maybe (Either ByteString Text)
forall a. a -> Maybe a
Just (Either ByteString Text -> Maybe (Either ByteString Text))
-> Either ByteString Text -> Maybe (Either ByteString Text)
forall a b. (a -> b) -> a -> b
$ Text -> Either ByteString Text
forall a b. b -> Either a b
Right Text
t
Just (JSON.Array Array
arr) -> Either ByteString Text -> Maybe (Either ByteString Text)
forall a. a -> Maybe a
Just (Either ByteString Text -> Maybe (Either ByteString Text))
-> ([Word8] -> Either ByteString Text)
-> [Word8]
-> Maybe (Either ByteString Text)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either ByteString Text
forall a b. a -> Either a b
Left (ByteString -> Either ByteString Text)
-> ([Word8] -> ByteString) -> [Word8] -> Either ByteString Text
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Word8] -> ByteString
ByteString.pack ([Word8] -> Maybe (Either ByteString Text))
-> Parser [Word8] -> Parser (Maybe (Either ByteString Text))
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> (Value -> Parser Word8) -> [Value] -> Parser [Word8]
forall (t :: * -> *) (m :: * -> *) a b.
(Traversable t, Monad m) =>
(a -> m b) -> t a -> m (t b)
forall (m :: * -> *) a b. Monad m => (a -> m b) -> [a] -> m [b]
mapM Value -> Parser Word8
forall a. FromJSON a => Value -> Parser a
parseJSON (Array -> [Value]
forall a. Vector a -> [a]
forall (t :: * -> *) a. Foldable t => t a -> [a]
toList Array
arr)
Just Value
JSON.Null -> Maybe (Either ByteString Text)
-> Parser (Maybe (Either ByteString Text))
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Either ByteString Text)
forall a. Maybe a
Nothing
Maybe Value
Nothing -> Maybe (Either ByteString Text)
-> Parser (Maybe (Either ByteString Text))
forall a. a -> Parser a
forall (f :: * -> *) a. Applicative f => a -> f a
pure Maybe (Either ByteString Text)
forall a. Maybe a
Nothing
Maybe Value
_ -> String -> Parser (Maybe (Either ByteString Text))
forall a. String -> Parser a
forall (m :: * -> *) a. MonadFail m => String -> m a
fail (String -> Parser (Maybe (Either ByteString Text)))
-> String -> Parser (Maybe (Either ByteString Text))
forall a b. (a -> b) -> a -> b
$ String
"Couldn't parse MESSAGE. Expected String, Array or Null."
data Exception = JSONError String deriving Int -> Exception -> ShowS
[Exception] -> ShowS
Exception -> String
(Int -> Exception -> ShowS)
-> (Exception -> String)
-> ([Exception] -> ShowS)
-> Show Exception
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Exception -> ShowS
showsPrec :: Int -> Exception -> ShowS
$cshow :: Exception -> String
show :: Exception -> String
$cshowList :: [Exception] -> ShowS
showList :: [Exception] -> ShowS
Show
instance Base.Exception Exception where
data StreamStart =
StartTime LocalTime
| Lines Int
| AtCursor Cursor
| AfterCursor Cursor
streamStartArgs :: StreamStart -> [String]
streamStartArgs :: StreamStart -> [String]
streamStartArgs (StartTime LocalTime
t) =
[ String
"--since", TimeLocale -> String -> LocalTime -> String
forall t. FormatTime t => TimeLocale -> String -> t -> String
formatTime TimeLocale
defaultTimeLocale String
"%F %T" LocalTime
t ]
streamStartArgs (Lines Int
n) =
[ String
"--lines" , Int -> String
forall a. Show a => a -> String
show Int
n ]
streamStartArgs (AtCursor (Cursor Text
t)) =
[ String
"--cursor", Text -> String
Text.unpack Text
t ]
streamStartArgs (AfterCursor (Cursor Text
t)) =
[ String
"--after-cursor", Text -> String
Text.unpack Text
t ]
entryStream
:: (MonadResource m, MonadThrow m)
=> StreamStart
-> ConduitT i Entry m ()
entryStream :: forall (m :: * -> *) i.
(MonadResource m, MonadThrow m) =>
StreamStart -> ConduitT i Entry m ()
entryStream StreamStart
start =
let args :: [String]
args :: [String]
args = StreamStart -> [String]
streamStartArgs StreamStart
start [String] -> [String] -> [String]
forall a. [a] -> [a] -> [a]
++ [ String
"--follow", String
"--output", String
"json" ]
hdl :: IO Handle
hdl :: IO Handle
hdl = ((Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-> Handle)
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-> IO Handle
forall a b. (a -> b) -> IO a -> IO b
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
fmap (\(Maybe Handle
_, Maybe Handle
h, Maybe Handle
_, ProcessHandle
_) -> Maybe Handle -> Handle
forall a. HasCallStack => Maybe a -> a
fromJust Maybe Handle
h)
(IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-> IO Handle)
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
-> IO Handle
forall a b. (a -> b) -> a -> b
$ CreateProcess
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
System.createProcess
(CreateProcess
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle))
-> CreateProcess
-> IO (Maybe Handle, Maybe Handle, Maybe Handle, ProcessHandle)
forall a b. (a -> b) -> a -> b
$ (String -> [String] -> CreateProcess
System.proc String
"journalctl" [String]
args)
{ System.std_out = System.CreatePipe
}
in IO Handle -> ConduitT i ByteString m ()
forall (m :: * -> *) i.
MonadResource m =>
IO Handle -> ConduitT i ByteString m ()
Conduit.sourceIOHandle IO Handle
hdl
ConduitT i ByteString m ()
-> ConduitT ByteString Entry m () -> ConduitT i Entry m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| ConduitT ByteString ByteString m ()
forall (m :: * -> *) seq.
(Monad m, IsSequence seq, Element seq ~ Word8) =>
ConduitT seq seq m ()
Conduit.linesUnboundedAscii
ConduitT ByteString ByteString m ()
-> ConduitT ByteString Entry m () -> ConduitT ByteString Entry m ()
forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| (ByteString -> m Entry) -> ConduitT ByteString Entry m ()
forall (m :: * -> *) a b.
Monad m =>
(a -> m b) -> ConduitT a b m ()
Conduit.mapM ((String -> m Entry)
-> (Entry -> m Entry) -> Either String Entry -> m Entry
forall a c b. (a -> c) -> (b -> c) -> Either a b -> c
either (Exception -> m Entry
forall e a. (HasCallStack, Exception e) => e -> m a
forall (m :: * -> *) e a.
(MonadThrow m, HasCallStack, Exception e) =>
e -> m a
throwM (Exception -> m Entry)
-> (String -> Exception) -> String -> m Entry
forall b c a. (b -> c) -> (a -> b) -> a -> c
. String -> Exception
JSONError) Entry -> m Entry
forall a. a -> m a
forall (f :: * -> *) a. Applicative f => a -> f a
pure (Either String Entry -> m Entry)
-> (ByteString -> Either String Entry) -> ByteString -> m Entry
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ByteString -> Either String Entry
forall a. FromJSON a => ByteString -> Either String a
JSON.eitherDecodeStrict)