Skip to content

Commit 16f4b2b

Browse files
authored
Merge pull request #74 from input-output-hk/coot/annotated-codec
AnnotatedCodec
2 parents 3b65eae + ee05d80 commit 16f4b2b

File tree

13 files changed

+951
-365
lines changed

13 files changed

+951
-365
lines changed

.github/workflows/haskell.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ jobs:
100100
runs-on: ubuntu-22.04
101101

102102
env:
103-
STYLISH_HASKELL_VERSION: "0.14.4.0"
103+
STYLISH_HASKELL_VERSION: "0.14.6.0"
104104

105105
steps:
106106
- name: Set cache version
@@ -116,8 +116,8 @@ jobs:
116116
uses: haskell-actions/setup@v2
117117
id: setup-haskell
118118
with:
119-
ghc-version: 9.2.5
120-
cabal-version: 3.8.1.0
119+
ghc-version: 9.8
120+
cabal-version: 3.12.1.0
121121

122122
- name: "Setup cabal bin path"
123123
run: |

cabal.project

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
index-state: 2025-05-21T15:48:46Z
1+
index-state: 2025-07-08T15:23:02Z
22

33
packages: ./typed-protocols
44
./typed-protocols-doc
@@ -10,6 +10,10 @@ if impl(ghc >= 9.12)
1010
, serdoc-core:template-haskell
1111
, serdoc-core:th-abstraction
1212

13+
allow-newer:
14+
, serdoc-core:QuickCheck
15+
, serdoc-core:tasty-quickcheck
16+
1317
if os(windows)
1418
package text
1519
flags: -simdutf

typed-protocols-doc/typed-protocols-doc.cabal

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ library
5454
, th-abstraction >=0.6.0.0 && <0.8
5555
, time >=1.12 && <1.14
5656
, serdoc-core
57-
, typed-protocols ^>= 1.0
57+
, typed-protocols ^>= 1.0 || ^>= 1.1
5858
hs-source-dirs: src
5959
default-language: GHC2021
6060
default-extensions: DataKinds
@@ -89,7 +89,7 @@ test-suite typed-protocols-doc-test
8989
build-depends: base >=4.14.0.0 && <5
9090
, blaze-html >=0.9.1.2 && <0.10
9191
, tasty >=1.5 && <1.6
92-
, tasty-quickcheck >=0.10.3 && <0.11
92+
, tasty-quickcheck >=0.10.3 && <0.12
9393
, typed-protocols
9494
, typed-protocols-doc
9595
, serdoc-core

typed-protocols/CHANGELOG.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
11
# Revision history for typed-protocols
22

3+
## 1.1.0.0 -- 05.08.2025
4+
5+
### Breaking changes
6+
7+
* Annotated codecs which allow to retain original bytes received from the network.
8+
The `Codec` type evolved into a new `CodecF` data type, and two type aliases
9+
`AnnotatedCodec`, `Codec`.
10+
* `prop_codec` properties moved to `typed-protocols:codec-properties` library
11+
(`Network.TypedProtocol.Codec.Properties` module). They now return the
12+
`QuickCheck`'s `Property` rather than a `Bool`.
13+
14+
### Non-breaking changes
15+
16+
## 1.0.0.0
17+
18+
* Hackage release.
19+
320
## 0.3.0.0
421

522
* `AnyMessageWithAgency` pattern synonym is exported as a constructor of `AnyMessage`.

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

Lines changed: 16 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ import Network.TypedProtocol.Codec
2525
import Network.TypedProtocol.Core
2626

2727

28-
-- | Construct a 'Codec' for a CBOR based serialisation format, using strict
28+
-- | Construct a 'CodecF' for a CBOR based serialisation format, using strict
2929
-- 'BS.ByteString's.
3030
--
31-
-- This is an adaptor between the @cborg@ library and the 'Codec' abstraction.
31+
-- This is an adaptor between the @cborg@ library and the 'CodecF' abstraction.
3232
--
3333
-- It takes encode and decode functions for the protocol messages that use the
3434
-- CBOR library encoder and decoder.
@@ -38,7 +38,7 @@ import Network.TypedProtocol.Core
3838
-- natively produces chunks).
3939
--
4040
mkCodecCborStrictBS
41-
:: forall ps m. MonadST m
41+
:: forall ps m f. MonadST m
4242

4343
=> (forall (st :: ps) (st' :: ps).
4444
StateTokenI st
@@ -49,10 +49,10 @@ mkCodecCborStrictBS
4949
-> (forall (st :: ps) s.
5050
ActiveState st
5151
=> StateToken st
52-
-> CBOR.Decoder s (SomeMessage st))
52+
-> CBOR.Decoder s (f st))
5353
-- ^ cbor decoder
5454

55-
-> Codec ps CBOR.DeserialiseFailure m BS.ByteString
55+
-> CodecF ps CBOR.DeserialiseFailure m f BS.ByteString
5656
mkCodecCborStrictBS cborMsgEncode cborMsgDecode =
5757
Codec {
5858
encode = \msg -> convertCborEncoder cborMsgEncode msg,
@@ -65,11 +65,12 @@ mkCodecCborStrictBS cborMsgEncode cborMsgDecode =
6565
. cborEncode
6666

6767
convertCborDecoder
68-
:: (forall s. CBOR.Decoder s a)
69-
-> m (DecodeStep BS.ByteString CBOR.DeserialiseFailure m a)
68+
:: (forall s. CBOR.Decoder s (f a))
69+
-> m (DecodeStep BS.ByteString CBOR.DeserialiseFailure m (f a))
7070
convertCborDecoder cborDecode =
7171
convertCborDecoderBS cborDecode stToIO
7272

73+
7374
convertCborDecoderBS
7475
:: forall s m a. Functor m
7576
=> CBOR.Decoder s a
@@ -89,16 +90,16 @@ convertCborDecoderBS cborDecode liftST =
8990
go (CBOR.Partial k) = DecodePartial (fmap go . liftST . k)
9091

9192

92-
-- | Construct a 'Codec' for a CBOR based serialisation format, using lazy
93+
-- | Construct a 'CodecF' for a CBOR based serialisation format, using lazy
9394
-- 'BS.ByteString's.
9495
--
95-
-- This is an adaptor between the @cborg@ library and the 'Codec' abstraction.
96+
-- This is an adaptor between the @cborg@ library and the 'CodecF' abstraction.
9697
--
9798
-- It takes encode and decode functions for the protocol messages that use the
9899
-- CBOR library encoder and decoder.
99100
--
100101
mkCodecCborLazyBS
101-
:: forall ps m. MonadST m
102+
:: forall ps m f. MonadST m
102103

103104
=> (forall (st :: ps) (st' :: ps).
104105
StateTokenI st
@@ -109,10 +110,10 @@ mkCodecCborLazyBS
109110
-> (forall (st :: ps) s.
110111
ActiveState st
111112
=> StateToken st
112-
-> CBOR.Decoder s (SomeMessage st))
113+
-> CBOR.Decoder s (f st))
113114
-- ^ cbor decoder
114115

115-
-> Codec ps CBOR.DeserialiseFailure m LBS.ByteString
116+
-> CodecF ps CBOR.DeserialiseFailure m f LBS.ByteString
116117
mkCodecCborLazyBS cborMsgEncode cborMsgDecode =
117118
Codec {
118119
encode = \msg -> convertCborEncoder cborMsgEncode msg,
@@ -126,11 +127,12 @@ mkCodecCborLazyBS cborMsgEncode cborMsgDecode =
126127
. cborEncode
127128

128129
convertCborDecoder
129-
:: (forall s. CBOR.Decoder s a)
130-
-> m (DecodeStep LBS.ByteString CBOR.DeserialiseFailure m a)
130+
:: (forall s. CBOR.Decoder s (f a))
131+
-> m (DecodeStep LBS.ByteString CBOR.DeserialiseFailure m (f a))
131132
convertCborDecoder cborDecode =
132133
convertCborDecoderLBS cborDecode stToIO
133134

135+
134136
convertCborDecoderLBS
135137
:: forall s m a. Monad m
136138
=> CBOR.Decoder s a

typed-protocols/examples/Network/TypedProtocol/ReqResp/Codec.hs

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
{-# LANGUAGE BlockArguments #-}
2+
13
module Network.TypedProtocol.ReqResp.Codec where
24

35
import Network.TypedProtocol.Codec
@@ -43,6 +45,68 @@ codecReqResp =
4345
where failure = CodecFailure ("unexpected server message: " ++ str)
4446

4547

48+
data WithBytes a = WithBytes {
49+
bytes :: String,
50+
message :: a
51+
}
52+
deriving (Show, Eq)
53+
54+
mkWithBytes :: Show a => a -> WithBytes a
55+
mkWithBytes message = WithBytes { bytes = show message, message }
56+
57+
58+
anncodecReqResp ::
59+
forall req resp m
60+
. (Monad m, Show req, Show resp, Read req, Read resp)
61+
=> AnnotatedCodec (ReqResp (WithBytes req) (WithBytes resp)) CodecFailure m String
62+
anncodecReqResp =
63+
Codec{encode, decode}
64+
where
65+
encode :: forall req' resp'
66+
(st :: ReqResp (WithBytes req') (WithBytes resp'))
67+
(st' :: ReqResp (WithBytes req') (WithBytes resp'))
68+
. ( Show req'
69+
, Show resp'
70+
)
71+
=> Message (ReqResp (WithBytes req') (WithBytes resp')) st st'
72+
-> String
73+
-- NOTE: we're not using 'Show (Message ...)' instance. If `req ~ Int`,
74+
-- then negative numbers will be surrounded with braces (e.g. @"(-1)"@) and
75+
-- the `Read` type class doesn't have a way to see that brackets were consumed
76+
-- from the input string.
77+
encode (MsgReq WithBytes { message })
78+
= "MsgReq " ++ show message ++ "\n"
79+
encode (MsgResp WithBytes { message })
80+
= "MsgResp " ++ show message ++ "\n"
81+
encode MsgDone
82+
= "MsgDone" ++ "\n"
83+
84+
decode :: forall req' resp' m'
85+
(st :: ReqResp (WithBytes req') (WithBytes resp'))
86+
. (Monad m', Read req', Read resp', ActiveState st)
87+
=> StateToken st
88+
-> m' (DecodeStep String CodecFailure m' (Annotator String st))
89+
decode stok =
90+
decodeTerminatedFrame '\n' $ \str trailing ->
91+
case (stok, break (==' ') str) of
92+
(SingIdle, ("MsgReq", str'))
93+
| Just req <- readMaybe @req' str'
94+
-> DecodeDone (Annotator \str'' ->
95+
let used = init $ drop 7 str'' in
96+
SomeMessage (MsgReq (WithBytes used req))) trailing
97+
(SingIdle, ("MsgDone", ""))
98+
-> DecodeDone (Annotator \_str'' -> SomeMessage MsgDone) trailing
99+
(SingBusy, ("MsgResp", str'))
100+
| Just resp <- readMaybe @resp' str'
101+
-> DecodeDone (Annotator \str'' ->
102+
let used = init $ drop 8 str'' in
103+
SomeMessage (MsgResp (WithBytes used resp))) trailing
104+
105+
(_ , _ ) -> DecodeFail failure
106+
where failure = CodecFailure ("unexpected server message: " ++ str)
107+
108+
109+
46110
codecReqRespId ::
47111
forall req resp m
48112
. (Monad m, Show req, Show resp)

0 commit comments

Comments
 (0)