From e49339f8b053a1577071b6946ff578147e75df94 Mon Sep 17 00:00:00 2001 From: plastikfan Date: Tue, 21 May 2024 12:12:36 +0100 Subject: [PATCH] feat: create navigators (#33) --- Taskfile.yml | 8 ++- builders.go | 29 ++++++-- director.go | 55 +++----------- director_test.go | 39 ++++++++++ internal/kernel/kernel-defs.go | 13 +--- internal/kernel/kernel-suite_test.go | 13 ++++ internal/kernel/kernel-support_test.go | 6 ++ internal/kernel/kernel-types.go | 4 -- internal/kernel/navigator-base.go | 9 +++ internal/kernel/navigator-factory.go | 71 ++++++++++--------- internal/kernel/navigator-files.go | 5 ++ internal/kernel/navigator-files_test.go | 39 ++++++++++ .../navigator-folders-with-files_test.go | 39 ++++++++++ internal/kernel/navigator-folders.go | 5 ++ internal/kernel/navigator-folders_test.go | 39 ++++++++++ internal/kernel/navigator-hades.go | 29 ++++++++ internal/kernel/navigator-universal.go | 5 ++ internal/kernel/navigator-universal_test.go | 39 ++++++++++ pref/options.go | 27 ++++--- support_test.go | 9 +++ 20 files changed, 371 insertions(+), 112 deletions(-) create mode 100644 internal/kernel/kernel-suite_test.go create mode 100644 internal/kernel/kernel-support_test.go delete mode 100644 internal/kernel/kernel-types.go create mode 100644 internal/kernel/navigator-base.go create mode 100644 internal/kernel/navigator-files.go create mode 100644 internal/kernel/navigator-files_test.go create mode 100644 internal/kernel/navigator-folders-with-files_test.go create mode 100644 internal/kernel/navigator-folders.go create mode 100644 internal/kernel/navigator-folders_test.go create mode 100644 internal/kernel/navigator-hades.go create mode 100644 internal/kernel/navigator-universal.go create mode 100644 internal/kernel/navigator-universal_test.go create mode 100644 support_test.go diff --git a/Taskfile.yml b/Taskfile.yml index 6cd5efd..c680f40 100644 --- a/Taskfile.yml +++ b/Taskfile.yml @@ -28,9 +28,9 @@ tasks: cmds: - go test ./... - tt: + tk: cmds: - - go test + - go test ./internal/kernel ti: cmds: @@ -40,6 +40,10 @@ tasks: cmds: - go test ./pref + tt: + cmds: + - go test + # === ginkgo ================================================ # initialise a test suite for a package. (only 1 per package) diff --git a/builders.go b/builders.go index 9c080fa..c384a1c 100644 --- a/builders.go +++ b/builders.go @@ -4,6 +4,7 @@ import ( "errors" "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/internal/kernel" "github.com/snivilised/traverse/internal/types" "github.com/snivilised/traverse/pref" ) @@ -23,17 +24,32 @@ type Builders struct { func (bs *Builders) buildAll() (*buildArtefacts, error) { o, optionsErr := bs.ob.build() if optionsErr != nil { - return nil, optionsErr + had, _ := kernel.HadesNav(optionsErr) + + return &buildArtefacts{ + o: o, + nav: had, + }, optionsErr } nav, navErr := bs.nb.build(o) if navErr != nil { - return nil, navErr + had, _ := kernel.HadesNav(navErr) + + return &buildArtefacts{ + o: o, + nav: had, + }, navErr } plugins, pluginsErr := bs.pb.build(o) if pluginsErr != nil { - return nil, pluginsErr + had, _ := kernel.HadesNav(pluginsErr) + + return &buildArtefacts{ + o: o, + nav: had, + }, pluginsErr } if host, ok := nav.(types.UsePlugin); ok { @@ -44,7 +60,12 @@ func (bs *Builders) buildAll() (*buildArtefacts, error) { } if pluginErr := errors.Join(es...); pluginErr != nil { - return nil, pluginErr + had, _ := kernel.HadesNav(pluginErr) + + return &buildArtefacts{ + o: o, + nav: had, + }, pluginErr } } diff --git a/director.go b/director.go index 120a784..88293f8 100644 --- a/director.go +++ b/director.go @@ -2,7 +2,6 @@ package tv import ( "github.com/snivilised/traverse/core" - "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/hiber" "github.com/snivilised/traverse/internal/kernel" "github.com/snivilised/traverse/internal/services" @@ -12,30 +11,6 @@ import ( "github.com/snivilised/traverse/sampling" ) -type duffPrimeController struct { - err error - root string - client core.Client - from string - options []pref.Option -} - -type duffResult struct{} - -func (r *duffResult) Error() error { - return nil -} - -func (c *duffPrimeController) Navigate() (core.TraverseResult, error) { - return &duffResult{}, nil -} - -type duffResumeController struct { - err error - from string - strategy enums.ResumeStrategy -} - type ifActive func(o *pref.Options) types.Plugin // activated interrogates options and invokes requests on behalf of the user @@ -59,10 +34,6 @@ func activated(o *pref.Options) ([]types.Plugin, error) { return plugins, err } -func (c *duffResumeController) Navigate() (core.TraverseResult, error) { - return &duffResult{}, nil -} - // Prime extent func Prime(using core.Using, settings ...pref.Option) *Builders { return &Builders{ @@ -77,13 +48,7 @@ func Prime(using core.Using, settings ...pref.Option) *Builders { return pref.Get(settings...) }), nb: factory(func(o *pref.Options) (core.Navigator, error) { - controller, err := kernel.Prime(using, o) - - if err != nil { - controller = &duffPrimeController{ - err: err, - } - } + controller, err := kernel.PrimeNav(using, o) return controller, err }), @@ -115,7 +80,7 @@ func Resume(as As, settings ...pref.Option) *Builders { return o, err }), nb: factory(func(o *pref.Options) (core.Navigator, error) { - controller, err := kernel.Resume(as, o, + controller, err := kernel.ResumeNav(as, o, kernel.DecorateController(func(n core.Navigator) core.Navigator { // TODO: create the resume controller // @@ -123,12 +88,6 @@ func Resume(as As, settings ...pref.Option) *Builders { }), ) - if err != nil { - controller = &duffResumeController{ - err: err, - } - } - // at this point, the resume controller does not know // the wake point as would be loaded by the options // builder. @@ -171,11 +130,13 @@ func (f *walker) Configure() Director { // the extent is, so we know if we need to make this query? // // - artefacts, _ := bs.buildAll() // TODO: check error + artefacts, err := bs.buildAll() - // Announce the availability of the navigator via UsePlugin interface - ctx, _ := artefacts.o.Acceleration.Cancellation() - _ = services.Broker.Emit(ctx, services.TopicInterceptNavigator, artefacts.nav) + if err == nil { + // Announce the availability of the navigator via UsePlugin interface + ctx, _ := artefacts.o.Acceleration.Cancellation() + _ = services.Broker.Emit(ctx, services.TopicInterceptNavigator, artefacts.nav) + } return &driver{ session{ diff --git a/director_test.go b/director_test.go index 9c3dee1..35ca052 100644 --- a/director_test.go +++ b/director_test.go @@ -59,6 +59,45 @@ var _ = Describe("Traverse", Ordered, func() { }) }) + When("Prime with subscription error", func() { + It("🧪 should: fail", func() { + defer leaktest.Check(GinkgoT())() + + _, err := tv.Walk().Configure().Extent(tv.Prime( + tv.Using{ + Root: RootPath, + Handler: func(_ *tv.Node) error { + return nil + }, + }, + )).Navigate() + + Expect(err).NotTo(Succeed()) + }) + }) + + When("Prime with options build error", func() { + It("🧪 should: fail", func() { + defer leaktest.Check(GinkgoT())() + + _, err := tv.Walk().Configure().Extent(tv.Prime( + tv.Using{ + Root: RootPath, + Subscription: tv.SubscribeFiles, + Handler: func(_ *tv.Node) error { + return nil + }, + }, + tv.WithSubscription(tv.SubscribeFiles), + func(_ *pref.Options) error { + return errBuildOptions + }, + )).Navigate() + + Expect(err).To(MatchError(errBuildOptions)) + }) + }) + When("Resume", func() { It("🧪 should: walk resume navigation successfully", func() { defer leaktest.Check(GinkgoT())() diff --git a/internal/kernel/kernel-defs.go b/internal/kernel/kernel-defs.go index 3403489..aaca0f5 100644 --- a/internal/kernel/kernel-defs.go +++ b/internal/kernel/kernel-defs.go @@ -1,15 +1,5 @@ package kernel -// type Navigator interface { -// Navigate() (core.TraverseResult, error) -// } - -// type NavigatorFunc func() (core.TraverseResult, error) - -// func (fn NavigatorFunc) Navigate() (core.TraverseResult, error) { -// return fn() -// } - type navigationResult struct { err error } @@ -17,3 +7,6 @@ type navigationResult struct { func (r *navigationResult) Error() error { return r.err } + +type navigatorImpl interface { +} diff --git a/internal/kernel/kernel-suite_test.go b/internal/kernel/kernel-suite_test.go new file mode 100644 index 0000000..b2070cb --- /dev/null +++ b/internal/kernel/kernel-suite_test.go @@ -0,0 +1,13 @@ +package kernel_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok +) + +func TestKernel(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Kernel Suite") +} diff --git a/internal/kernel/kernel-support_test.go b/internal/kernel/kernel-support_test.go new file mode 100644 index 0000000..eef10c3 --- /dev/null +++ b/internal/kernel/kernel-support_test.go @@ -0,0 +1,6 @@ +package kernel_test + +const ( + RootPath = "/traversal-root-path" + RestorePath = "/from-restore-path" +) diff --git a/internal/kernel/kernel-types.go b/internal/kernel/kernel-types.go deleted file mode 100644 index 6cc42b9..0000000 --- a/internal/kernel/kernel-types.go +++ /dev/null @@ -1,4 +0,0 @@ -package kernel - -type navigatorImpl interface { -} diff --git a/internal/kernel/navigator-base.go b/internal/kernel/navigator-base.go new file mode 100644 index 0000000..1b1240a --- /dev/null +++ b/internal/kernel/navigator-base.go @@ -0,0 +1,9 @@ +package kernel + +import ( + "github.com/snivilised/traverse/pref" +) + +type navigatorBase struct { + o *pref.Options +} diff --git a/internal/kernel/navigator-factory.go b/internal/kernel/navigator-factory.go index 5ffa199..f4e877e 100644 --- a/internal/kernel/navigator-factory.go +++ b/internal/kernel/navigator-factory.go @@ -1,19 +1,23 @@ package kernel import ( - "errors" - "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/enums" "github.com/snivilised/traverse/pref" ) -func Prime(using core.Using, o *pref.Options) (core.Navigator, error) { +func PrimeNav(using core.Using, o *pref.Options) (core.Navigator, error) { return newController(&using, o) } -func Resume(with core.As, o *pref.Options, resumption Resumption) (core.Navigator, error) { - controller, err := newController(&with.Using, o) +func ResumeNav(with core.As, o *pref.Options, + resumption Resumption, +) (controller core.Navigator, err error) { + controller, err = newController(&with.Using, o) + + if err != nil { + return HadesNav(err) + } return resumption.Decorate(controller), err } @@ -28,47 +32,46 @@ func (f DecorateController) Decorate(source core.Navigator) core.Navigator { return f(source) } -func newController(using *core.Using, o *pref.Options) (core.Navigator, error) { - if err := using.Validate(); err != nil { - return nil, err +func newController(using *core.Using, + o *pref.Options, +) (navigator core.Navigator, err error) { + if err = using.Validate(); err != nil { + return } - var ( - impl core.Navigator - err error - ) - - impl, err = newImpl(using, o) + impl := newImpl(using, o) - navigator := &navigationController{ + navigator = &navigationController{ impl: impl, o: o, } - return navigator, err + return } -func newImpl(using *core.Using, o *pref.Options) (core.Navigator, error) { - var ( - navigator core.Navigator - err error - subscription = using.Subscription - ) +func newImpl(using *core.Using, + o *pref.Options, +) (navigator navigatorImpl) { + base := navigatorBase{ + o: o, + } - switch subscription { + switch using.Subscription { //nolint:exhaustive // already validated by using case enums.SubscribeFiles: - navigator = &navigationController{ - o: o, - } // just temporary (create the impl's) - case enums.SubscribeFolders: - navigator = &navigationController{} - case enums.SubscribeFoldersWithFiles: - navigator = &navigationController{} + navigator = &navigatorFiles{ + navigatorBase: base, + } + + case enums.SubscribeFolders, enums.SubscribeFoldersWithFiles: + navigator = &navigatorFolders{ + navigatorBase: base, + } + case enums.SubscribeUniversal: - navigator = &navigationController{} - case enums.SubscribeUndefined: - err = errors.New("invalid subscription") + navigator = &navigatorUniversal{ + navigatorBase: base, + } } - return navigator, err + return } diff --git a/internal/kernel/navigator-files.go b/internal/kernel/navigator-files.go new file mode 100644 index 0000000..e430f47 --- /dev/null +++ b/internal/kernel/navigator-files.go @@ -0,0 +1,5 @@ +package kernel + +type navigatorFiles struct { + navigatorBase +} diff --git a/internal/kernel/navigator-files_test.go b/internal/kernel/navigator-files_test.go new file mode 100644 index 0000000..c33a01b --- /dev/null +++ b/internal/kernel/navigator-files_test.go @@ -0,0 +1,39 @@ +package kernel_test + +import ( + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok + + "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/enums" + "github.com/snivilised/traverse/internal/kernel" + "github.com/snivilised/traverse/pref" +) + +var _ = Describe("NavigatorFiles", func() { + var o *pref.Options + + BeforeEach(func() { + o, _ = pref.Get() + }) + + Context("nav", func() { + When("foo", func() { + It("🧪 should: not fail", func() { + nav, err := kernel.PrimeNav( + core.Using{ + Root: RootPath, + Subscription: enums.SubscribeFiles, + Handler: func(_ *core.Node) error { + return nil + }, + }, + o, + ) + + Expect(err).To(Succeed()) + Expect(nav).NotTo(BeNil()) + }) + }) + }) +}) diff --git a/internal/kernel/navigator-folders-with-files_test.go b/internal/kernel/navigator-folders-with-files_test.go new file mode 100644 index 0000000..e7f0538 --- /dev/null +++ b/internal/kernel/navigator-folders-with-files_test.go @@ -0,0 +1,39 @@ +package kernel_test + +import ( + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok + + "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/enums" + "github.com/snivilised/traverse/internal/kernel" + "github.com/snivilised/traverse/pref" +) + +var _ = Describe("NavigatorFoldersWithFiles", func() { + var o *pref.Options + + BeforeEach(func() { + o, _ = pref.Get() + }) + + Context("nav", func() { + When("foo", func() { + It("🧪 should: not fail", func() { + nav, err := kernel.PrimeNav( + core.Using{ + Root: RootPath, + Subscription: enums.SubscribeFoldersWithFiles, + Handler: func(_ *core.Node) error { + return nil + }, + }, + o, + ) + + Expect(err).To(Succeed()) + Expect(nav).NotTo(BeNil()) + }) + }) + }) +}) diff --git a/internal/kernel/navigator-folders.go b/internal/kernel/navigator-folders.go new file mode 100644 index 0000000..d773392 --- /dev/null +++ b/internal/kernel/navigator-folders.go @@ -0,0 +1,5 @@ +package kernel + +type navigatorFolders struct { + navigatorBase +} diff --git a/internal/kernel/navigator-folders_test.go b/internal/kernel/navigator-folders_test.go new file mode 100644 index 0000000..c9769aa --- /dev/null +++ b/internal/kernel/navigator-folders_test.go @@ -0,0 +1,39 @@ +package kernel_test + +import ( + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok + + "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/enums" + "github.com/snivilised/traverse/internal/kernel" + "github.com/snivilised/traverse/pref" +) + +var _ = Describe("NavigatorFolders", func() { + var o *pref.Options + + BeforeEach(func() { + o, _ = pref.Get() + }) + + Context("nav", func() { + When("foo", func() { + It("🧪 should: not fail", func() { + nav, err := kernel.PrimeNav( + core.Using{ + Root: RootPath, + Subscription: enums.SubscribeFolders, + Handler: func(_ *core.Node) error { + return nil + }, + }, + o, + ) + + Expect(err).To(Succeed()) + Expect(nav).NotTo(BeNil()) + }) + }) + }) +}) diff --git a/internal/kernel/navigator-hades.go b/internal/kernel/navigator-hades.go new file mode 100644 index 0000000..7eb6b5d --- /dev/null +++ b/internal/kernel/navigator-hades.go @@ -0,0 +1,29 @@ +package kernel + +import ( + "github.com/snivilised/traverse/core" +) + +func HadesNav(err error) (core.Navigator, error) { + return &navigatorHades{ + err: err, + }, err +} + +type hadesResult struct { + err error +} + +func (r *hadesResult) Error() error { + return r.err +} + +type navigatorHades struct { + err error +} + +func (n *navigatorHades) Navigate() (core.TraverseResult, error) { + return &hadesResult{ + err: n.err, + }, n.err +} diff --git a/internal/kernel/navigator-universal.go b/internal/kernel/navigator-universal.go new file mode 100644 index 0000000..e93b132 --- /dev/null +++ b/internal/kernel/navigator-universal.go @@ -0,0 +1,5 @@ +package kernel + +type navigatorUniversal struct { + navigatorBase +} diff --git a/internal/kernel/navigator-universal_test.go b/internal/kernel/navigator-universal_test.go new file mode 100644 index 0000000..ab27147 --- /dev/null +++ b/internal/kernel/navigator-universal_test.go @@ -0,0 +1,39 @@ +package kernel_test + +import ( + . "github.com/onsi/ginkgo/v2" //nolint:revive // ok + . "github.com/onsi/gomega" //nolint:revive // ok + + "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/enums" + "github.com/snivilised/traverse/internal/kernel" + "github.com/snivilised/traverse/pref" +) + +var _ = Describe("NavigatorUniversal", func() { + var o *pref.Options + + BeforeEach(func() { + o, _ = pref.Get() + }) + + Context("nav", func() { + When("foo", func() { + It("🧪 should: not fail", func() { + nav, err := kernel.PrimeNav( + core.Using{ + Root: RootPath, + Subscription: enums.SubscribeUniversal, + Handler: func(_ *core.Node) error { + return nil + }, + }, + o, + ) + + Expect(err).To(Succeed()) + Expect(nav).NotTo(BeNil()) + }) + }) + }) +}) diff --git a/pref/options.go b/pref/options.go index 24e7ff0..c661a84 100644 --- a/pref/options.go +++ b/pref/options.go @@ -62,12 +62,12 @@ type ( Option func(o *Options) error ) -func Get(settings ...Option) (*Options, error) { - o := DefaultOptions() +func Get(settings ...Option) (o *Options, err error) { + o = DefaultOptions() binder := NewBinder() o.Events.Bind(&binder.Notification) - apply(o, settings...) + err = apply(o, settings...) if o.Acceleration.ctx == nil { o.Acceleration.ctx = context.Background() @@ -75,7 +75,7 @@ func Get(settings ...Option) (*Options, error) { o.Binder = binder - return o, nil + return } type LoadInfo struct { @@ -83,8 +83,8 @@ type LoadInfo struct { WakeAt string } -func Load(from string, settings ...Option) (*Options, error) { - o := DefaultOptions() +func Load(from string, settings ...Option) (o *Options, err error) { + o = DefaultOptions() // do load _ = from binder := NewBinder() @@ -93,7 +93,7 @@ func Load(from string, settings ...Option) (*Options, error) { // TODO: save any active state on the binder, eg the wake point - apply(o, settings...) + err = apply(o, settings...) if o.Acceleration.ctx == nil { o.Acceleration.ctx = context.Background() @@ -104,14 +104,19 @@ func Load(from string, settings ...Option) (*Options, error) { WakeAt: "tbd", } - return o, nil + return } -func apply(o *Options, settings ...Option) { +func apply(o *Options, settings ...Option) (err error) { for _, option := range settings { - // TODO: check error - _ = option(o) + err = option(o) + + if err != nil { + return err + } } + + return } func DefaultOptions() *Options { diff --git a/support_test.go b/support_test.go new file mode 100644 index 0000000..35ab3a8 --- /dev/null +++ b/support_test.go @@ -0,0 +1,9 @@ +package tv_test + +import ( + "errors" +) + +var ( + errBuildOptions = errors.New("options build error") +)