Skip to content

Commit

Permalink
feat(rx): define envelope (#253)
Browse files Browse the repository at this point in the history
  • Loading branch information
plastikfan committed May 4, 2024
1 parent 15b9d8d commit ff0f1ea
Show file tree
Hide file tree
Showing 4 changed files with 97 additions and 0 deletions.
44 changes: 44 additions & 0 deletions rx/envelope.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package rx

// Envelope wraps a struct type T so that it can be defined with
// pointer receivers. It also means that the client does not need
// to manually defined methods for constraint ProxyField as they
// are implemented by the Envelope.
type Envelope[T any, O Numeric] struct {
T *T
P O
}

// Seal wraps a struct T inside an Envelope
func Seal[T any, O Numeric](t *T) *Envelope[T, O] {
// When using rx, we must instantiate it with a struct, not a pointer
// to struct. So we create a distinction between what is the unit of
// transfer (Envelope) and it's payload of type T. This means that the
// Envelope can only have non pointer receivers, but T can
// be defined with pointer receivers.
//
return &Envelope[T, O]{
T: t,
}
}

// Field nominates which member of T of type O is the proxy field required
// to satisfy constraint ProxyField.
func (e Envelope[T, O]) Field() O {
return e.P
}

// Inc increments the P member of the Envelope required to satisfy constraint ProxyField.
func (e Envelope[T, O]) Inc(index *Envelope[T, O], by Envelope[T, O]) *Envelope[T, O] {
index.P += by.P

return index
}

// Index creates a new Envelope from the numeric value of i, required to
// satisfy constraint ProxyField.
func (e Envelope[T, O]) Index(i int) *Envelope[T, O] {
return &Envelope[T, O]{
P: O(i),
}
}
25 changes: 25 additions & 0 deletions rx/factory_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1062,6 +1062,31 @@ var _ = Describe("Factory", func() {
})

// reverse

When("using Envelope", func() {
When("positive count", func() {
It("馃И should: create observable", func() {
// Test_Range
defer leaktest.Check(GinkgoT())()

obs := rx.RangePF(&rx.ProxyRangeIterator[rx.Envelope[nugget, int], int]{
StartAt: rx.Envelope[nugget, int]{P: 5},
By: rx.Envelope[nugget, int]{P: 1},
Whilst: rx.LessThanPF(rx.Envelope[nugget, int]{P: 8}),
})

rx.Assert(context.Background(), obs,
rx.HasItems[rx.Envelope[nugget, int]]{
Expected: []rx.Envelope[nugget, int]{
{P: 5},
{P: 6},
{P: 7},
},
},
)
})
})
})
})
})

Expand Down
5 changes: 5 additions & 0 deletions rx/iterable-range.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,13 +152,18 @@ func (i *NumericRangeIterator[T]) While(current T) bool {
return i.Whilst(current)
}

// ProxyRangeIterator iterator required for struct types of T, where the
// client has nominated a member of T to be the proxy field with
// which numeric operations are performed to generate indexes for iteration.
type ProxyRangeIterator[T ProxyField[T, O], O Numeric] struct {
StartAt T
By T
Whilst WhilstFunc[T]
zero T
}

// Init is invoked prior to iteration and returns an error if not
// defined correctly.
func (i *ProxyRangeIterator[T, O]) Init() error {
if i.Whilst == nil {
return RangeMissingWhilstError
Expand Down
23 changes: 23 additions & 0 deletions rx/util_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -166,3 +166,26 @@ func widgetLessThan(until widget) func(widget) bool {
return current.id < until.id
}
}

// nugget use to test struct with pointer receivers
type nugget struct {
id int
name string
amount int
}

func (n *nugget) Field() int {
return n.id
}

func (n *nugget) Inc(index *nugget, by nugget) *nugget {
index.id += by.id

return index
}

func (n *nugget) Index(i int) *nugget {
return &nugget{
id: i,
}
}

0 comments on commit ff0f1ea

Please sign in to comment.