module Shrun.IO.Types
  ( Stderr (..),
    CommandResult (..),
    ReadHandleResult (..),
    readHandleResultToStderr,
    readHandle,
  )
where
import Data.Time.Relative (RelativeTime)
import Effects.FileSystem.HandleReader
  ( MonadHandleReader (hIsClosed),
    hGetNonBlocking,
    hIsReadable,
  )
import Shrun.Data.Text (UnlinedText)
import Shrun.Data.Text qualified as ShrunText
import Shrun.Prelude
newtype Stderr = MkStderr {Stderr -> List UnlinedText
unStderr :: List UnlinedText}
  deriving stock (Stderr -> Stderr -> Bool
(Stderr -> Stderr -> Bool)
-> (Stderr -> Stderr -> Bool) -> Eq Stderr
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Stderr -> Stderr -> Bool
== :: Stderr -> Stderr -> Bool
$c/= :: Stderr -> Stderr -> Bool
/= :: Stderr -> Stderr -> Bool
Eq, Int -> Stderr -> ShowS
[Stderr] -> ShowS
Stderr -> String
(Int -> Stderr -> ShowS)
-> (Stderr -> String) -> ([Stderr] -> ShowS) -> Show Stderr
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Stderr -> ShowS
showsPrec :: Int -> Stderr -> ShowS
$cshow :: Stderr -> String
show :: Stderr -> String
$cshowList :: [Stderr] -> ShowS
showList :: [Stderr] -> ShowS
Show)
data CommandResult
  = CommandSuccess RelativeTime
  | CommandFailure RelativeTime Stderr
  deriving stock (CommandResult -> CommandResult -> Bool
(CommandResult -> CommandResult -> Bool)
-> (CommandResult -> CommandResult -> Bool) -> Eq CommandResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: CommandResult -> CommandResult -> Bool
== :: CommandResult -> CommandResult -> Bool
$c/= :: CommandResult -> CommandResult -> Bool
/= :: CommandResult -> CommandResult -> Bool
Eq, Int -> CommandResult -> ShowS
[CommandResult] -> ShowS
CommandResult -> String
(Int -> CommandResult -> ShowS)
-> (CommandResult -> String)
-> ([CommandResult] -> ShowS)
-> Show CommandResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> CommandResult -> ShowS
showsPrec :: Int -> CommandResult -> ShowS
$cshow :: CommandResult -> String
show :: CommandResult -> String
$cshowList :: [CommandResult] -> ShowS
showList :: [CommandResult] -> ShowS
Show)
data ReadHandleResult
  = 
    ReadErr (List UnlinedText)
  | 
    ReadSuccess (List UnlinedText)
  | 
    ReadNoData
  deriving stock (ReadHandleResult -> ReadHandleResult -> Bool
(ReadHandleResult -> ReadHandleResult -> Bool)
-> (ReadHandleResult -> ReadHandleResult -> Bool)
-> Eq ReadHandleResult
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: ReadHandleResult -> ReadHandleResult -> Bool
== :: ReadHandleResult -> ReadHandleResult -> Bool
$c/= :: ReadHandleResult -> ReadHandleResult -> Bool
/= :: ReadHandleResult -> ReadHandleResult -> Bool
Eq, Int -> ReadHandleResult -> ShowS
[ReadHandleResult] -> ShowS
ReadHandleResult -> String
(Int -> ReadHandleResult -> ShowS)
-> (ReadHandleResult -> String)
-> ([ReadHandleResult] -> ShowS)
-> Show ReadHandleResult
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> ReadHandleResult -> ShowS
showsPrec :: Int -> ReadHandleResult -> ShowS
$cshow :: ReadHandleResult -> String
show :: ReadHandleResult -> String
$cshowList :: [ReadHandleResult] -> ShowS
showList :: [ReadHandleResult] -> ShowS
Show)
instance Semigroup ReadHandleResult where
  ReadSuccess List UnlinedText
l <> :: ReadHandleResult -> ReadHandleResult -> ReadHandleResult
<> ReadHandleResult
_ = List UnlinedText -> ReadHandleResult
ReadSuccess List UnlinedText
l
  ReadHandleResult
_ <> ReadSuccess List UnlinedText
r = List UnlinedText -> ReadHandleResult
ReadSuccess List UnlinedText
r
  ReadErr List UnlinedText
l <> ReadHandleResult
_ = List UnlinedText -> ReadHandleResult
ReadErr List UnlinedText
l
  ReadHandleResult
_ <> ReadErr List UnlinedText
r = List UnlinedText -> ReadHandleResult
ReadErr List UnlinedText
r
  ReadHandleResult
_ <> ReadHandleResult
_ = ReadHandleResult
ReadNoData
instance Monoid ReadHandleResult where
  mempty :: ReadHandleResult
mempty = ReadHandleResult
ReadNoData
readHandleResultToStderr :: ReadHandleResult -> Stderr
readHandleResultToStderr :: ReadHandleResult -> Stderr
readHandleResultToStderr ReadHandleResult
ReadNoData = List UnlinedText -> Stderr
MkStderr (List UnlinedText -> Stderr) -> List UnlinedText -> Stderr
forall a b. (a -> b) -> a -> b
$ Text -> List UnlinedText
ShrunText.fromText Text
"<No data>"
readHandleResultToStderr (ReadErr List UnlinedText
errs) = List UnlinedText -> Stderr
MkStderr List UnlinedText
errs
readHandleResultToStderr (ReadSuccess List UnlinedText
errs) = List UnlinedText -> Stderr
MkStderr List UnlinedText
errs
readHandle ::
  ( HasCallStack,
    MonadCatch m,
    MonadHandleReader m
  ) =>
  Int ->
  Handle ->
  m ReadHandleResult
readHandle :: forall (m :: Type -> Type).
(HasCallStack, MonadCatch m, MonadHandleReader m) =>
Int -> Handle -> m ReadHandleResult
readHandle Int
blockSize Handle
handle = do
  
  
  m ReadHandleResult -> m (Either SomeException ReadHandleResult)
forall (m :: Type -> Type) a.
(HasCallStack, MonadCatch m) =>
m a -> m (Either SomeException a)
tryAny m ReadHandleResult
readHandle' m (Either SomeException ReadHandleResult)
-> (Either SomeException ReadHandleResult -> ReadHandleResult)
-> m ReadHandleResult
forall (f :: Type -> Type) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
    Left SomeException
ex -> List UnlinedText -> ReadHandleResult
ReadErr (List UnlinedText -> ReadHandleResult)
-> List UnlinedText -> ReadHandleResult
forall a b. (a -> b) -> a -> b
$ Text -> List UnlinedText
ShrunText.fromText (Text -> List UnlinedText) -> Text -> List UnlinedText
forall a b. (a -> b) -> a -> b
$ Text
"HandleException: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> SomeException -> Text
forall e. Exception e => e -> Text
displayExceptiont SomeException
ex
    Right ReadHandleResult
x -> ReadHandleResult
x
  where
    readHandle' :: m ReadHandleResult
readHandle' =
      m (Maybe Text)
nothingIfReady m (Maybe Text)
-> (Maybe Text -> m ReadHandleResult) -> m ReadHandleResult
forall a b. m a -> (a -> m b) -> m b
forall (m :: Type -> Type) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
        Just Text
err -> ReadHandleResult -> m ReadHandleResult
forall a. a -> m a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (ReadHandleResult -> m ReadHandleResult)
-> ReadHandleResult -> m ReadHandleResult
forall a b. (a -> b) -> a -> b
$ List UnlinedText -> ReadHandleResult
ReadErr (List UnlinedText -> ReadHandleResult)
-> List UnlinedText -> ReadHandleResult
forall a b. (a -> b) -> a -> b
$ Text -> List UnlinedText
ShrunText.fromText Text
err
        Maybe Text
Nothing ->
          
          
          
          
          
          
          
          
          
          
          
          Handle -> Int -> m ByteString
forall (m :: Type -> Type).
(MonadHandleReader m, HasCallStack) =>
Handle -> Int -> m ByteString
hGetNonBlocking Handle
handle Int
blockSize m ByteString
-> (ByteString -> ReadHandleResult) -> m ReadHandleResult
forall (f :: Type -> Type) a b. Functor f => f a -> (a -> b) -> f b
<&> \case
            ByteString
"" -> ReadHandleResult
ReadNoData
            ByteString
bs -> List UnlinedText -> ReadHandleResult
ReadSuccess (Text -> List UnlinedText
ShrunText.fromText (Text -> List UnlinedText) -> Text -> List UnlinedText
forall a b. (a -> b) -> a -> b
$ ByteString -> Text
decodeUtf8Lenient ByteString
bs)
    nothingIfReady :: m (Maybe Text)
nothingIfReady = do
      
      
      
      
      
      
      
      
      
      Bool
isClosed <- Handle -> m Bool
forall (m :: Type -> Type).
(MonadHandleReader m, HasCallStack) =>
Handle -> m Bool
hIsClosed Handle
handle
      if Bool
isClosed
        then Maybe Text -> m (Maybe Text)
forall a. a -> m a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Maybe Text -> m (Maybe Text)) -> Maybe Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"Handle closed"
        else do
          
          
          
          Bool
isReadable <- Handle -> m Bool
forall (m :: Type -> Type).
(MonadHandleReader m, HasCallStack) =>
Handle -> m Bool
hIsReadable Handle
handle
          if Bool -> Bool
not Bool
isReadable
            then Maybe Text -> m (Maybe Text)
forall a. a -> m a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure (Maybe Text -> m (Maybe Text)) -> Maybe Text -> m (Maybe Text)
forall a b. (a -> b) -> a -> b
$ Text -> Maybe Text
forall a. a -> Maybe a
Just Text
"Handle is not readable"
            else Maybe Text -> m (Maybe Text)
forall a. a -> m a
forall (f :: Type -> Type) a. Applicative f => a -> f a
pure Maybe Text
forall a. Maybe a
Nothing