Skip to content

Commit

Permalink
Replace message.Set with enginekit's Set type.
Browse files Browse the repository at this point in the history
  • Loading branch information
jmalloc committed Oct 1, 2024
1 parent 3af80ab commit 6df89b7
Show file tree
Hide file tree
Showing 6 changed files with 32 additions and 197 deletions.
20 changes: 13 additions & 7 deletions entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"reflect"

"github.com/dogmatiq/configkit/message"
"github.com/dogmatiq/enginekit/collection/sets"
)

// Entity is an interface that represents the configuration of a Dogma "entity"
Expand Down Expand Up @@ -50,10 +51,10 @@ type EntityMessageNames struct {
Kinds map[message.Name]message.Kind

// Produced contains the names of the messages produced by the entity.
Produced message.Set[message.Name]
Produced sets.Set[message.Name]

// Consumed contains the names of the messages consumed by the entity.
Consumed message.Set[message.Name]
Consumed sets.Set[message.Name]
}

// Has returns true if entity uses a message type with the given name.
Expand All @@ -78,7 +79,7 @@ func (names EntityMessageNames) IsEqual(n EntityMessageNames) bool {
}

func (names *EntityMessageNames) union(n EntityMessageNames) {
merge := func(dst, src *message.Set[message.Name]) {
merge := func(dst, src *sets.Set[message.Name]) {
for name := range src.All() {
k, ok := n.Kinds[name]
if !ok {
Expand Down Expand Up @@ -113,10 +114,10 @@ func (names *EntityMessageNames) union(n EntityMessageNames) {
// EntityMessageTypes describes the message types used by a Dogma entity.
type EntityMessageTypes struct {
// Produced is a set of message types produced by the entity.
Produced message.Set[message.Type]
Produced sets.Set[message.Type]

// Consumed is a set of message types consumed by the entity.
Consumed message.Set[message.Type]
Consumed sets.Set[message.Type]
}

// Has returns true if the entity uses messages of the given type.
Expand All @@ -131,8 +132,13 @@ func (types EntityMessageTypes) IsEqual(t EntityMessageTypes) bool {
}

func (types *EntityMessageTypes) union(t EntityMessageTypes) {
types.Produced.Union(t.Produced)
types.Consumed.Union(t.Consumed)
for mt := range t.Produced.All() {
types.Produced.Add(mt)
}

for mt := range t.Consumed.All() {
types.Consumed.Add(mt)
}
}

func (types EntityMessageTypes) asNames() EntityMessageNames {
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ toolchain go1.23.1
require (
github.com/dogmatiq/aureus v0.1.0
github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1
github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9
github.com/dogmatiq/enginekit v0.14.1-0.20241001043115-6518710927b0
github.com/dogmatiq/iago v0.4.0
github.com/emicklei/dot v1.6.2
github.com/google/uuid v1.6.0
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ github.com/dogmatiq/dapper v0.6.0 h1:hnWUsjnt3nUiC9hmkPvuxrnMd7fYNz1i+/GS3gOx0Xs
github.com/dogmatiq/dapper v0.6.0/go.mod h1:ubRHWzt73s0MsPpGhWvnfW/Z/1YPnrkCsQv6CUOZVEw=
github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1 h1:SKhtRnDs7CC3ZNMux7lYCxCFz5ZhmeXjvo7/uMNISLo=
github.com/dogmatiq/dogma v0.14.4-0.20240926234834-3c0cc27a2ca1/go.mod h1:9lyVA+6V2+E/exV0IrBOrkUiyFwIATEhv+b0vnB2umQ=
github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9 h1:CuQ1OBcfZVfnfAZ4zcqCadsOLVtnfCN4vRsprt/Nncw=
github.com/dogmatiq/enginekit v0.13.1-0.20240929214803-d23339eac9a9/go.mod h1:XsY6KGPIC6zHx1duYDXbHTomqH9y+QOJX/tEiNZE2Qg=
github.com/dogmatiq/enginekit v0.14.1-0.20241001043115-6518710927b0 h1:AE6bjsP+/ANViP2xIPibZAVvUCaI/EyhVYQp41fEYh4=
github.com/dogmatiq/enginekit v0.14.1-0.20241001043115-6518710927b0/go.mod h1:XsY6KGPIC6zHx1duYDXbHTomqH9y+QOJX/tEiNZE2Qg=
github.com/dogmatiq/iago v0.4.0 h1:57nZqVT34IZxtCZEW/RFif7DNUEjMXgevfr/Mmd0N8I=
github.com/dogmatiq/iago v0.4.0/go.mod h1:fishMWBtzYcjgis6d873VTv9kFm/wHYLOzOyO9ECBDc=
github.com/dogmatiq/jumble v0.1.0 h1:Cb3ExfxY+AoUP4G9/sOwoOdYX8o+kOLK8+dhXAry+QA=
Expand Down
106 changes: 7 additions & 99 deletions message/set.go
Original file line number Diff line number Diff line change
@@ -1,27 +1,13 @@
package message

import (
"iter"
"maps"

"github.com/dogmatiq/dogma"
"github.com/dogmatiq/enginekit/collection/sets"
)

// Set is an unorded Set of unique T values.
type Set[T comparable] struct {
m map[T]struct{}
}

// NewSet returns a new set containing the given elements.
func NewSet[T comparable](elements ...T) Set[T] {
var s Set[T]
s.Add(elements...)
return s
}

// NamesOf returns a name set containing the names of the given messages.
func NamesOf(messages ...dogma.Message) Set[Name] {
var names Set[Name]
func NamesOf(messages ...dogma.Message) sets.Set[Name] {
var names sets.Set[Name]

for _, m := range messages {
names.Add(NameOf(m))
Expand All @@ -31,90 +17,12 @@ func NamesOf(messages ...dogma.Message) Set[Name] {
}

// TypesOf returns a type set containing the types of the given messages.
func TypesOf(messages ...dogma.Message) Set[Type] {
s := Set[Type]{}
func TypesOf(messages ...dogma.Message) sets.Set[Type] {
var types sets.Set[Type]

for _, m := range messages {
s.Add(TypeOf(m))
}

return s
}

// Add adds the given elements to the set.
func (s *Set[T]) Add(elements ...T) {
if s.m == nil {
s.m = make(map[T]struct{})
types.Add(TypeOf(m))
}

for _, e := range elements {
s.m[e] = struct{}{}
}
}

// Remove removes the given elements from the set.
func (s *Set[T]) Remove(elements ...T) {
for _, e := range elements {
delete(s.m, e)
}
}

// Clear removes all elements from the set.
func (s *Set[T]) Clear() {
s.m = nil
}

// Has returns true if the set contains all of the given elements.
func (s Set[T]) Has(elements ...T) bool {
for _, e := range elements {
if _, ok := s.m[e]; !ok {
return false
}
}
return true
}

// All returns an iterator that yields all elements in the set.
func (s Set[T]) All() iter.Seq[T] {
return maps.Keys(s.m)
}

// Union adds all elements from other to s.
func (s *Set[T]) Union(other Set[T]) {
for v := range other.m {
s.Add(v)
}
}

// IsEqual returns true if s and other contain the same elements.
func (s Set[T]) IsEqual(other Set[T]) bool {
if len(s.m) != len(other.m) {
return false
}

for k := range s.m {
if _, ok := other.m[k]; !ok {
return false
}
}

return true
}

// Len returns the number of elements in the set.
func (s Set[T]) Len() int {
return len(s.m)
}

// Clone returns a clone of the set.
func (s Set[T]) Clone() Set[T] {
return Set[T]{m: maps.Clone(s.m)}
}

// Union returns the union of a and b.
func Union[T comparable](a, b Set[T]) Set[T] {
var u Set[T]
u.Union(a)
u.Union(b)
return u
return types
}
86 changes: 3 additions & 83 deletions message/set_test.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package message_test

import (
"slices"
"testing"

. "github.com/dogmatiq/configkit/message"
"github.com/dogmatiq/enginekit/collection/sets"
. "github.com/dogmatiq/enginekit/enginetest/stubs"
)

func TestNamesOf(t *testing.T) {
got := NamesOf(CommandA1, EventA1)
want := NewSet(NameOf(CommandA1), NameOf(EventA1))
want := sets.New(NameOf(CommandA1), NameOf(EventA1))

if !got.IsEqual(want) {
t.Fatalf("unexpected result: got %v, want %v", got, want)
Expand All @@ -19,89 +19,9 @@ func TestNamesOf(t *testing.T) {

func TestTypesOf(t *testing.T) {
got := TypesOf(CommandA1, EventA1)
want := NewSet(TypeOf(CommandA1), TypeOf(EventA1))
want := sets.New(TypeOf(CommandA1), TypeOf(EventA1))

if !got.IsEqual(want) {
t.Fatalf("unexpected result: got %v, want %v", got, want)
}
}

func TestSet(t *testing.T) {
var set Set[int]

if set.Has(100) {
t.Fatal("did not expect set to have element")
}

set.Add(100)

if !set.Has(100) {
t.Fatal("expected set to have element")
}

if set.IsEqual(NewSet(200)) {
t.Fatal("did not expect set to equal a disjoint set with the same number of elements")
}

set.Remove(100)

if set.Has(100) {
t.Fatal("did not expect set to have element")
}

set.Add(100, 200, 300)

want := 100
for _, e := range slices.Sorted(set.All()) {
if e != want {
t.Fatalf("unexpected element from iterator: got %d, want %d", e, want)
}
want += 100
}

snapshot := set.Clone()
set.Union(NewSet(200, 300, 400))

if set.IsEqual(snapshot) {
t.Fatalf("union was not performed in-place")
}

wantSet := NewSet(100, 200, 300, 400)
if !set.IsEqual(wantSet) {
t.Fatalf("union did not produce the correct elements: got %v, want %v", set, wantSet)
}

if !set.Has(400) {
t.Fatal("expected set to have element new element from unioned set")
}

if set.Len() != wantSet.Len() {
t.Fatalf("unexpected number of elements after union: got %d, want %d", set.Len(), wantSet.Len())
}

set.Clear()

if set.Len() != 0 {
t.Fatalf("expected set to be empty after clearing")
}
}

func TestUnion(t *testing.T) {
a := NewSet(100, 200, 300)
b := NewSet(200, 300, 400)

union := Union(a, b)

want := NewSet(100, 200, 300, 400)
if !union.IsEqual(want) {
t.Fatalf("unexpected union result: got %v, want %v", a, want)
}

if !a.IsEqual(NewSet(100, 200, 300)) {
t.Fatalf("union modified set a")
}

if !b.IsEqual(NewSet(200, 300, 400)) {
t.Fatalf("union modified set b")
}
}
11 changes: 6 additions & 5 deletions string.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"strings"

"github.com/dogmatiq/configkit/message"
"github.com/dogmatiq/enginekit/collection/sets"
"github.com/dogmatiq/iago/indent"
"github.com/dogmatiq/iago/must"
)
Expand Down Expand Up @@ -82,7 +83,7 @@ func (s *stringer) visitHandler(cfg Handler) error {

names := cfg.MessageNames()

for _, p := range sortNameRoles(names.Kinds, names.Consumed) {
for _, p := range sortNameKinds(names.Kinds, names.Consumed) {
if p.Kind == message.TimeoutKind {
break
}
Expand All @@ -95,7 +96,7 @@ func (s *stringer) visitHandler(cfg Handler) error {
)
}

for _, p := range sortNameRoles(names.Kinds, names.Produced) {
for _, p := range sortNameKinds(names.Kinds, names.Produced) {
must.Fprintf(
s.w,
" %s %s%s\n",
Expand Down Expand Up @@ -144,11 +145,11 @@ type pair struct {
Kind message.Kind
}

// sortNameRoles returns a list of name/kind pairs, sorted by name.
// sortNameKinds returns a list of name/kind pairs, sorted by name.
// Timeout messages are always sorted towards the end.
func sortNameRoles(
func sortNameKinds(
kinds map[message.Name]message.Kind,
names message.Set[message.Name],
names sets.Set[message.Name],
) []pair {
sorted := make([]pair, 0, names.Len())

Expand Down

0 comments on commit 6df89b7

Please sign in to comment.