Idris2/libs/network/Control/Linear/Network.idr
2021-04-08 21:11:03 +01:00

155 lines
4.4 KiB
Idris

module Control.Linear.Network
-- An experimental linear type based API to sockets
import public Data.Either
import public Data.Maybe
import Control.Linear.LIO
import public Network.Socket.Data
import Network.Socket
public export
data SocketState = Ready | Bound | Listening | Open | Closed
||| Define the domain of SocketState transitions.
||| Label every such transition.
public export
data Action : SocketState -> Type where
Bind : Action Ready
Listen : Action Bound
Accept : Action Listening
Send : Action Open
Receive : Action Open
Close : Action st
export
data Socket : SocketState -> Type where
MkSocket : Socket.Data.Socket -> Socket st
||| For every label of a SocketState transition
||| and a success value of the transition,
||| define its result.
public export
Next : (action : Action st)
-> (success : Bool)
-> Type
Next Bind True = Socket Bound
Next Listen True = Socket Listening
Next Accept True = LPair (Socket Listening) (Socket Open)
Next Send True = Socket Open
Next Receive True = Socket Open
Next Close True = Socket Closed
Next _ False = Socket Closed
export
newSocket : LinearIO io
=> (fam : SocketFamily)
-> (ty : SocketType)
-> (pnum : ProtocolNumber)
-> (success : (1 _ : Socket Ready) -> L io ())
-> (fail : SocketError -> L io ())
-> L io ()
newSocket fam ty pnum success fail
= do Right rawsock <- socket fam ty pnum
| Left err => fail err
success (MkSocket rawsock)
export
close : LinearIO io => (1 _ : Socket st) -> L io {use=1} (Socket Closed)
close (MkSocket sock)
= do Socket.close sock
pure1 (MkSocket sock)
export
done : LinearIO io => (1 _ : Socket Closed) -> L io ()
done (MkSocket sock) = pure ()
export
bind : LinearIO io =>
(1 sock : Socket Ready) ->
(addr : Maybe SocketAddress) ->
(port : Port) ->
L io {use=1} (Res (Maybe SocketError)
(\res => Next Bind (isNothing res)))
bind (MkSocket sock) addr port
= do code <- Socket.bind sock addr port
case code of
0 =>
pure1 $ Nothing # MkSocket sock
err =>
pure1 $ Just err # MkSocket sock
export
connect : LinearIO io =>
(sock : Socket) ->
(addr : SocketAddress) ->
(port : Port) ->
L io {use=1} (Res (Maybe SocketError)
(\case Nothing => Socket Ready
Just _ => Socket Closed))
connect sock addr port
= do code <- Socket.connect sock addr port
case code of
0 =>
pure1 $ Nothing # MkSocket sock
err =>
pure1 $ Just err # MkSocket sock
export
listen : LinearIO io =>
(1 sock : Socket Bound) ->
L io {use=1} (Res (Maybe SocketError)
(\res => Next Listen (isNothing res)))
listen (MkSocket sock)
= do code <- Socket.listen sock
case code of
0 =>
pure1 $ Nothing # MkSocket sock
err =>
pure1 $ Just err # MkSocket sock
export
accept : LinearIO io =>
(1 _ : Socket Listening) ->
L io {use=1} (Res (Maybe SocketError)
(\res => Next Accept (isNothing res)))
accept (MkSocket sock)
= do Right (sock', sockaddr) <- Socket.accept sock
| Left err => pure1 (Just err # MkSocket sock)
pure1 (Nothing # (MkSocket sock # MkSocket sock'))
export
send : LinearIO io =>
(1 _ : Socket Open) ->
(msg : String) ->
L io {use=1} (Res (Maybe SocketError)
(\res => Next Send (isNothing res)))
send (MkSocket sock) msg
= do Right c <- Socket.send sock msg
| Left err => pure1 (Just err # MkSocket sock)
pure1 (Nothing # MkSocket sock)
export
recv : LinearIO io =>
(1 _ : Socket Open) ->
(len : ByteLength) ->
L io {use=1} (Res (Either SocketError (String, ResultCode))
(\res => Next Receive (isRight res)))
recv (MkSocket sock) len
= do Right msg <- Socket.recv sock len
| Left err => pure1 (Left err # MkSocket sock)
pure1 (Right msg # MkSocket sock)
export
recvAll : LinearIO io =>
(1 _ : Socket Open) ->
L io {use=1} (Res (Either SocketError String)
(\res => Next Receive (isRight res)))
recvAll (MkSocket sock)
= do Right msg <- Socket.recvAll sock
| Left err => pure1 (Left err # MkSocket sock)
pure1 (Right msg # MkSocket sock)