From bb23ebe497ddc07c71b90af357b4e98c010fdde1 Mon Sep 17 00:00:00 2001 From: James Harris Date: Fri, 21 Jun 2024 09:23:15 +1000 Subject: [PATCH] Add `stateless.DefaultCodec`. --- CHANGELOG.md | 8 +++ codec/cbor/codec.go | 2 +- codec/internal/fixtures/protobuf.pb.go | 8 +-- codec/json/codec.go | 2 +- codec/protobuf/doc.go | 2 +- codec/stateless/codec.go | 58 +++++++++++++++++++++ codec/stateless/codec_test.go | 71 ++++++++++++++++++++++++++ codec/stateless/doc.go | 3 ++ codec/stateless/ginkgo_test.go | 15 ++++++ 9 files changed, 162 insertions(+), 7 deletions(-) create mode 100644 codec/stateless/codec.go create mode 100644 codec/stateless/codec_test.go create mode 100644 codec/stateless/doc.go create mode 100644 codec/stateless/ginkgo_test.go diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e7cae9..daa3e04 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,14 @@ The format is based on [Keep a Changelog], and this project adheres to [keep a changelog]: https://keepachangelog.com/en/1.0.0/ [semantic versioning]: https://semver.org/spec/v2.0.0.html +## Unreleased + +### Added + +- Added `stateless.DefaultCodec`, a codec for marshaling/unmarshaling + `dogma.StatelessProcessRoot` values without assuming that any other codec is + available. + ## [0.7.3] - 2023-03-27 ### Fixed diff --git a/codec/cbor/codec.go b/codec/cbor/codec.go index 27d22ae..a8b71b6 100644 --- a/codec/cbor/codec.go +++ b/codec/cbor/codec.go @@ -10,7 +10,7 @@ import ( // Codec is an implementation of marshalkit.Codec that uses CBOR encoding. type Codec struct{} -// DefaultCodec is a marshalkit.Codec that marshals messages using CBOR encoding. +// DefaultCodec is a marshalkit.Codec that marshals values using CBOR encoding. var DefaultCodec = Codec{} // Query returns the capabilities of the codec for the given types. diff --git a/codec/internal/fixtures/protobuf.pb.go b/codec/internal/fixtures/protobuf.pb.go index 3ea0ac7..63d0da7 100644 --- a/codec/internal/fixtures/protobuf.pb.go +++ b/codec/internal/fixtures/protobuf.pb.go @@ -1,7 +1,7 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // versions: -// protoc-gen-go v1.30.0 -// protoc v4.22.2 +// protoc-gen-go v1.34.2 +// protoc v5.27.1 // source: github.com/dogmatiq/marshalkit/codec/internal/fixtures/protobuf.proto package fixtures @@ -99,7 +99,7 @@ func file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_ } var file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_msgTypes = make([]protoimpl.MessageInfo, 1) -var file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_goTypes = []interface{}{ +var file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_goTypes = []any{ (*ProtoMessage)(nil), // 0: dogmatiq.marshalkit.fixtures.ProtoMessage } var file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_depIdxs = []int32{ @@ -116,7 +116,7 @@ func file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_ return } if !protoimpl.UnsafeEnabled { - file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + file_github_com_dogmatiq_marshalkit_codec_internal_fixtures_protobuf_proto_msgTypes[0].Exporter = func(v any, i int) any { switch v := v.(*ProtoMessage); i { case 0: return &v.state diff --git a/codec/json/codec.go b/codec/json/codec.go index dbaba02..d2d501f 100644 --- a/codec/json/codec.go +++ b/codec/json/codec.go @@ -11,7 +11,7 @@ import ( // implementation. type Codec struct{} -// DefaultCodec is a marshalkit.Codec that marshals messages using Go's standard +// DefaultCodec is a marshalkit.Codec that marshals values using Go's standard // JSON implementation. var DefaultCodec = Codec{} diff --git a/codec/protobuf/doc.go b/codec/protobuf/doc.go index 20d0bcb..300bb17 100644 --- a/codec/protobuf/doc.go +++ b/codec/protobuf/doc.go @@ -1,3 +1,3 @@ -// Package protobuf contains codec implemenentations for protocol buffers +// Package protobuf contains codec implementations for protocol buffers // types. package protobuf diff --git a/codec/stateless/codec.go b/codec/stateless/codec.go new file mode 100644 index 0000000..f2cff47 --- /dev/null +++ b/codec/stateless/codec.go @@ -0,0 +1,58 @@ +package stateless + +import ( + "fmt" + "reflect" + "slices" + + "github.com/dogmatiq/dogma" + "github.com/dogmatiq/marshalkit/codec" +) + +// Codec is an implementation of [marshalkit.Codec] that marshals +// [dogma.StatelessProcessRoot] values. +type Codec struct{} + +// DefaultCodec is a marshalkit.Codec that marshals [dogma.StatelessProcessRoot] +// values. +var DefaultCodec = Codec{} + +var processRootType = reflect.TypeOf(dogma.StatelessProcessRoot) + +// Query returns the capabilities of the codec for the given types. +func (Codec) Query(types []reflect.Type) codec.Capabilities { + caps := codec.Capabilities{} + + if slices.Contains(types, processRootType) { + caps.Types = map[reflect.Type]string{ + processRootType: "process", + } + } + + return caps +} + +// BasicMediaType returns the type and subtype portion of the media-type used to +// identify data encoded by this codec. +func (Codec) BasicMediaType() string { + return "application/x-empty" +} + +// Marshal returns the binary representation of v. +func (Codec) Marshal(v any) ([]byte, error) { + if v == dogma.StatelessProcessRoot { + return nil, nil + } + return nil, fmt.Errorf("%T is not dogma.StatelessProcessRoot", v) +} + +// Unmarshal decodes a binary representation into v. +func (Codec) Unmarshal(data []byte, v any) error { + if v != &dogma.StatelessProcessRoot { + return fmt.Errorf("%T is not a pointer to dogma.StatelessProcessRoot", v) + } + if len(data) != 0 { + return fmt.Errorf("expected empty data, got %d byte(s)", len(data)) + } + return nil +} diff --git a/codec/stateless/codec_test.go b/codec/stateless/codec_test.go new file mode 100644 index 0000000..971f531 --- /dev/null +++ b/codec/stateless/codec_test.go @@ -0,0 +1,71 @@ +package stateless_test + +import ( + "reflect" + + "github.com/dogmatiq/dogma" + // . "github.com/dogmatiq/dogma/fixtures" + // . "github.com/dogmatiq/marshalkit/codec/internal/fixtures" + . "github.com/dogmatiq/marshalkit/codec/stateless" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +var _ = Describe("type Codec", func() { + var codec *Codec + + BeforeEach(func() { + codec = &Codec{} + }) + + Describe("func Query()", func() { + It("uses a short string as the portable type", func() { + rt := reflect.TypeOf(dogma.StatelessProcessRoot) + + caps := codec.Query( + []reflect.Type{rt}, + ) + + Expect(caps.Types[rt]).To(Equal("process")) + }) + }) + + Describe("func BasicMediaType()", func() { + It("returns the expected basic media-type", func() { + Expect(codec.BasicMediaType()).To(Equal("application/x-empty")) + }) + }) + + Describe("func Marshal()", func() { + It("returns an empty byte-slice", func() { + data, err := codec.Marshal(dogma.StatelessProcessRoot) + Expect(err).ShouldNot(HaveOccurred()) + Expect(data).To(BeEmpty()) + }) + + It("returns an error if passed any value other than dogma.StatelessProcessRoot", func() { + _, err := codec.Marshal(123) + Expect(err).To(MatchError("int is not dogma.StatelessProcessRoot")) + }) + }) + + Describe("func Unmarshal()", func() { + It("does nothing on success", func() { + before := dogma.StatelessProcessRoot + + err := codec.Unmarshal(nil, &dogma.StatelessProcessRoot) + Expect(err).ShouldNot(HaveOccurred()) + Expect(dogma.StatelessProcessRoot).To(Equal(before)) + }) + It("returns an error if passed a non-empty byte-slice", func() { + data := []byte(` `) + err := codec.Unmarshal(data, &dogma.StatelessProcessRoot) + Expect(err).To(MatchError("expected empty data, got 1 byte(s)")) + }) + + It("returns an error if passed any value other than the address of dogma.StatelessProcessRoot", func() { + err := codec.Unmarshal(nil, 123) + Expect(err).To(MatchError("int is not a pointer to dogma.StatelessProcessRoot")) + }) + }) +}) diff --git a/codec/stateless/doc.go b/codec/stateless/doc.go new file mode 100644 index 0000000..aaee72f --- /dev/null +++ b/codec/stateless/doc.go @@ -0,0 +1,3 @@ +// Package stateless contains a codec implementation for +// [dogma.StatelessProcessRoot] values. +package stateless diff --git a/codec/stateless/ginkgo_test.go b/codec/stateless/ginkgo_test.go new file mode 100644 index 0000000..6126a54 --- /dev/null +++ b/codec/stateless/ginkgo_test.go @@ -0,0 +1,15 @@ +package stateless_test + +import ( + "reflect" + "testing" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" +) + +func TestSuite(t *testing.T) { + type tag struct{} + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, reflect.TypeOf(tag{}).PkgPath()) +}