From f085eb183e356a67324c1efcaba0c099f938a41c Mon Sep 17 00:00:00 2001 From: plastikfan Date: Fri, 5 Jul 2024 22:17:06 +0100 Subject: [PATCH] feat(kernel): define guardian decoration order (#73) --- .vscode/settings.json | 3 +- Taskfile.yml | 4 + builders.go | 26 ++- collections/collections-suite_test.go | 13 ++ collections/positional-set.go | 110 +++++++++++++ collections/positional-set_test.go | 193 +++++++++++++++++++++++ director.go | 8 +- enums/role-en-auto.go | 6 +- enums/role-en.go | 2 +- factories.go | 6 +- internal/hiber/hibernate-plugin.go | 7 +- internal/kernel/base-plugin.go | 10 +- internal/kernel/guardian.go | 125 ++++++++------- internal/kernel/kernel-defs.go | 31 +++- internal/kernel/mediator.go | 18 ++- internal/kernel/navigation-controller.go | 20 +-- internal/kernel/navigator-agent.go | 4 +- internal/kernel/navigator-hades.go | 9 +- internal/refine/filter-plugin.go | 7 +- internal/resume/controller.go | 4 +- internal/sampling/sampling-plugin.go | 7 +- internal/types/definitions.go | 14 +- session.go | 35 ++-- synchronise.go | 6 +- 24 files changed, 520 insertions(+), 148 deletions(-) create mode 100644 collections/collections-suite_test.go create mode 100644 collections/positional-set.go create mode 100644 collections/positional-set_test.go diff --git a/.vscode/settings.json b/.vscode/settings.json index 209b999..d92611b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -92,6 +92,7 @@ "watchv", "watchvc", "watchvi", - "Wrapf" + "Wrapf", + "xcol" ] } diff --git a/Taskfile.yml b/Taskfile.yml index 33f9aaf..e5ccc1c 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -52,6 +52,10 @@ tasks: cmds: - go test ./i18n + toc: + cmds: + - go test ./collections + tp: cmds: - go test ./pref diff --git a/builders.go b/builders.go index 8ffb315..a6dc5ad 100644 --- a/builders.go +++ b/builders.go @@ -1,17 +1,20 @@ package tv import ( + "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/internal/kernel" + "github.com/snivilised/traverse/internal/lo" "github.com/snivilised/traverse/internal/types" "github.com/snivilised/traverse/measure" "github.com/snivilised/traverse/pref" ) type buildArtefacts struct { - o *pref.Options - kc types.KernelController - plugins []types.Plugin - ext extent + o *pref.Options + kc types.KernelController + plugins []types.Plugin + activeRoles []enums.Role + ext extent } type Builders struct { @@ -73,6 +76,12 @@ func (bs *Builders) buildAll() (*buildArtefacts, error) { // INIT PLUGINS // + roles := lo.Map(plugins, func(plugin types.Plugin, _ int) enums.Role { + return plugin.Role() + }) + + artefacts.Mediator.Arrange(roles) + for _, p := range plugins { if bindErr := p.Init(); bindErr != nil { return &buildArtefacts{ @@ -85,9 +94,10 @@ func (bs *Builders) buildAll() (*buildArtefacts, error) { } return &buildArtefacts{ - o: o, - kc: artefacts.Kontroller, - plugins: plugins, - ext: ext, + o: o, + kc: artefacts.Kontroller, + plugins: plugins, + activeRoles: roles, + ext: ext, }, nil } diff --git a/collections/collections-suite_test.go b/collections/collections-suite_test.go new file mode 100644 index 0000000..3afab45 --- /dev/null +++ b/collections/collections-suite_test.go @@ -0,0 +1,13 @@ +package collections_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok +) + +func TestCollections(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Collections Suite") +} diff --git a/collections/positional-set.go b/collections/positional-set.go new file mode 100644 index 0000000..4615fdc --- /dev/null +++ b/collections/positional-set.go @@ -0,0 +1,110 @@ +package collections + +import ( + "github.com/snivilised/traverse/internal/lo" + "golang.org/x/exp/constraints" +) + +type Orderable interface { + constraints.Integer | string +} + +type PositionalSet[T Orderable] struct { + order []T // Defines the valid elements and their order + items map[T]bool // Tracks which items are in the set + positions map[T]int // Maps each item to its position in the order + anchor T +} + +func NewPositionalSet[T Orderable](order []T, anchor T) *PositionalSet[T] { + o := lo.Reject(lo.Uniq(order), func( + item T, _ int, + ) bool { + return item == anchor + }) + o = append(o, anchor) + + ps := &PositionalSet[T]{ + order: o, + items: make(map[T]bool), + positions: make(map[T]int), + anchor: anchor, + } + + for i, item := range o { + ps.positions[item] = i + } + ps.items[anchor] = true + + return ps +} + +// Insert adds an item to the set if it's in the order and is not present +func (ps *PositionalSet[T]) Insert(item T) bool { + if item == ps.anchor { + return false + } + + if _, exists := ps.positions[item]; exists { + if _, found := ps.items[item]; found { + return false + } + ps.items[item] = true + return true + } + + return false +} + +// Add insert multiple items into the set under the same conditions as +// Insert +func (ps *PositionalSet[T]) All(items ...T) bool { + result := true + + for _, item := range items { + inserted := ps.Insert(item) + + if result { + result = inserted + } + } + + return result +} + +// Delete removes an item from the set +func (ps *PositionalSet[T]) Delete(item T) { + if item == ps.anchor { + return + } + + ps.items[item] = false + delete(ps.items, item) +} + +// Contains checks if an item is in the set +func (ps *PositionalSet[T]) Contains(item T) bool { + return ps.items[item] +} + +// Items returns all items in the set, in the defined order +func (ps *PositionalSet[T]) Items() []T { + result := make([]T, 0, len(ps.items)) + for _, item := range ps.order { + if ps.items[item] { + result = append(result, item) + } + } + return result +} + +// Position returns the position of an item in the order +func (ps *PositionalSet[T]) Position(item T) (int, bool) { + pos, exists := ps.positions[item] + return pos, exists +} + +// Count returns the elements in the set +func (ps *PositionalSet[T]) Count() int { + return len(ps.items) +} diff --git a/collections/positional-set_test.go b/collections/positional-set_test.go new file mode 100644 index 0000000..bef6af0 --- /dev/null +++ b/collections/positional-set_test.go @@ -0,0 +1,193 @@ +package collections_test + +import ( + "fmt" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok + + "github.com/snivilised/traverse/collections" + "github.com/snivilised/traverse/internal/helpers" +) + +var ( + rainbow = []string{"richard", "of", "york", "gave", "battle", "in", "vain"} + scrambled = []string{"york", "vain", "battle", "of", "richard", "gave", "in"} +) + +func assertColoursAreInOrder(set *collections.PositionalSet[string]) { + anchor, _ := set.Position("ANCHOR") + + for _, colour := range rainbow { + pos, _ := set.Position(colour) + Expect(pos < anchor).To(BeTrue(), helpers.Reason( + fmt.Sprintf("position(%v) of colour: %v should be less than anchor's(%v)", + pos, colour, anchor), + )) + } +} + +var _ = Describe("PositionalSet", func() { + type ( + orderingStringTE struct { + given string + should string + roles []string + } + ) + + var ( + set *collections.PositionalSet[string] + ) + + BeforeEach(func() { + set = collections.NewPositionalSet(rainbow, "ANCHOR") + }) + + Context("Count", func() { + When("no items added", func() { + It("🧪 should: contain just the anchor", func() { + Expect(set.Count()).To(Equal(1), helpers.Reason("only anchor should be present")) + }) + }) + }) + + Context("Insert", func() { + When("requested item is the anchor", func() { + It("🧪 should: not insert", func() { + Expect(set.Insert("ANCHOR")).To(BeFalse(), helpers.Reason("inserting anchor is invalid")) + Expect(set.Count()).To(Equal(1), helpers.Reason("only anchor should be present")) + }) + }) + + When("valid item requested", func() { + It("🧪 should: insert", func() { + Expect(set.Insert("richard")).To(BeTrue(), helpers.Reason("richard is in order list")) + Expect(set.Count()).To(Equal(2), helpers.Reason("richard, anchor")) + }) + }) + + When("valid item already present", func() { + It("🧪 should: not insert", func() { + set.Insert("richard") + Expect(set.Insert("richard")).To(BeFalse(), helpers.Reason("richard already in order list")) + Expect(set.Count()).To(Equal(2), helpers.Reason("richard, anchor")) + }) + }) + + When("invalid item requested", func() { + It("🧪 should: not insert", func() { + Expect(set.Insert("gold")).To(BeFalse(), helpers.Reason("gold not in order list")) + Expect(set.Count()).To(Equal(1), helpers.Reason("only anchor should be present")) + }) + }) + }) + + Context("All", func() { + When("All valid items requested", func() { + It("🧪 should: insert all", func() { + Expect(set.All( + "richard", "of", "york", "gave", "battle", "in", "vain", + )).To(BeTrue(), helpers.Reason("all items are valid")) + Expect(set.Count()).To(Equal(8), helpers.Reason("should contain all items")) + }) + }) + + When("Not all are valid", func() { + It("🧪 should: insert only valid", func() { + Expect(set.All( + "richard", "gold", "of", "silver", "york", "bronze", + )).To(BeFalse(), helpers.Reason("all items are valid")) + Expect(set.Count()).To(Equal(4), helpers.Reason("should contain valid items")) + }) + }) + }) + + Context("Delete", func() { + When("requested item is the anchor", func() { + It("🧪 should: not delete", func() { + set.Delete("ANCHOR") + Expect(set.Count()).To(Equal(1), helpers.Reason("anchor should still be present")) + }) + }) + + When("requested valid item is present", func() { + It("🧪 should: delete", func() { + set.Insert("york") + set.Delete("york") + Expect(set.Count()).To(Equal(1), helpers.Reason("york should deleted")) + }) + }) + + When("requested valid item is not present", func() { + It("🧪 should: not delete", func() { + set.Delete("york") + Expect(set.Count()).To(Equal(1), helpers.Reason("only anchor should be present")) + }) + }) + + When("requested valid item is not valid", func() { + It("🧪 should: not delete", func() { + set.Delete("silver") + Expect(set.Count()).To(Equal(1), helpers.Reason("only anchor should be present")) + }) + }) + }) + + Context("Position", func() { + When("multiple items inserted in order", func() { + It("🧪 should: return position less than anchor", func() { + set.All(rainbow...) + assertColoursAreInOrder(set) + }) + }) + + When("multiple items inserted out of order", func() { + It("🧪 should: return position less than anchor", func() { + set.All(scrambled...) + assertColoursAreInOrder(set) + }) + + It("🧪 should: contain correct positions when colours compared", func() { + set.All(scrambled...) + richard, _ := set.Position("richard") + of, _ := set.Position("of") + york, _ := set.Position("york") + Expect(richard < of).To(BeTrue()) + Expect(richard < york).To(BeTrue()) + }) + }) + }) + + Context("Items", func() { + When("multiple items inserted in order", func() { + It("🧪 should: return items defined by order", func() { + set.All(rainbow...) + expected := append(append([]string{}, rainbow...), "ANCHOR") + Expect(set.Items()).To(HaveExactElements(expected)) + }) + }) + + When("multiple items inserted out of order", func() { + It("🧪 should: return items defined by order", func() { + set.All(scrambled...) + expected := append(append([]string{}, rainbow...), "ANCHOR") + Expect(set.Items()).To(HaveExactElements(expected)) + }) + }) + + When("partial items inserted in order", func() { + It("🧪 should: return items defined by order", func() { + set.All("vain", "battle", "york") + expected := []string{"york", "battle", "vain", "ANCHOR"} + Expect(set.Items()).To(HaveExactElements(expected)) + + set.Delete("battle") + set.Insert("of") + + expected = []string{"of", "york", "vain", "ANCHOR"} + Expect(set.Items()).To(HaveExactElements(expected)) + }) + }) + }) +}) diff --git a/director.go b/director.go index ba3350f..9f620b2 100644 --- a/director.go +++ b/director.go @@ -92,6 +92,7 @@ func Prime(using *pref.Using, settings ...pref.Option) *Builders { navigator: kernel.Builder(func(o *pref.Options, resources *types.Resources, ) (*kernel.Artefacts, error) { + // !!! return kernel.New(using, o, &kernel.Benign{}, resources), nil }), plugins: features(activated), // swap over features & activated @@ -138,9 +139,10 @@ func Resume(was *Was, settings ...pref.Option) *Builders { navigator: kernel.Builder(func(o *pref.Options, resources *types.Resources, ) (*kernel.Artefacts, error) { - artefacts := kernel.New(&was.Using, o, resume.GetSealer(was), resources) - - return resume.NewController(was, artefacts), nil + // !!! + return resume.NewController(was, + kernel.New(&was.Using, o, resume.GetSealer(was), resources), + ), nil }), plugins: features(activated), } diff --git a/enums/role-en-auto.go b/enums/role-en-auto.go index fc31a49..81efd61 100644 --- a/enums/role-en-auto.go +++ b/enums/role-en-auto.go @@ -9,7 +9,7 @@ func _() { // Re-run the stringer command to generate them again. var x [1]struct{} _ = x[RoleUndefined-0] - _ = x[RoleTerminus-1] + _ = x[RoleAnchor-1] _ = x[RoleClientFilter-2] _ = x[RoleClientHiberWake-3] _ = x[RoleClientHiberSleep-4] @@ -17,9 +17,9 @@ func _() { _ = x[RoleSampler-6] } -const _Role_name = "undefined-roleterminus-roleclient-filter-roleclient-hiber-wake-roleclient-hiber-sleep-rolefastward-rolesampler-role" +const _Role_name = "undefined-roleanchor-roleclient-filter-roleclient-hiber-wake-roleclient-hiber-sleep-rolefastward-rolesampler-role" -var _Role_index = [...]uint8{0, 14, 27, 45, 67, 90, 103, 115} +var _Role_index = [...]uint8{0, 14, 25, 43, 65, 88, 101, 113} func (i Role) String() string { if i >= Role(len(_Role_index)-1) { diff --git a/enums/role-en.go b/enums/role-en.go index 6a7ea87..8402b20 100644 --- a/enums/role-en.go +++ b/enums/role-en.go @@ -6,7 +6,7 @@ type Role uint32 const ( RoleUndefined Role = iota // undefined-role - RoleTerminus // terminus-role + RoleAnchor // anchor-role RoleClientFilter // client-filter-role RoleClientHiberWake // client-hiber-wake-role RoleClientHiberSleep // client-hiber-sleep-role diff --git a/factories.go b/factories.go index 8dd4f86..70df04f 100644 --- a/factories.go +++ b/factories.go @@ -48,7 +48,8 @@ func (f *walkerFac) Configure() Director { err: err, }, }, - plugins: artefacts.plugins, + plugins: artefacts.plugins, + activeRoles: artefacts.activeRoles, }, } }) @@ -77,7 +78,8 @@ func (f *runnerFac) Configure() Director { }, wg: f.wg, }, - plugins: artefacts.plugins, + plugins: artefacts.plugins, + activeRoles: artefacts.activeRoles, }, } }) diff --git a/internal/hiber/hibernate-plugin.go b/internal/hiber/hibernate-plugin.go index d80b47e..4c7d256 100644 --- a/internal/hiber/hibernate-plugin.go +++ b/internal/hiber/hibernate-plugin.go @@ -12,7 +12,8 @@ func IfActive(o *pref.Options, mediator types.Mediator) types.Plugin { if o.Core.Hibernate.Wake != nil { return &Plugin{ BasePlugin: kernel.BasePlugin{ - Mediator: mediator, + Mediator: mediator, + ActivatedRole: enums.RoleClientHiberSleep, // TODO: or wake; to be resolved }, } } @@ -40,10 +41,6 @@ func (p *Plugin) Next(node *core.Node) (bool, error) { return true, nil } -func (p *Plugin) Role() enums.Role { - return enums.RoleClientHiberWake // !!! -} - func (p *Plugin) Init() error { return p.Mediator.Decorate(p) } diff --git a/internal/kernel/base-plugin.go b/internal/kernel/base-plugin.go index 72f2396..0960855 100644 --- a/internal/kernel/base-plugin.go +++ b/internal/kernel/base-plugin.go @@ -1,10 +1,16 @@ package kernel import ( + "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/internal/types" ) type BasePlugin struct { - Mediator types.Mediator - Kontroller types.KernelController + Mediator types.Mediator + Kontroller types.KernelController + ActivatedRole enums.Role +} + +func (p *BasePlugin) Role() enums.Role { + return p.ActivatedRole } diff --git a/internal/kernel/guardian.go b/internal/kernel/guardian.go index 4a9db48..573e7c7 100644 --- a/internal/kernel/guardian.go +++ b/internal/kernel/guardian.go @@ -2,8 +2,9 @@ package kernel import ( "errors" + "slices" - "github.com/snivilised/extendio/collections" + "github.com/snivilised/traverse/collections" "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/internal/lo" @@ -12,8 +13,8 @@ import ( ) type ( - invocationChain = collections.Stack[types.Link] - invocationIt = collections.Iterator[types.Link] + invocationChain = map[enums.Role]types.Link + positionalRoleSet = collections.PositionalSet[enums.Role] ) type owned struct { @@ -23,7 +24,7 @@ type owned struct { // anchor is a specialised link that should always be the // last in the chain and contains the original client's handler. type anchor struct { - target core.Client + client core.Client owned owned } @@ -35,88 +36,101 @@ func (t *anchor) Next(node *core.Node) (bool, error) { metric.Tick() } - return false, t.target(node) + return false, t.client(node) } func (t *anchor) Role() enums.Role { - return enums.RoleTerminus + return enums.RoleAnchor } type iterationContainer struct { - invocationChain - // it iterates the contents of the chain in reverse; we - // need a reverse iterator, because the first element of the chain - // (index 0), will always represent the client's actual callback, - // that should only ever be called last, if the prior links permits it - // to be so. - // - it invocationIt + invoker Invokable + positions *positionalRoleSet + chain invocationChain } // guardian controls access to the client callback type guardian struct { - callback core.Client - chain iterationContainer - master types.GuardianSealer + container iterationContainer + master types.GuardianSealer + anchor *anchor } -func newGuardian(callback core.Client, +func newGuardian(client core.Client, master types.GuardianSealer, mums measure.Mutables, ) *guardian { - // TODO: need to pass in a sequence manifest that describes the - // valid chain of decorators, defined by role eg - // { enums.RoleTerminus, enums.RoleClientFilter, } - // - stack := collections.NewStack[types.Link]() - stack.Push(&anchor{ - target: callback, + anchor := &anchor{ + client: client, owned: owned{ mums: mums, }, - }) + } return &guardian{ - callback: callback, - chain: iterationContainer{ - invocationChain: *stack, - it: collections.ReverseIt(stack.Content(), nil), + container: iterationContainer{ + chain: make(invocationChain), }, master: master, + anchor: anchor, } } +func (g *guardian) arrange(activeRoles []enums.Role) { + g.container.chain[enums.RoleAnchor] = g.anchor + + if len(activeRoles) == 0 { + g.container.invoker = NodeInvoker(func(node *core.Node) error { + _, err := g.anchor.Next(node) + return err + }) + + return + } + + order := make([]enums.Role, 0, len(activeRoles)+1) + for _, role := range manifest { + if slices.Contains(activeRoles, role) { + order = append(order, role) + } + } + + g.container.positions = collections.NewPositionalSet(order, enums.RoleAnchor) + g.container.invoker = NodeInvoker(func(node *core.Node) error { + return g.iterate(node) + }) + + g.container.positions.Items() +} + // role indicates the guise under which the decorator is being applied. // Not all roles can be decorated (sealed). The fastward-resume decorator is // sealed. If an attempt is made to Decorate a sealed decorator, // an error is returned. func (g *guardian) Decorate(link types.Link) error { - // role enums.Role, decorator core.Client - // - // if every feature is active - // [prime]: - // hiber => sampling => filtering => callback - // [fastward-resume]: - // fastward-filter => [prime] - // - // sequence: fastward-filter => hiber => sampling => filtering => callback - // - top, err := g.chain.Current() - if err != nil { - return err - } + top := g.container.chain[g.container.positions.Items()[0]] if g.master.IsSealed(top) { return errors.New("can't decorate, last item is sealed") } - g.chain.Push(link) - g.create() + role := link.Role() + g.container.chain[role] = link + g.container.positions.Insert(role) return nil } -func (g *guardian) Unwind(enums.Role) error { +func (g *guardian) Unwind(role enums.Role) error { + if role == enums.RoleAnchor { + return nil + } + + delete(g.container.chain, role) + g.container.positions.Delete(role) + + // TODO: required only for fastward resume or hibernation + // return nil } @@ -124,10 +138,13 @@ func (g *guardian) Unwind(enums.Role) error { // the invocation of the client's callback, depending on the contents // of the chain. func (g *guardian) Invoke(node *core.Node) error { - // TODO: Actually, using an iterator is not the best way forward as it - // adds unnecessary overhead. Each link should have access to the next, - // without depending on an iterator. - for link := g.chain.it.Start(); g.chain.it.Valid(); g.chain.it.Next() { + return g.container.invoker.Invoke(node) +} + +func (g *guardian) iterate(node *core.Node) error { + for _, role := range g.container.positions.Items() { + link := g.container.chain[role] + if next, err := link.Next(node); !next || err != nil { return err } @@ -136,12 +153,6 @@ func (g *guardian) Invoke(node *core.Node) error { return nil } -// create rebuilds the iterator, should be called after the chain has been -// modified. -func (g *guardian) create() { - g.chain.it = collections.ReverseIt(g.chain.Content(), nil) -} - // Benign is used when a master sealer has not been registered. It is // permissive in nature. type Benign struct { diff --git a/internal/kernel/kernel-defs.go b/internal/kernel/kernel-defs.go index b039bc3..b27a297 100644 --- a/internal/kernel/kernel-defs.go +++ b/internal/kernel/kernel-defs.go @@ -9,11 +9,24 @@ import ( "github.com/snivilised/traverse/internal/types" ) +var ( + // the manifest dictates the order in which decorators + // are applied over the top of the client callback + // function. + manifest = []enums.Role{ + enums.RoleFastward, + enums.RoleClientHiberWake, + enums.RoleClientHiberSleep, + enums.RoleClientFilter, + enums.RoleSampler, + } +) + type ( // NavigatorImpl NavigatorImpl interface { - // Starting - Starting(session core.Session) + // Ignite + Ignite(ignition *types.Ignition) // Top Top(ctx context.Context, @@ -85,11 +98,17 @@ type ( Unwind(role enums.Role) error } + // Invokable Invokable interface { - Gateway Invoke(node *core.Node) error } + // Mutant represents the mutable interface to the Guardian + Mutant interface { + Gateway + Invokable + } + // navigationStatic contains static info, ie info that is established during // bootstrap and doesn't change after navigation begins. Used to help // minimise allocations. @@ -158,3 +177,9 @@ func (v *navigationVapour) pick(et enums.EntryType) { v.ents = v.cargo.files } } + +type NodeInvoker func(node *core.Node) error + +func (fn NodeInvoker) Invoke(node *core.Node) error { + return fn(node) +} diff --git a/internal/kernel/mediator.go b/internal/kernel/mediator.go index b6d4d58..53ccd27 100644 --- a/internal/kernel/mediator.go +++ b/internal/kernel/mediator.go @@ -17,7 +17,7 @@ type mediator struct { root string using *pref.Using impl NavigatorImpl - client Invokable + guardian *guardian frame *navigationFrame pad *scratchPad // gets created just before nav begins o *pref.Options @@ -48,7 +48,7 @@ func newMediator(using *pref.Using, root: using.Root, using: using, impl: impl, - client: newGuardian(using.Handler, sealer, resources.Supervisor.Many( + guardian: newGuardian(using.Handler, sealer, resources.Supervisor.Many( enums.MetricNoFilesInvoked, enums.MetricNoFoldersInvoked, )), @@ -62,15 +62,19 @@ func newMediator(using *pref.Using, } func (m *mediator) Decorate(link types.Link) error { - return m.client.Decorate(link) + return m.guardian.Decorate(link) } func (m *mediator) Unwind(role enums.Role) error { - return m.client.Unwind(role) + return m.guardian.Unwind(role) } -func (m *mediator) Starting(session core.Session) { - m.impl.Starting(session) +func (m *mediator) Arrange(activeRoles []enums.Role) { + m.guardian.arrange(activeRoles) +} + +func (m *mediator) Ignite(ignition *types.Ignition) { + m.impl.Ignite(ignition) } func (m *mediator) Navigate(ctx context.Context) (core.TraverseResult, error) { @@ -95,7 +99,7 @@ func (m *mediator) Spawn(ctx context.Context, root string) (core.TraverseResult, } func (m *mediator) Invoke(node *core.Node) error { - return m.client.Invoke(node) + return m.guardian.Invoke(node) } func (m *mediator) Supervisor() *measure.Supervisor { diff --git a/internal/kernel/navigation-controller.go b/internal/kernel/navigation-controller.go index ba1f7e3..aeef7b5 100644 --- a/internal/kernel/navigation-controller.go +++ b/internal/kernel/navigation-controller.go @@ -11,22 +11,22 @@ type NavigationController struct { Mediator *mediator } -func (nc *NavigationController) Result(ctx context.Context, err error) *types.KernelResult { - return nc.Mediator.impl.Result(ctx, err) -} - -func (nc *NavigationController) Starting(session core.Session) { - nc.Mediator.Starting(session) +func (nc *NavigationController) Register(types.Plugin) error { + return nil } -func (nc *NavigationController) Navigate(ctx context.Context) (core.TraverseResult, error) { - return nc.Mediator.Navigate(ctx) +func (nc *NavigationController) Ignite(ignition *types.Ignition) { + nc.Mediator.Ignite(ignition) } func (nc *NavigationController) Impl() NavigatorImpl { return nc.Mediator.impl } -func (nc *NavigationController) Register(types.Plugin) error { - return nil +func (nc *NavigationController) Navigate(ctx context.Context) (core.TraverseResult, error) { + return nc.Mediator.Navigate(ctx) +} + +func (nc *NavigationController) Result(ctx context.Context, err error) *types.KernelResult { + return nc.Mediator.impl.Result(ctx, err) } diff --git a/internal/kernel/navigator-agent.go b/internal/kernel/navigator-agent.go index 81b7fe8..91d3846 100644 --- a/internal/kernel/navigator-agent.go +++ b/internal/kernel/navigator-agent.go @@ -57,8 +57,8 @@ func (n *navigatorAgent) ascend(navi *navigationInfo, permit bool) { _, _ = navi, permit } -func (n *navigatorAgent) Starting(session core.Session) { - n.session = session +func (n *navigatorAgent) Ignite(ignition *types.Ignition) { + n.session = ignition.Session } func (n *navigatorAgent) top(ctx context.Context, diff --git a/internal/kernel/navigator-hades.go b/internal/kernel/navigator-hades.go index 0968bba..d91e9dd 100644 --- a/internal/kernel/navigator-hades.go +++ b/internal/kernel/navigator-hades.go @@ -17,13 +17,16 @@ type navigatorHades struct { err error } -func (n *navigatorHades) Result(_ context.Context, err error) *types.KernelResult { - return types.NewFailed(err) +func (n *navigatorHades) Rank() { } -func (n *navigatorHades) Starting(_ core.Session) { +func (n *navigatorHades) Ignite(*types.Ignition) { } func (n *navigatorHades) Navigate(ctx context.Context) (core.TraverseResult, error) { return n.Result(ctx, n.err), n.err } + +func (n *navigatorHades) Result(_ context.Context, err error) *types.KernelResult { + return types.NewFailed(err) +} diff --git a/internal/refine/filter-plugin.go b/internal/refine/filter-plugin.go index 2dcd695..d73d0cf 100644 --- a/internal/refine/filter-plugin.go +++ b/internal/refine/filter-plugin.go @@ -12,7 +12,8 @@ func IfActive(o *pref.Options, mediator types.Mediator) types.Plugin { if o.Core.Filter.Node != nil { return &Plugin{ BasePlugin: kernel.BasePlugin{ - Mediator: mediator, + Mediator: mediator, + ActivatedRole: enums.RoleClientFilter, }, } } @@ -45,10 +46,6 @@ func (p *Plugin) Next(node *core.Node) (bool, error) { return true, nil } -func (p *Plugin) Role() enums.Role { - return enums.RoleClientFilter -} - func (p *Plugin) Init() error { p.Mediator.Supervisor().Many( enums.MetricNoFoldersFilteredOut, diff --git a/internal/resume/controller.go b/internal/resume/controller.go index 298bf19..62259d0 100644 --- a/internal/resume/controller.go +++ b/internal/resume/controller.go @@ -18,8 +18,8 @@ type Controller struct { facilities types.Facilities } -func (c *Controller) Starting(session core.Session) { - c.kc.Starting(session) +func (c *Controller) Ignite(ignition *types.Ignition) { + c.kc.Ignite(ignition) } func (c *Controller) Result(ctx context.Context, err error) *types.KernelResult { diff --git a/internal/sampling/sampling-plugin.go b/internal/sampling/sampling-plugin.go index d8ff2e9..39e8cdc 100644 --- a/internal/sampling/sampling-plugin.go +++ b/internal/sampling/sampling-plugin.go @@ -12,7 +12,8 @@ func IfActive(o *pref.Options, mediator types.Mediator) types.Plugin { if (o.Core.Sampling.NoOf.Files > 0) || (o.Core.Sampling.NoOf.Folders > 0) { return &Plugin{ BasePlugin: kernel.BasePlugin{ - Mediator: mediator, + Mediator: mediator, + ActivatedRole: enums.RoleSampler, }, } } @@ -41,10 +42,6 @@ func (p *Plugin) Next(node *core.Node) (bool, error) { return true, nil } -func (p *Plugin) Role() enums.Role { - return enums.RoleSampler -} - func (p *Plugin) Init() error { return p.Mediator.Decorate(p) } diff --git a/internal/types/definitions.go b/internal/types/definitions.go index e58da15..1e31d6c 100644 --- a/internal/types/definitions.go +++ b/internal/types/definitions.go @@ -40,8 +40,15 @@ type ( Unwind(role enums.Role) error } + Arrangeable interface { + Arrange(roles []enums.Role) + } + + // Mediator controls interactions between different entities of + // of the navigator Mediator interface { Guardian + Arrangeable Navigate(ctx context.Context) (core.TraverseResult, error) Spawn(ctx context.Context, root string) (core.TraverseResult, error) Supervisor() *measure.Supervisor @@ -61,6 +68,7 @@ type ( Plugin interface { Name() string Register(kc KernelController) error + Role() enums.Role Init() error } @@ -76,10 +84,14 @@ type ( Metrics() *measure.Supervisor } + Ignition struct { + Session core.Session + } + // KernelController KernelController interface { core.Navigator - Starting(session core.Session) + Ignite(ignition *Ignition) Result(ctx context.Context, err error) *KernelResult } ) diff --git a/session.go b/session.go index b29a464..2fbe31e 100644 --- a/session.go +++ b/session.go @@ -5,41 +5,26 @@ import ( "time" "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/internal/types" ) type session struct { - sync synchroniser - started time.Time - duration time.Duration - plugins []types.Plugin + sync synchroniser + started time.Time + duration time.Duration + plugins []types.Plugin + activeRoles []enums.Role } func (s *session) start() { s.started = time.Now() - s.sync.Starting(s) + s.sync.Ignite(&types.Ignition{ + Session: s, + }) } -/* -func (s *session) finish(result *TraverseResult, _ error) { - s.duration = time.Since(s.startAt) - - if result != nil { - result.Session = s - } -} -*/ - -func (s *session) finish(result core.TraverseResult) { - _ = result - // if result != nil { - // result.Session - // } - // I wonder if the traverse result should become available - // as a result of a message sent of the bus. Any component - // needing access to the result should handle the message. This - // way, we don't have to explicitly pass it around. - // +func (s *session) finish(_ core.TraverseResult) { s.duration = time.Since(s.started) } diff --git a/synchronise.go b/synchronise.go index 150e7ce..c339d65 100644 --- a/synchronise.go +++ b/synchronise.go @@ -13,7 +13,7 @@ import ( type synchroniser interface { core.Navigator - Starting(core.Session) + Ignite(*types.Ignition) IsComplete() bool } @@ -35,8 +35,8 @@ func (t trunk) IsComplete() bool { return t.ext.complete() } -func (t trunk) Starting(session core.Session) { - t.kc.Starting(session) +func (t trunk) Ignite(ignition *types.Ignition) { + t.kc.Ignite(ignition) } type concurrent struct {