module Mpv.Socket where

import qualified Network.Socket as Socket
import Network.Socket (SockAddr (SockAddrUnix), Socket)
import Path (Abs, File, Path, toFilePath)
import Polysemy.Conc (retryingWithError)
import Polysemy.Time (MilliSeconds (MilliSeconds), Seconds (Seconds))

import qualified Mpv.Data.MpvError as MpvError
import Mpv.Data.MpvError (MpvError)

unixSocket ::
  Members [Error MpvError, Embed IO] r =>
  Sem r Socket
unixSocket :: forall (r :: [(* -> *) -> * -> *]).
Members '[Error MpvError, Embed IO] r =>
Sem r Socket
unixSocket =
  forall exc err (r :: [(* -> *) -> * -> *]) a.
(Exception exc, Member (Error err) r, Member (Embed IO) r) =>
(exc -> err) -> IO a -> Sem r a
fromExceptionVia @SomeException (Text -> MpvError
MpvError.Fatal (Text -> MpvError)
-> (SomeException -> Text) -> SomeException -> MpvError
forall b c a. (b -> c) -> (a -> b) -> a -> c
. SomeException -> Text
forall b a. (Show a, IsString b) => a -> b
show) (Family -> SocketType -> ProtocolNumber -> IO Socket
Socket.socket Family
Socket.AF_UNIX SocketType
Socket.Stream ProtocolNumber
0)

connectSocket ::
  Member (Embed IO) r =>
  Path Abs File ->
  Socket ->
  Sem r (Either Text ())
connectSocket :: forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
Path Abs File -> Socket -> Sem r (Either Text ())
connectSocket Path Abs File
path Socket
socket =
  IO () -> Sem r (Either Text ())
forall (r :: [(* -> *) -> * -> *]) a.
Member (Embed IO) r =>
IO a -> Sem r (Either Text a)
tryAny (Socket -> SockAddr -> IO ()
Socket.connect Socket
socket (String -> SockAddr
SockAddrUnix (Path Abs File -> String
forall b t. Path b t -> String
toFilePath Path Abs File
path)))

acquireSocket ::
  Members [Error MpvError, Race, Time t d, Embed IO] r =>
  Path Abs File ->
  Sem r Socket
acquireSocket :: forall t d (r :: [(* -> *) -> * -> *]).
Members '[Error MpvError, Race, Time t d, Embed IO] r =>
Path Abs File -> Sem r Socket
acquireSocket Path Abs File
path = do
  Socket
socket <- Sem r Socket
forall (r :: [(* -> *) -> * -> *]).
Members '[Error MpvError, Embed IO] r =>
Sem r Socket
unixSocket
  Seconds
-> MilliSeconds
-> Sem r (Either Text ())
-> Sem r (Maybe (Either Text ()))
forall e w u t d (r :: [(* -> *) -> * -> *]) a.
(TimeUnit w, TimeUnit u, Members '[Race, Time t d, Embed IO] r) =>
w -> u -> Sem r (Either e a) -> Sem r (Maybe (Either e a))
retryingWithError (Int64 -> Seconds
Seconds Int64
5) (Int64 -> MilliSeconds
MilliSeconds Int64
100) (Path Abs File -> Socket -> Sem r (Either Text ())
forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
Path Abs File -> Socket -> Sem r (Either Text ())
connectSocket Path Abs File
path Socket
socket) Sem r (Maybe (Either Text ()))
-> (Maybe (Either Text ()) -> Sem r Socket) -> Sem r Socket
forall (m :: * -> *) a b. Monad m => m a -> (a -> m b) -> m b
>>= \case
    Just (Right ()) -> Socket -> Sem r Socket
forall (f :: * -> *) a. Applicative f => a -> f a
pure Socket
socket
    Just (Left Text
err) -> MpvError -> Sem r Socket
forall e (r :: [(* -> *) -> * -> *]) a.
MemberWithError (Error e) r =>
e -> Sem r a
throw (Text -> MpvError
MpvError.Fatal Text
err)
    Maybe (Either Text ())
Nothing -> MpvError -> Sem r Socket
forall e (r :: [(* -> *) -> * -> *]) a.
MemberWithError (Error e) r =>
e -> Sem r a
throw (Text -> MpvError
MpvError.Fatal Text
"socket connect timed out")

releaseSocket ::
  Member (Embed IO) r =>
  Either MpvError Socket ->
  Sem r ()
releaseSocket :: forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
Either MpvError Socket -> Sem r ()
releaseSocket = \case
  Right Socket
sock ->
    Sem r (Either Text ()) -> Sem r ()
forall (f :: * -> *) a. Functor f => f a -> f ()
void (IO () -> Sem r (Either Text ())
forall (r :: [(* -> *) -> * -> *]) a.
Member (Embed IO) r =>
IO a -> Sem r (Either Text a)
tryAny (Socket -> IO ()
Socket.close Socket
sock))
  Left MpvError
_ ->
    Sem r ()
forall (f :: * -> *). Applicative f => f ()
unit

withSocket ::
  Members [Resource, Race, Time t d, Embed IO] r =>
  Path Abs File ->
  (Either MpvError Socket -> Sem r a) ->
  Sem r a
withSocket :: forall t d (r :: [(* -> *) -> * -> *]) a.
Members '[Resource, Race, Time t d, Embed IO] r =>
Path Abs File -> (Either MpvError Socket -> Sem r a) -> Sem r a
withSocket Path Abs File
path =
  Sem r (Either MpvError Socket)
-> (Either MpvError Socket -> Sem r ())
-> (Either MpvError Socket -> Sem r a)
-> Sem r a
forall (r :: [(* -> *) -> * -> *]) a c b.
MemberWithError Resource r =>
Sem r a -> (a -> Sem r c) -> (a -> Sem r b) -> Sem r b
bracket (Sem (Error MpvError : r) Socket -> Sem r (Either MpvError Socket)
forall e (r :: [(* -> *) -> * -> *]) a.
Sem (Error e : r) a -> Sem r (Either e a)
runError (Path Abs File -> Sem (Error MpvError : r) Socket
forall t d (r :: [(* -> *) -> * -> *]).
Members '[Error MpvError, Race, Time t d, Embed IO] r =>
Path Abs File -> Sem r Socket
acquireSocket Path Abs File
path)) Either MpvError Socket -> Sem r ()
forall (r :: [(* -> *) -> * -> *]).
Member (Embed IO) r =>
Either MpvError Socket -> Sem r ()
releaseSocket