Skip to content

Commit e216a0a

Browse files
authored
Merge pull request #52 from input-output-hk/coot/typed-protocols-new-api
New API for typed-protocols
2 parents f3277f6 + f451040 commit e216a0a

File tree

59 files changed

+4571
-1286
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+4571
-1286
lines changed

.github/workflows/haskell.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,8 @@ jobs:
9393
- name: typed-protocols-examples [test]
9494
run: cabal run typed-protocols-examples:test
9595

96-
- name: typed-protocols-doc [test]
97-
run: cabal test typed-protocols-doc
96+
# - name: typed-protocols-doc [test]
97+
# run: cabal test typed-protocols-doc
9898

9999
stylish-haskell:
100100
runs-on: ubuntu-22.04

cabal.project

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,13 @@ repository cardano-haskell-packages
1212

1313
index-state:
1414
hackage.haskell.org 2024-08-27T18:06:30Z
15-
, cardano-haskell-packages 2024-06-27T10:53:24Z
15+
, cardano-haskell-packages 2024-07-24T14:16:32Z
1616

1717
packages: ./typed-protocols
1818
./typed-protocols-cborg
19+
./typed-protocols-stateful
20+
./typed-protocols-stateful-cborg
1921
./typed-protocols-examples
20-
./typed-protocols-doc
22+
-- ./typed-protocols-doc
2123

2224
test-show-details: direct

scripts/check-stylish.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ export LC_ALL=C.UTF-8
55

66
[[ -x '/usr/bin/fd' ]] && FD="fd" || FD="fdfind"
77

8-
$FD . './typed-protocols' -e hs -E Setup.hs -X stylish-haskell -c .stylish-haskell.yaml -i
8+
$FD . './typed-protocols' -e hs -E Setup.hs -E Core.hs -X stylish-haskell -c .stylish-haskell.yaml -i
99
$FD . './typed-protocols-cborg' -e hs -E Setup.hs -X stylish-haskell -c .stylish-haskell.yaml -i
10-
$FD . './typed-protocols-examples' -e hs -E Setup.hs -X stylish-haskell -c .stylish-haskell.yaml -i
10+
$FD . './typed-protocols-examples' -e hs -E Setup.hs -E Channel.hs -X stylish-haskell -c .stylish-haskell.yaml -i

typed-protocols-cborg/src/Network/TypedProtocol/Codec/CBOR.hs

Lines changed: 40 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
{-# LANGUAGE DataKinds #-}
2+
{-# LANGUAGE FlexibleContexts #-}
3+
{-# LANGUAGE LambdaCase #-}
4+
{-# LANGUAGE MonoLocalBinds #-}
25
{-# LANGUAGE PolyKinds #-}
36
{-# LANGUAGE RankNTypes #-}
47
{-# LANGUAGE ScopedTypeVariables #-}
58

69
module Network.TypedProtocol.Codec.CBOR
710
( module Network.TypedProtocol.Codec
8-
, DeserialiseFailure
911
, mkCodecCborLazyBS
1012
, mkCodecCborStrictBS
13+
, convertCborDecoderBS
14+
, convertCborDecoderLBS
15+
-- * Re-exports
16+
, CBOR.DeserialiseFailure (..)
1117
) where
1218

1319
import Control.Monad.Class.MonadST (MonadST (..))
@@ -27,8 +33,6 @@ import Network.TypedProtocol.Codec
2733
import Network.TypedProtocol.Core
2834

2935

30-
type DeserialiseFailure = CBOR.DeserialiseFailure
31-
3236
-- | Construct a 'Codec' for a CBOR based serialisation format, using strict
3337
-- 'BS.ByteString's.
3438
--
@@ -44,19 +48,23 @@ type DeserialiseFailure = CBOR.DeserialiseFailure
4448
mkCodecCborStrictBS
4549
:: forall ps m. MonadST m
4650

47-
=> (forall (pr :: PeerRole) (st :: ps) (st' :: ps).
48-
PeerHasAgency pr st
49-
-> Message ps st st' -> CBOR.Encoding)
51+
=> (forall (st :: ps) (st' :: ps).
52+
StateTokenI st
53+
=> ActiveState st
54+
=> Message ps st st' -> CBOR.Encoding)
55+
-- ^ cbor encoder
5056

51-
-> (forall (pr :: PeerRole) (st :: ps) s.
52-
PeerHasAgency pr st
57+
-> (forall (st :: ps) s.
58+
ActiveState st
59+
=> StateToken st
5360
-> CBOR.Decoder s (SomeMessage st))
61+
-- ^ cbor decoder
5462

55-
-> Codec ps DeserialiseFailure m BS.ByteString
63+
-> Codec ps CBOR.DeserialiseFailure m BS.ByteString
5664
mkCodecCborStrictBS cborMsgEncode cborMsgDecode =
5765
Codec {
58-
encode = \stok msg -> convertCborEncoder (cborMsgEncode stok) msg,
59-
decode = \stok -> convertCborDecoder (cborMsgDecode stok)
66+
encode = \msg -> convertCborEncoder cborMsgEncode msg,
67+
decode = \stok -> convertCborDecoder (cborMsgDecode stok)
6068
}
6169
where
6270
convertCborEncoder :: (a -> CBOR.Encoding) -> a -> BS.ByteString
@@ -66,20 +74,22 @@ mkCodecCborStrictBS cborMsgEncode cborMsgDecode =
6674

6775
convertCborDecoder
6876
:: (forall s. CBOR.Decoder s a)
69-
-> m (DecodeStep BS.ByteString DeserialiseFailure m a)
77+
-> m (DecodeStep BS.ByteString CBOR.DeserialiseFailure m a)
7078
convertCborDecoder cborDecode =
7179
convertCborDecoderBS cborDecode stToIO
7280

7381
convertCborDecoderBS
7482
:: forall s m a. Functor m
75-
=> (CBOR.Decoder s a)
83+
=> CBOR.Decoder s a
84+
-- ^ cbor decoder
7685
-> (forall b. ST s b -> m b)
77-
-> m (DecodeStep BS.ByteString DeserialiseFailure m a)
86+
-- ^ lift ST computation (e.g. 'Control.Monad.ST.stToIO', 'stToPrim', etc)
87+
-> m (DecodeStep BS.ByteString CBOR.DeserialiseFailure m a)
7888
convertCborDecoderBS cborDecode liftST =
7989
go <$> liftST (CBOR.deserialiseIncremental cborDecode)
8090
where
8191
go :: CBOR.IDecode s a
82-
-> DecodeStep BS.ByteString DeserialiseFailure m a
92+
-> DecodeStep BS.ByteString CBOR.DeserialiseFailure m a
8393
go (CBOR.Done trailing _ x)
8494
| BS.null trailing = DecodeDone x Nothing
8595
| otherwise = DecodeDone x (Just trailing)
@@ -98,19 +108,23 @@ convertCborDecoderBS cborDecode liftST =
98108
mkCodecCborLazyBS
99109
:: forall ps m. MonadST m
100110

101-
=> (forall (pr :: PeerRole) (st :: ps) (st' :: ps).
102-
PeerHasAgency pr st
103-
-> Message ps st st' -> CBOR.Encoding)
111+
=> (forall (st :: ps) (st' :: ps).
112+
StateTokenI st
113+
=> ActiveState st
114+
=> Message ps st st' -> CBOR.Encoding)
115+
-- ^ cbor encoder
104116

105-
-> (forall (pr :: PeerRole) (st :: ps) s.
106-
PeerHasAgency pr st
117+
-> (forall (st :: ps) s.
118+
ActiveState st
119+
=> StateToken st
107120
-> CBOR.Decoder s (SomeMessage st))
121+
-- ^ cbor decoder
108122

109123
-> Codec ps CBOR.DeserialiseFailure m LBS.ByteString
110124
mkCodecCborLazyBS cborMsgEncode cborMsgDecode =
111125
Codec {
112-
encode = \stok msg -> convertCborEncoder (cborMsgEncode stok) msg,
113-
decode = \stok -> convertCborDecoder (cborMsgDecode stok)
126+
encode = \msg -> convertCborEncoder cborMsgEncode msg,
127+
decode = \stok -> convertCborDecoder (cborMsgDecode stok)
114128
}
115129
where
116130
convertCborEncoder :: (a -> CBOR.Encoding) -> a -> LBS.ByteString
@@ -127,8 +141,10 @@ mkCodecCborLazyBS cborMsgEncode cborMsgDecode =
127141

128142
convertCborDecoderLBS
129143
:: forall s m a. Monad m
130-
=> (CBOR.Decoder s a)
144+
=> CBOR.Decoder s a
145+
-- ^ cbor decoder
131146
-> (forall b. ST s b -> m b)
147+
-- ^ lift ST computation (e.g. 'Control.Monad.ST.stToIO', 'stToPrim', etc)
132148
-> m (DecodeStep LBS.ByteString CBOR.DeserialiseFailure m a)
133149
convertCborDecoderLBS cborDecode liftST =
134150
go [] =<< liftST (CBOR.deserialiseIncremental cborDecode)
@@ -148,7 +164,7 @@ convertCborDecoderLBS cborDecode liftST =
148164
-- We keep a bunch of chunks and supply the CBOR decoder with them
149165
-- until we run out, when we go get another bunch.
150166
go (c:cs) (CBOR.Partial k) = go cs =<< liftST (k (Just c))
151-
go [] (CBOR.Partial k) = return $ DecodePartial $ \mbs -> case mbs of
167+
go [] (CBOR.Partial k) = return $ DecodePartial $ \case
152168
Nothing -> go [] =<< liftST (k Nothing)
153169
Just bs -> go cs (CBOR.Partial k)
154170
where cs = LBS.toChunks bs

typed-protocols-cborg/typed-protocols-cborg.cabal

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
cabal-version: 3.0
22
name: typed-protocols-cborg
3-
version: 0.1.0.4
3+
version: 0.2.0.0
44
synopsis: CBOR codecs for typed-protocols
55
-- description:
66
license: Apache-2.0
@@ -21,6 +21,7 @@ library
2121
build-depends: base >=4.12 && <4.21,
2222
bytestring >=0.10 && <0.13,
2323
cborg >=0.2.1 && <0.3,
24+
singletons,
2425

2526
io-classes ^>=1.5,
2627
typed-protocols

typed-protocols-examples/src/Network/TypedProtocol/Channel.hs

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
{-# LANGUAGE CPP #-}
12
{-# LANGUAGE FlexibleContexts #-}
23
{-# LANGUAGE NamedFieldPuns #-}
34
{-# LANGUAGE RankNTypes #-}
45
{-# LANGUAGE ScopedTypeVariables #-}
6+
{-# LANGUAGE TypeApplications #-}
57

68
module Network.TypedProtocol.Channel
79
( Channel (..)
@@ -10,8 +12,12 @@ module Network.TypedProtocol.Channel
1012
, fixedInputChannel
1113
, mvarsAsChannel
1214
, handlesAsChannel
15+
#if !defined(mingw32_HOST_OS)
16+
, socketAsChannel
17+
#endif
1318
, createConnectedChannels
1419
, createConnectedBufferedChannels
20+
, createConnectedBufferedChannelsUnbounded
1521
, createPipelineTestChannels
1622
, channelEffect
1723
, delayChannel
@@ -25,8 +31,14 @@ import Control.Monad.Class.MonadTimer.SI
2531
import qualified Data.ByteString as BS
2632
import qualified Data.ByteString.Lazy as LBS
2733
import Data.ByteString.Lazy.Internal (smallChunkSize)
34+
import Data.Proxy
2835
import Numeric.Natural
2936

37+
#if !defined(mingw32_HOST_OS)
38+
import Network.Socket (Socket)
39+
import qualified Network.Socket.ByteString.Lazy as Socket
40+
#endif
41+
3042
import qualified System.IO as IO (Handle, hFlush, hIsEOF)
3143

3244

@@ -119,12 +131,20 @@ mvarsAsChannel bufferRead bufferWrite =
119131
--
120132
-- This is primarily useful for testing protocols.
121133
--
122-
createConnectedChannels :: MonadSTM m => m (Channel m a, Channel m a)
134+
createConnectedChannels :: forall m a. (MonadLabelledSTM m, MonadTraceSTM m, Show a) => m (Channel m a, Channel m a)
123135
createConnectedChannels = do
124136
-- Create two TMVars to act as the channel buffer (one for each direction)
125137
-- and use them to make both ends of a bidirectional channel
126-
bufferA <- atomically $ newEmptyTMVar
127-
bufferB <- atomically $ newEmptyTMVar
138+
bufferA <- atomically $ do
139+
v <- newEmptyTMVar
140+
labelTMVar v "buffer-a"
141+
traceTMVar (Proxy @m) v $ \_ a -> pure $ TraceString ("buffer-a: " ++ show a)
142+
return v
143+
bufferB <- atomically $ do
144+
v <- newEmptyTMVar
145+
traceTMVar (Proxy @m) v $ \_ a -> pure $ TraceString ("buffer-b: " ++ show a)
146+
labelTMVar v "buffer-b"
147+
return v
128148

129149
return (mvarsAsChannel bufferB bufferA,
130150
mvarsAsChannel bufferA bufferB)
@@ -156,11 +176,32 @@ createConnectedBufferedChannels sz = do
156176
recv = atomically (Just <$> readTBQueue bufferRead)
157177

158178

179+
-- | Create a pair of channels that are connected via two unbounded buffers.
180+
--
181+
-- This is primarily useful for testing protocols.
182+
--
183+
createConnectedBufferedChannelsUnbounded :: forall m a. MonadSTM m
184+
=> m (Channel m a, Channel m a)
185+
createConnectedBufferedChannelsUnbounded = do
186+
-- Create two TQueues to act as the channel buffers (one for each
187+
-- direction) and use them to make both ends of a bidirectional channel
188+
bufferA <- newTQueueIO
189+
bufferB <- newTQueueIO
190+
191+
return (queuesAsChannel bufferB bufferA,
192+
queuesAsChannel bufferA bufferB)
193+
where
194+
queuesAsChannel bufferRead bufferWrite =
195+
Channel{send, recv}
196+
where
197+
send x = atomically (writeTQueue bufferWrite x)
198+
recv = atomically ( Just <$> readTQueue bufferRead)
199+
159200
-- | Create a pair of channels that are connected via N-place buffers.
160201
--
161202
-- This variant /fails/ when 'send' would exceed the maximum buffer size.
162-
-- Use this variant when you want the 'PeerPipelined' to limit the pipelining
163-
-- itself, and you want to check that it does not exceed the expected level of
203+
-- Use this variant when you want the 'Peer' to limit the pipelining itself,
204+
-- and you want to check that it does not exceed the expected level of
164205
-- pipelining.
165206
--
166207
-- This is primarily useful for testing protocols.
@@ -194,7 +235,8 @@ createPipelineTestChannels sz = do
194235
--
195236
-- The Handles should be open in the appropriate read or write mode, and in
196237
-- binary mode. Writes are flushed after each write, so it is safe to use
197-
-- a buffering mode.
238+
-- a buffering mode. On unix named pipes can be used, see
239+
-- 'Network.TypedProtocol.ReqResp.Test.prop_namedPipePipelined_IO'
198240
--
199241
-- For bidirectional handles it is safe to pass the same handle for both.
200242
--
@@ -251,6 +293,23 @@ delayChannel delay = channelEffect (\_ -> return ())
251293
(\_ -> threadDelay delay)
252294

253295

296+
#if !defined(mingw32_HOST_OS)
297+
socketAsChannel :: Socket
298+
-> Channel IO LBS.ByteString
299+
socketAsChannel sock =
300+
Channel{send, recv}
301+
where
302+
send :: LBS.ByteString -> IO ()
303+
send = Socket.sendAll sock
304+
305+
recv :: IO (Maybe LBS.ByteString)
306+
recv = do
307+
bs <- Socket.recv sock (fromIntegral smallChunkSize)
308+
if LBS.null bs
309+
then return Nothing
310+
else return (Just bs)
311+
#endif
312+
254313
-- | Channel which logs sent and received messages.
255314
--
256315
loggingChannel :: ( MonadSay m

0 commit comments

Comments
 (0)