From c0dbfeee6473f93d0df3dbcd2b23f1f7ab688b98 Mon Sep 17 00:00:00 2001 From: plastikfan Date: Wed, 22 May 2024 08:39:25 +0100 Subject: [PATCH] feat: enable options to be pushed (#38) --- core/core-defs.go | 52 ------------ core/errors.go | 13 --- director.go | 6 +- director_test.go | 64 ++++++++++++++ internal/kernel/navigator-factory.go | 8 +- internal/kernel/navigator-files_test.go | 2 +- .../navigator-folders-with-files_test.go | 2 +- internal/kernel/navigator-folders_test.go | 2 +- internal/kernel/navigator-universal_test.go | 2 +- pref/errors.go | 14 +++ pref/options_test.go | 1 - pref/using.go | 85 +++++++++++++++++++ traverse-api.go | 9 +- 13 files changed, 178 insertions(+), 82 deletions(-) create mode 100644 pref/errors.go create mode 100644 pref/using.go diff --git a/core/core-defs.go b/core/core-defs.go index ecf830e..2369de7 100644 --- a/core/core-defs.go +++ b/core/core-defs.go @@ -1,7 +1,5 @@ package core -import "github.com/snivilised/traverse/enums" - // core contains universal definitions and handles cross cutting concerns // try to keep to a minimum to reduce rippling changes @@ -35,53 +33,3 @@ type ( // the traversal node, such as directory ascend or descend. NodeHandler func(node *Node) ) - -type Using struct { - Root string - Subscription enums.Subscription - Handler Client -} - -func (u Using) Validate() error { - if u.Root == "" { - return UsingError{ - message: "missing root path", - } - } - - if u.Subscription == enums.SubscribeUndefined { - return UsingError{ - message: "missing subscription", - } - } - - if u.Handler == nil { - return UsingError{ - message: "missing handler", - } - } - - return nil -} - -type As struct { - Using - From string - Strategy enums.ResumeStrategy -} - -func (a As) Validate() error { - if a.From == "" { - return UsingError{ - message: "missing restore from path", - } - } - - if a.Strategy == enums.ResumeStrategyUndefined { - return UsingError{ - message: "missing subscription", - } - } - - return a.Using.Validate() -} diff --git a/core/errors.go b/core/errors.go index 027383e..9a8bc95 100644 --- a/core/errors.go +++ b/core/errors.go @@ -1,14 +1 @@ package core - -import ( - "fmt" -) - -type UsingError struct { - message string -} - -func (e UsingError) Error() string { - // TODO: i18n - return fmt.Sprintf("using error: %v", e.message) -} diff --git a/director.go b/director.go index c147a29..09f0f93 100644 --- a/director.go +++ b/director.go @@ -35,13 +35,17 @@ func activated(o *pref.Options) ([]types.Plugin, error) { } // Prime extent -func Prime(using core.Using, settings ...pref.Option) *Builders { +func Prime(using pref.Using, settings ...pref.Option) *Builders { return &Builders{ ob: optionals(func() (*pref.Options, error) { if err := using.Validate(); err != nil { return nil, err } + if using.O != nil { + return using.O, nil + } + // we probably need to mark something somehow to indicate // Prime // diff --git a/director_test.go b/director_test.go index 35ca052..4119c9b 100644 --- a/director_test.go +++ b/director_test.go @@ -59,6 +59,26 @@ var _ = Describe("Traverse", Ordered, func() { }) }) + When("Prime with Pushed Options", func() { + It("🧪 should: walk primary navigation successfully", func() { + defer leaktest.Check(GinkgoT())() + + o, _ := pref.Get() + _, err := tv.Walk().Configure().Extent(tv.Prime( + tv.Using{ + Root: RootPath, + Subscription: tv.SubscribeFiles, + Handler: func(_ *tv.Node) error { + return nil + }, + O: o, + }, + )).Navigate() + + Expect(err).To(Succeed()) + }) + }) + When("Prime with subscription error", func() { It("🧪 should: fail", func() { defer leaktest.Check(GinkgoT())() @@ -182,6 +202,50 @@ var _ = Describe("Traverse", Ordered, func() { }) }) + When("Prime with Pushed Options", func() { + It("🧪 should: run primary navigation successfully", func() { + defer leaktest.Check(GinkgoT())() + + ctx, cancel := context.WithCancel(context.Background()) + o, _ := pref.Get( + tv.WithContext(ctx), + tv.WithCancel(cancel), + ) + _, err := tv.Run().Configure().Extent(tv.Prime( + tv.Using{ + Root: RootPath, + Subscription: tv.SubscribeFiles, + Handler: func(_ *tv.Node) error { + return nil + }, + O: o, + }, + )).Navigate() + + Expect(err).To(Succeed()) + }) + }) + + When("Prime with subscription error", func() { + It("🧪 should: fail", func() { + defer leaktest.Check(GinkgoT())() + + ctx, cancel := context.WithCancel(context.Background()) + _, err := tv.Run().Configure().Extent(tv.Prime( + tv.Using{ + Root: RootPath, + Handler: func(_ *tv.Node) error { + return nil + }, + }, + tv.WithContext(ctx), + tv.WithCancel(cancel), + )).Navigate() + + Expect(err).NotTo(Succeed()) + }) + }) + When("Resume", func() { It("🧪 should: perform run navigation successfully", func() { defer leaktest.Check(GinkgoT())() diff --git a/internal/kernel/navigator-factory.go b/internal/kernel/navigator-factory.go index f4e877e..8c1211b 100644 --- a/internal/kernel/navigator-factory.go +++ b/internal/kernel/navigator-factory.go @@ -6,11 +6,11 @@ import ( "github.com/snivilised/traverse/pref" ) -func PrimeNav(using core.Using, o *pref.Options) (core.Navigator, error) { +func PrimeNav(using pref.Using, o *pref.Options) (core.Navigator, error) { return newController(&using, o) } -func ResumeNav(with core.As, o *pref.Options, +func ResumeNav(with pref.As, o *pref.Options, resumption Resumption, ) (controller core.Navigator, err error) { controller, err = newController(&with.Using, o) @@ -32,7 +32,7 @@ func (f DecorateController) Decorate(source core.Navigator) core.Navigator { return f(source) } -func newController(using *core.Using, +func newController(using *pref.Using, o *pref.Options, ) (navigator core.Navigator, err error) { if err = using.Validate(); err != nil { @@ -49,7 +49,7 @@ func newController(using *core.Using, return } -func newImpl(using *core.Using, +func newImpl(using *pref.Using, o *pref.Options, ) (navigator navigatorImpl) { base := navigatorBase{ diff --git a/internal/kernel/navigator-files_test.go b/internal/kernel/navigator-files_test.go index c33a01b..64405fb 100644 --- a/internal/kernel/navigator-files_test.go +++ b/internal/kernel/navigator-files_test.go @@ -21,7 +21,7 @@ var _ = Describe("NavigatorFiles", func() { When("foo", func() { It("🧪 should: not fail", func() { nav, err := kernel.PrimeNav( - core.Using{ + pref.Using{ Root: RootPath, Subscription: enums.SubscribeFiles, Handler: func(_ *core.Node) error { diff --git a/internal/kernel/navigator-folders-with-files_test.go b/internal/kernel/navigator-folders-with-files_test.go index e7f0538..408427d 100644 --- a/internal/kernel/navigator-folders-with-files_test.go +++ b/internal/kernel/navigator-folders-with-files_test.go @@ -21,7 +21,7 @@ var _ = Describe("NavigatorFoldersWithFiles", func() { When("foo", func() { It("🧪 should: not fail", func() { nav, err := kernel.PrimeNav( - core.Using{ + pref.Using{ Root: RootPath, Subscription: enums.SubscribeFoldersWithFiles, Handler: func(_ *core.Node) error { diff --git a/internal/kernel/navigator-folders_test.go b/internal/kernel/navigator-folders_test.go index c9769aa..498bca1 100644 --- a/internal/kernel/navigator-folders_test.go +++ b/internal/kernel/navigator-folders_test.go @@ -21,7 +21,7 @@ var _ = Describe("NavigatorFolders", func() { When("foo", func() { It("🧪 should: not fail", func() { nav, err := kernel.PrimeNav( - core.Using{ + pref.Using{ Root: RootPath, Subscription: enums.SubscribeFolders, Handler: func(_ *core.Node) error { diff --git a/internal/kernel/navigator-universal_test.go b/internal/kernel/navigator-universal_test.go index ab27147..d40e6c5 100644 --- a/internal/kernel/navigator-universal_test.go +++ b/internal/kernel/navigator-universal_test.go @@ -21,7 +21,7 @@ var _ = Describe("NavigatorUniversal", func() { When("foo", func() { It("🧪 should: not fail", func() { nav, err := kernel.PrimeNav( - core.Using{ + pref.Using{ Root: RootPath, Subscription: enums.SubscribeUniversal, Handler: func(_ *core.Node) error { diff --git a/pref/errors.go b/pref/errors.go new file mode 100644 index 0000000..a2e2911 --- /dev/null +++ b/pref/errors.go @@ -0,0 +1,14 @@ +package pref + +import ( + "fmt" +) + +type UsageError struct { + message string +} + +func (e UsageError) Error() string { + // TODO: i18n + return fmt.Sprintf("usage error: %v", e.message) +} diff --git a/pref/options_test.go b/pref/options_test.go index a29fa10..61ed4e1 100644 --- a/pref/options_test.go +++ b/pref/options_test.go @@ -14,7 +14,6 @@ var _ = Describe("Options", func() { When("client listens", func() { It("🧪 should: invoke client's handler", func() { begun := false - o, _ := pref.Get() o.Events.Begin.On(func(_ string) { diff --git a/pref/using.go b/pref/using.go new file mode 100644 index 0000000..3ef4756 --- /dev/null +++ b/pref/using.go @@ -0,0 +1,85 @@ +package pref + +import ( + "github.com/snivilised/traverse/core" + "github.com/snivilised/traverse/enums" +) + +// Using contains essential properties required for a traversal. If +// any of the required properties are missing, then traversal will +// result in an error indicating as such. +type Using struct { + // Root is the path of the directory tree to be traversed + Root string + + // Subscription indicates which file system nodes the client's + // callback function will be invoked for. + Subscription enums.Subscription + + // Handler is the callback function invoked for each encountered + // file system node. + Handler core.Client + + // O is the optional Options entity. If provided, then these + // options will be used verbatim, without requiring WithXXX + // options setters. This is useful if multiple traversals are + // required, eg a preview traversal followed by a full + // traversal; in this case the full traversal can reuse the + // same options that was used in the preview, by setting this + // property. + O *Options +} + +// Validate checks that the properties on Using are all valid. +func (u Using) Validate() error { + if u.Root == "" { + return UsageError{ + message: "missing root path", + } + } + + if u.Subscription == enums.SubscribeUndefined { + return UsageError{ + message: "missing subscription", + } + } + + if u.Handler == nil { + return UsageError{ + message: "missing handler", + } + } + + return nil +} + +// As is similar to Using except that it is required for Resume +// exclusively and contains properties required to support +// restoring a session from a previously terminated run. +type As struct { + Using + + // From is the path to the resumption file from which a prior + // traverse session is loaded. + From string + + // Strategy represent what type of resume is run. + Strategy enums.ResumeStrategy +} + +// Validate checks that the properties on Using and As are all valid. +func (a As) Validate() error { + if a.From == "" { + return UsageError{ + message: "missing restore from path", + } + } + + if a.Strategy == enums.ResumeStrategyUndefined { + return UsageError{ + message: "missing subscription", + } + } + + return a.Using.Validate() +} diff --git a/traverse-api.go b/traverse-api.go index 16b2cbc..b52933e 100644 --- a/traverse-api.go +++ b/traverse-api.go @@ -10,12 +10,11 @@ import ( // on the top of the code stack and is allowed to use anything, but nothing // else can depend on definitions here, except unit tests. +type As = pref.As type Client = core.Client type Node = core.Node - type Option = pref.Option type Options = pref.Options - type Subscription = enums.Subscription const ( @@ -32,10 +31,6 @@ const ( ResumeStrategyFastward = enums.ResumeStrategyFastward ) -type ( - As = core.As -) - var ( WithCancel = pref.WithCancel WithCPU = pref.WithCPU @@ -58,7 +53,7 @@ var ( WithSubscription = pref.WithSubscription ) -type Using = core.Using +type Using = pref.Using // sub package description: //