From 62119c645f88753f5a4ce515e16084800a833906 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 10 Nov 2023 19:18:21 +0100 Subject: [PATCH] Feat: Added WithElements to Set (#607) * Feat: Added WithElements to Set * Refactor: fixed comments * Fix: fixed nil pointer exception * Refactor: changed signature of NewEntityLogger (#608) * Refactor: changed signature of NewEntityLogger * Fix: fixed test * Fix: fixed WithValue (#609) --- ds/reactive/set.go | 5 +++++ ds/reactive/set_impl.go | 35 +++++++++++++++++++++++++++++++++++ ds/reactive/variable_impl.go | 2 +- log/logger.go | 4 +--- log/logger_impl.go | 16 +++++----------- log/logger_test.go | 10 +++++----- 6 files changed, 52 insertions(+), 20 deletions(-) diff --git a/ds/reactive/set.go b/ds/reactive/set.go index 5de713fb9..0dd098288 100644 --- a/ds/reactive/set.go +++ b/ds/reactive/set.go @@ -33,6 +33,11 @@ type ReadableSet[ElementType comparable] interface { // set minus the elements of the other sets. SubtractReactive(others ...ReadableSet[ElementType]) Set[ElementType] + // WithElements is a utility function that allows to set up dynamic behavior based on the elements of the Set which + // is torn down once the element is removed gi(or the returned teardown function is called). It accepts an optional + // condition that has to be satisfied for the setup function to be called. + WithElements(setup func(element ElementType) (teardown func()), condition ...func(ElementType) bool) (teardown func()) + // ReadableSet imports the read methods of the Set interface. ds.ReadableSet[ElementType] } diff --git a/ds/reactive/set_impl.go b/ds/reactive/set_impl.go index 5bac3f7a7..087e98dad 100644 --- a/ds/reactive/set_impl.go +++ b/ds/reactive/set_impl.go @@ -212,6 +212,41 @@ func (r *readableSet[ElementType]) SubtractReactive(others ...ReadableSet[Elemen return s } +// WithElements is a utility function that allows to set up dynamic behavior based on the elements of the Set which is +// torn down once the element is removed (or the returned teardown function is called). It accepts an optional +// condition that has to be satisfied for the setup function to be called. +func (r *readableSet[ElementType]) WithElements(setup func(element ElementType) (teardown func()), condition ...func(ElementType) bool) (teardown func()) { + teardownFunctions := make(map[ElementType]func()) + + return lo.Batch( + r.OnUpdate(func(appliedMutations ds.SetMutations[ElementType]) { + appliedMutations.AddedElements().Range(func(element ElementType) { + if len(condition) == 0 || condition[0](element) { + if teardownFunc := setup(element); teardownFunc != nil { + teardownFunctions[element] = teardownFunc + } + } + }) + + appliedMutations.DeletedElements().Range(func(element ElementType) { + if teardownFunc, exists := teardownFunctions[element]; exists { + delete(teardownFunctions, element) + + teardownFunc() + } + }) + }), + + func() { + for element, teardownFunc := range teardownFunctions { + delete(teardownFunctions, element) + + teardownFunc() + } + }, + ) +} + // endregion /////////////////////////////////////////////////////////////////////////////////////////////////////////// // region derivedSet /////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/ds/reactive/variable_impl.go b/ds/reactive/variable_impl.go index da1410cc2..22e10ffd4 100644 --- a/ds/reactive/variable_impl.go +++ b/ds/reactive/variable_impl.go @@ -144,7 +144,7 @@ func (r *readableVariable[Type]) WithValue(setup func(value Type) (teardown func if len(condition) == 0 || condition[0](value) { unsubscribeOnUpdate(func() func() { return setup(value) }) } - }) + }, true) } // WithNonEmptyValue is a utility function that allows to set up dynamic behavior based on the latest (non-empty) diff --git a/log/logger.go b/log/logger.go index 879b0e387..a6caf2faa 100644 --- a/log/logger.go +++ b/log/logger.go @@ -3,8 +3,6 @@ package log import ( "log/slog" "os" - - "github.com/iotaledger/hive.go/ds/reactive" ) // Logger is a reactive logger that can be used to log messages with different log levels. @@ -86,7 +84,7 @@ type Logger interface { // NewEntityLogger creates a new logger for an entity with the given name. The logger is automatically shut down // when the given shutdown event is triggered. The initLogging function is called with the new logger instance and // can be used to configure the logger. - NewEntityLogger(entityName string, shutdownEvent reactive.Event, initLogging func(entityLogger Logger)) Logger + NewEntityLogger(entityName string) (entityLogger Logger, shutdown func()) } // NewLogger creates a new logger with the given name and an optional handler. If no handler is provided, the logger diff --git a/log/logger_impl.go b/log/logger_impl.go index 99507e679..b275e5e99 100644 --- a/log/logger_impl.go +++ b/log/logger_impl.go @@ -224,20 +224,14 @@ func (l *logger) NewChildLogger(name string) (childLogger Logger, shutdown func( return nestedLoggerInstance, nestedLoggerInstance.reactiveLevel.InheritFrom(l.reactiveLevel) } -// NewEntityLogger creates a new logger for an entity with the given name. The logger is automatically shut down when -// the given shutdown event is triggered. The initLogging function is called with the new logger instance and can be -// used to configure the logger. -func (l *logger) NewEntityLogger(entityName string, shutdownEvent reactive.Event, initLogging func(entityLogger Logger)) Logger { +// NewEntityLogger is identical to NewChildLogger with the difference that the name of the logger is automatically +// extended with a unique identifier to avoid name collisions. +func (l *logger) NewEntityLogger(entityName string) (entityLogger Logger, shutdown func()) { if l == nil { - return l + return l, func() {} } - embeddedLogger, shutdown := l.NewChildLogger(l.uniqueEntityName(entityName)) - shutdownEvent.OnTrigger(shutdown) - - initLogging(embeddedLogger) - - return embeddedLogger + return l.NewChildLogger(l.uniqueEntityName(entityName)) } // uniqueEntityName returns the name of an embedded instance of the given type. diff --git a/log/logger_test.go b/log/logger_test.go index c7b50226b..9f75b01de 100644 --- a/log/logger_test.go +++ b/log/logger_test.go @@ -80,11 +80,11 @@ func NewTestObject(logger log.Logger) *TestObject { IsEvicted: reactive.NewEvent(), } - t.Logger = logger.NewEntityLogger("TestObject", t.IsEvicted, func(entityLogger log.Logger) { - t.ImportantValue1.LogUpdates(entityLogger, log.LevelInfo, "ImportantValue1") - t.ImportantValue2.LogUpdates(entityLogger, log.LevelInfo, "ImportantValue2") - t.LessImportantValue1.LogUpdates(entityLogger, log.LevelDebug, "LessImportantValue1") - }) + t.Logger, _ = logger.NewEntityLogger("TestObject") + + t.ImportantValue1.LogUpdates(t.Logger, log.LevelInfo, "ImportantValue1") + t.ImportantValue2.LogUpdates(t.Logger, log.LevelInfo, "ImportantValue2") + t.LessImportantValue1.LogUpdates(t.Logger, log.LevelDebug, "LessImportantValue1") return t }