From 01966ff8f9b9e4393f6f5cab711e886db4df0e30 Mon Sep 17 00:00:00 2001 From: plastikfan Date: Mon, 30 Sep 2024 12:31:46 +0100 Subject: [PATCH] ref(tv): consolidate file systems into universal (#194) --- .vscode/settings.json | 3 + builders.go | 19 +- director-prime_test.go | 9 +- director-resume_test.go | 18 +- director.go | 62 ++- extent.go | 25 +- internal-traverse-defs.go | 9 +- internal/feat/hiber/hibernate_test.go | 14 +- internal/feat/hiber/with-filter_test.go | 9 +- .../feat/sampling/navigator-sample_test.go | 14 +- internal/filtering/filter-custom_test.go | 9 +- .../filtering/filter-extended-glob_test.go | 14 +- internal/filtering/filter-glob_test.go | 14 +- internal/filtering/filter-hybrid_test.go | 9 +- internal/filtering/filter-poly_test.go | 14 +- internal/filtering/filter-regex_test.go | 14 +- internal/kernel/navigator-agent.go | 2 +- internal/kernel/navigator-files.go | 2 +- .../navigator-folders-with-files_test.go | 9 +- internal/kernel/navigator-folders.go | 2 +- internal/kernel/navigator-simple_test.go | 9 +- internal/kernel/navigator-universal.go | 2 +- internal/laboratory/assert.go | 30 +- internal/laboratory/directory-tree-builder.go | 25 +- internal/laboratory/traverse-fs.go | 45 +++ lfs/file-systems.go | 360 +++++++++++++++++- lfs/lfs-defs.go | 96 +++++ lfs/{nfs-suite_test.go => lfs-suite_test.go} | 4 +- lfs/nfs-defs.go | 46 --- pref/using.go | 37 +- tapable/tapable_test.go | 3 +- traverse-api.go | 5 +- 32 files changed, 636 insertions(+), 297 deletions(-) create mode 100644 internal/laboratory/traverse-fs.go create mode 100644 lfs/lfs-defs.go rename lfs/{nfs-suite_test.go => lfs-suite_test.go} (97%) delete mode 100644 lfs/nfs-defs.go diff --git a/.vscode/settings.json b/.vscode/settings.json index d805f16..0aee9a6 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -4,6 +4,7 @@ "--fast" ], "cSpell.words": [ + "afero", "alloc", "Aphex", "argh", @@ -107,11 +108,13 @@ "toplevel", "tparallel", "trimprefix", + "tsys", "Turan", "typecheck", "unconvert", "unlambda", "unparam", + "usys", "vals", "varcheck", "verr", diff --git a/builders.go b/builders.go index faec1e9..4e595b3 100644 --- a/builders.go +++ b/builders.go @@ -16,10 +16,15 @@ type buildArtefacts struct { ext extent } +// Builders performs build orchestration via its buildAll method. Builders +// is instructed by the factories (via Configure) of which there are 2; one +// for Walk and one for Run. The Prime/Resume extents create the Builders +// instance. type Builders struct { - using *pref.Using - readerFS pref.ReadDirFileSystemBuilder - queryFS pref.QueryStatusFileSystemBuilder + using *pref.Using + universalFS pref.TraverseFileSystemBuilder + // readerFS pref.ReadDirFileSystemBuilder + // queryFS pref.QueryStatusFileSystemBuilder options optionsBuilder navigator kernel.NavigatorBuilder plugins pluginsBuilder @@ -29,10 +34,8 @@ type Builders struct { func (bs *Builders) buildAll() (*buildArtefacts, error) { // BUILD FILE SYSTEM & EXTENT // - reader := bs.readerFS.Build() ext := bs.extent.build( - reader, - bs.queryFS.Build(reader), + bs.universalFS.Build(bs.using.Root), ) // BUILD OPTIONS @@ -50,9 +53,7 @@ func (bs *Builders) buildAll() (*buildArtefacts, error) { // artefacts, navErr := bs.navigator.Build(o, &types.Resources{ FS: FileSystems{ - N: ext.navFS(), - Q: ext.queryFS(), - R: ext.resFS(), + T: ext.traverseFS(), }, Supervisor: measure.New(), Binder: binder, diff --git a/director-prime_test.go b/director-prime_test.go index 59ac3e6..888a231 100644 --- a/director-prime_test.go +++ b/director-prime_test.go @@ -12,6 +12,7 @@ import ( "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/cycle" "github.com/snivilised/traverse/enums" + lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/opts" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/locale" @@ -19,6 +20,10 @@ import ( ) var _ = Describe("Director(Prime)", Ordered, func() { + var ( + root string + ) + BeforeAll(func() { Expect(li18ngo.Use( func(o *li18ngo.UseOptions) { @@ -27,6 +32,8 @@ var _ = Describe("Director(Prime)", Ordered, func() { } }, )).To(Succeed()) + + root = lab.Repo("test") }) BeforeEach(func() { @@ -44,7 +51,7 @@ var _ = Describe("Director(Prime)", Ordered, func() { _, err := tv.Walk().Configure().Extent(tv.Prime( &tv.Using{ - Root: RootPath, + Root: root, Subscription: tv.SubscribeFiles, Handler: noOpHandler, }, diff --git a/director-resume_test.go b/director-resume_test.go index f27edc4..5cf1d20 100644 --- a/director-resume_test.go +++ b/director-resume_test.go @@ -2,7 +2,6 @@ package tv_test import ( "context" - "io/fs" "os" "sync" "testing/fstest" @@ -16,14 +15,16 @@ import ( "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/cycle" "github.com/snivilised/traverse/enums" + lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/locale" "github.com/snivilised/traverse/pref" ) var _ = Describe("Director(Resume)", Ordered, func() { var ( - emptyFS fstest.MapFS + emptyFS *lab.TestTraverseFS restore pref.Option ) @@ -33,9 +34,11 @@ var _ = Describe("Director(Resume)", Ordered, func() { return nil } - emptyFS = fstest.MapFS{ - ".": &fstest.MapFile{ - Mode: os.ModeDir, + emptyFS = &lab.TestTraverseFS{ + MapFS: fstest.MapFS{ + ".": &fstest.MapFile{ + Mode: os.ModeDir, + }, }, } @@ -67,10 +70,7 @@ var _ = Describe("Director(Resume)", Ordered, func() { Using: tv.Using{ Subscription: tv.SubscribeFiles, Handler: noOpHandler, - GetReadDirFS: func() fs.ReadDirFS { - return emptyFS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return emptyFS }, }, diff --git a/director.go b/director.go index 49e0d9f..df836db 100644 --- a/director.go +++ b/director.go @@ -1,8 +1,6 @@ package tv import ( - "io/fs" - "github.com/snivilised/traverse/internal/feat/filter" "github.com/snivilised/traverse/internal/feat/hiber" "github.com/snivilised/traverse/internal/feat/nanny" @@ -12,10 +10,19 @@ import ( "github.com/snivilised/traverse/internal/opts" "github.com/snivilised/traverse/internal/third/lo" "github.com/snivilised/traverse/internal/types" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) -type ifActive func(o *pref.Options, using *pref.Using, mediator types.Mediator) types.Plugin +const ( + noOverwrite = true +) + +type ( + ifActive func(o *pref.Options, + using *pref.Using, mediator types.Mediator, + ) types.Plugin +) // features interrogates options and invokes requests on behalf of the user // to activate features according to option selections. other plugins will @@ -69,33 +76,20 @@ func features(o *pref.Options, using *pref.Using, mediator types.Mediator, // Prime extent requests that the navigator performs a full // traversal from the root path specified. func Prime(using *pref.Using, settings ...pref.Option) *Builders { - // TODO: we need to create an aux file system, which is bound - // to a pre-defined location, that will be called upon if - // the navigation session is terminated either by a ctrl-c or - // by a panic. - // return &Builders{ using: using, - readerFS: pref.CreateReadDirFS(func() fs.ReadDirFS { - if using.GetReadDirFS != nil { - return using.GetReadDirFS() + universalFS: pref.CreateTraverseFS(func(root string) lfs.TraverseFS { + if using.GetTraverseFS != nil { + return using.GetTraverseFS(root) } - return NewLocalFS(using.Root) + return lfs.NewTraverseFS(root, noOverwrite) }), - queryFS: pref.CreateQueryStatusFS(func(qsys fs.FS) fs.StatFS { - if using.GetQueryStatusFS != nil { - return using.GetQueryStatusFS(qsys) - } - - return NewQueryStatusFS(qsys) - }), - extent: extension(func(rsys fs.ReadDirFS, qsys fs.StatFS) extent { + extent: extension(func(tsys lfs.TraverseFS) extent { return &primeExtent{ baseExtent: baseExtent{ fileSys: fileSystems{ - nas: rsys, - qus: qsys, + tsys: tsys, }, }, u: using, @@ -130,32 +124,20 @@ func Prime(using *pref.Using, settings ...pref.Option) *Builders { // as a result of it being terminated prematurely via a ctrl-c // interrupt. func Resume(was *Was, settings ...pref.Option) *Builders { - // TODO: the navigation file system, baseExtent.sys, will be set for - // resume, only once the resume file has been loaded, as - // its only at this point, we know where the original root - // path was. - // return &Builders{ using: &was.Using, - readerFS: pref.CreateReadDirFS(func() fs.ReadDirFS { - if was.Using.GetReadDirFS != nil { - return was.Using.GetReadDirFS() - } - return NewLocalFS(was.Root) - }), - queryFS: pref.CreateQueryStatusFS(func(fsys fs.FS) fs.StatFS { - if was.Using.GetQueryStatusFS != nil { - return was.Using.GetQueryStatusFS(fsys) + universalFS: pref.CreateTraverseFS(func(root string) lfs.TraverseFS { + if was.Using.GetTraverseFS != nil { + return was.Using.GetTraverseFS(root) } - return NewQueryStatusFS(fsys) + return lfs.NewTraverseFS(root, noOverwrite) }), - extent: extension(func(rsys fs.ReadDirFS, qsys fs.StatFS) extent { + extent: extension(func(tsys lfs.TraverseFS) extent { return &resumeExtent{ baseExtent: baseExtent{ fileSys: fileSystems{ - nas: rsys, - qus: qsys, + tsys: tsys, }, }, w: was, diff --git a/extent.go b/extent.go index fe68ae3..12ba91f 100644 --- a/extent.go +++ b/extent.go @@ -1,12 +1,11 @@ package tv import ( - "io/fs" - "github.com/snivilised/traverse/internal/feat/resume" "github.com/snivilised/traverse/internal/kernel" "github.com/snivilised/traverse/internal/opts" "github.com/snivilised/traverse/internal/types" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) @@ -15,32 +14,20 @@ type extent interface { was() *pref.Was plugin(*kernel.Artefacts) types.Plugin options(...pref.Option) (*pref.Options, *opts.Binder, error) - navFS() fs.ReadDirFS - queryFS() fs.StatFS - resFS() fs.FS + traverseFS() lfs.TraverseFS complete() bool } type fileSystems struct { - nas fs.ReadDirFS - qus fs.StatFS - res fs.FS + tsys lfs.TraverseFS } type baseExtent struct { fileSys fileSystems } -func (ex *baseExtent) navFS() fs.ReadDirFS { - return ex.fileSys.nas -} - -func (ex *baseExtent) queryFS() fs.StatFS { - return ex.fileSys.qus -} - -func (ex *baseExtent) resFS() fs.FS { - return ex.fileSys.nas +func (ex *baseExtent) traverseFS() lfs.TraverseFS { + return ex.fileSys.tsys } type primeExtent struct { @@ -95,7 +82,7 @@ func (ex *resumeExtent) plugin(artefacts *kernel.Artefacts) types.Plugin { } func (ex *resumeExtent) options(settings ...pref.Option) (*pref.Options, *opts.Binder, error) { - loaded, binder, err := resume.Load(ex.fileSys.res, ex.w.From, settings...) + loaded, binder, err := resume.Load(ex.fileSys.tsys, ex.w.From, settings...) ex.loaded = loaded // TODO: get the resume point from the resume persistence file diff --git a/internal-traverse-defs.go b/internal-traverse-defs.go index 2e730ae..4d62e17 100644 --- a/internal-traverse-defs.go +++ b/internal-traverse-defs.go @@ -5,6 +5,7 @@ import ( "github.com/snivilised/traverse/internal/opts" "github.com/snivilised/traverse/internal/types" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) @@ -56,13 +57,13 @@ func (fn filesystem) build(path string) fs.FS { } type extentBuilder interface { - build(rsys fs.ReadDirFS, qsys fs.StatFS) extent + build(tsys lfs.TraverseFS) extent } -type extension func(rsys fs.ReadDirFS, qsys fs.StatFS) extent +type extension func(tsys lfs.TraverseFS) extent -func (fn extension) build(rsys fs.ReadDirFS, qsys fs.StatFS) extent { - return fn(rsys, qsys) +func (fn extension) build(tsys lfs.TraverseFS) extent { + return fn(tsys) } // We need an entity that manages the decoration of the client handler. The diff --git a/internal/feat/hiber/hibernate_test.go b/internal/feat/hiber/hibernate_test.go index 566bb92..f4960ff 100644 --- a/internal/feat/hiber/hibernate_test.go +++ b/internal/feat/hiber/hibernate_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -17,11 +16,12 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" ) var _ = Describe("feature", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -57,10 +57,7 @@ var _ = Describe("feature", Ordered, func() { ) return nil }, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, @@ -154,10 +151,7 @@ var _ = Describe("feature", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: client, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/feat/hiber/with-filter_test.go b/internal/feat/hiber/with-filter_test.go index ff07d21..982b1ec 100644 --- a/internal/feat/hiber/with-filter_test.go +++ b/internal/feat/hiber/with-filter_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -16,12 +15,13 @@ import ( "github.com/snivilised/traverse/enums" lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) var _ = Describe("feature", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -50,10 +50,7 @@ var _ = Describe("feature", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: entry.Callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/feat/sampling/navigator-sample_test.go b/internal/feat/sampling/navigator-sample_test.go index 8952beb..ca8ae38 100644 --- a/internal/feat/sampling/navigator-sample_test.go +++ b/internal/feat/sampling/navigator-sample_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -15,13 +14,14 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/locale" "github.com/snivilised/traverse/pref" ) var _ = Describe("feature", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -56,10 +56,7 @@ var _ = Describe("feature", Ordered, func() { ) return nil }, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, @@ -127,10 +124,7 @@ var _ = Describe("feature", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/filtering/filter-custom_test.go b/internal/filtering/filter-custom_test.go index d820523..76bc335 100644 --- a/internal/filtering/filter-custom_test.go +++ b/internal/filtering/filter-custom_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -15,12 +14,13 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) var _ = Describe("NavigatorFilterCustom", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -74,10 +74,7 @@ var _ = Describe("NavigatorFilterCustom", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/filtering/filter-extended-glob_test.go b/internal/filtering/filter-extended-glob_test.go index 342f7e1..64b92d3 100644 --- a/internal/filtering/filter-extended-glob_test.go +++ b/internal/filtering/filter-extended-glob_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -15,12 +14,13 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) var _ = Describe("filtering", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -63,10 +63,7 @@ var _ = Describe("filtering", Ordered, func() { ) return nil }, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, @@ -138,10 +135,7 @@ var _ = Describe("filtering", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/filtering/filter-glob_test.go b/internal/filtering/filter-glob_test.go index c3caa62..a8b6af8 100644 --- a/internal/filtering/filter-glob_test.go +++ b/internal/filtering/filter-glob_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -16,12 +15,13 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) var _ = Describe("NavigatorFilterGlob", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -64,10 +64,7 @@ var _ = Describe("NavigatorFilterGlob", Ordered, func() { ) return nil }, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, @@ -139,10 +136,7 @@ var _ = Describe("NavigatorFilterGlob", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/filtering/filter-hybrid_test.go b/internal/filtering/filter-hybrid_test.go index 25cbec6..7c8b7da 100644 --- a/internal/filtering/filter-hybrid_test.go +++ b/internal/filtering/filter-hybrid_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -15,13 +14,14 @@ import ( "github.com/snivilised/traverse/enums" lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/locale" "github.com/snivilised/traverse/pref" ) var _ = Describe("feature", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -85,10 +85,7 @@ var _ = Describe("feature", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/filtering/filter-poly_test.go b/internal/filtering/filter-poly_test.go index 4c90a35..19d9d70 100644 --- a/internal/filtering/filter-poly_test.go +++ b/internal/filtering/filter-poly_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -16,12 +15,13 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) var _ = Describe("feature", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -75,10 +75,7 @@ var _ = Describe("feature", Ordered, func() { ) return nil }, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, @@ -149,10 +146,7 @@ var _ = Describe("feature", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/filtering/filter-regex_test.go b/internal/filtering/filter-regex_test.go index 289e8dc..c06e7b5 100644 --- a/internal/filtering/filter-regex_test.go +++ b/internal/filtering/filter-regex_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -15,12 +14,13 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/pref" ) var _ = Describe("feature", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -64,10 +64,7 @@ var _ = Describe("feature", Ordered, func() { ) return nil }, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, @@ -139,10 +136,7 @@ var _ = Describe("feature", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/kernel/navigator-agent.go b/internal/kernel/navigator-agent.go index 1796070..8ae6e0e 100644 --- a/internal/kernel/navigator-agent.go +++ b/internal/kernel/navigator-agent.go @@ -47,7 +47,7 @@ func (n *navigatorAgent) top(ctx context.Context, ns *navigationStatic, ) (*types.KernelResult, error) { info, ie := n.ao.hooks.QueryStatus.Invoke()( - ns.mediator.resources.FS.Q, ns.root, + ns.mediator.resources.FS.T, ns.root, ) err := lo.TernaryF(ie != nil, diff --git a/internal/kernel/navigator-files.go b/internal/kernel/navigator-files.go index 4d3a852..c6db57d 100644 --- a/internal/kernel/navigator-files.go +++ b/internal/kernel/navigator-files.go @@ -69,7 +69,7 @@ func (n *navigatorFiles) inspect(ns *navigationStatic, ) if vapour.present.IsFolder() { - vapour.cargo, err = read(ns.mediator.resources.FS.N, + vapour.cargo, err = read(ns.mediator.resources.FS.T, n.ro, current.Path, ) diff --git a/internal/kernel/navigator-folders-with-files_test.go b/internal/kernel/navigator-folders-with-files_test.go index 6696db8..4298940 100644 --- a/internal/kernel/navigator-folders-with-files_test.go +++ b/internal/kernel/navigator-folders-with-files_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -14,12 +13,13 @@ import ( "github.com/snivilised/traverse/enums" lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/locale" ) var _ = Describe("NavigatorFoldersWithFiles", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -62,10 +62,7 @@ var _ = Describe("NavigatorFoldersWithFiles", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: once, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/kernel/navigator-folders.go b/internal/kernel/navigator-folders.go index 1135616..93d34ed 100644 --- a/internal/kernel/navigator-folders.go +++ b/internal/kernel/navigator-folders.go @@ -63,7 +63,7 @@ func (n *navigatorFolders) inspect(ns *navigationStatic, // interested in folders and therefore forced to use // enums.DirectoryEntryOrderFoldersFirst instead. // - vapour.cargo, err = read(ns.mediator.resources.FS.N, + vapour.cargo, err = read(ns.mediator.resources.FS.T, n.ro, current.Path, ) diff --git a/internal/kernel/navigator-simple_test.go b/internal/kernel/navigator-simple_test.go index 0166ba2..f2e1c33 100644 --- a/internal/kernel/navigator-simple_test.go +++ b/internal/kernel/navigator-simple_test.go @@ -4,7 +4,6 @@ import ( "fmt" "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -15,12 +14,13 @@ import ( lab "github.com/snivilised/traverse/internal/laboratory" "github.com/snivilised/traverse/internal/services" "github.com/snivilised/traverse/internal/third/lo" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/locale" ) var _ = Describe("NavigatorUniversal", Ordered, func() { var ( - FS fstest.MapFS + FS *lab.TestTraverseFS root string ) @@ -72,10 +72,7 @@ var _ = Describe("NavigatorUniversal", Ordered, func() { Root: path, Subscription: entry.Subscription, Handler: callback, - GetReadDirFS: func() fs.ReadDirFS { - return FS - }, - GetQueryStatusFS: func(_ fs.FS) fs.StatFS { + GetTraverseFS: func(_ string) lfs.TraverseFS { return FS }, }, diff --git a/internal/kernel/navigator-universal.go b/internal/kernel/navigator-universal.go index 23d216e..fee713d 100644 --- a/internal/kernel/navigator-universal.go +++ b/internal/kernel/navigator-universal.go @@ -61,7 +61,7 @@ func (n *navigatorUniversal) inspect(ns *navigationStatic, ) if current.IsFolder() { - vapour.cargo, err = read(ns.mediator.resources.FS.N, + vapour.cargo, err = read(ns.mediator.resources.FS.T, n.ro, current.Path, ) diff --git a/internal/laboratory/assert.go b/internal/laboratory/assert.go index 3580e7a..d063035 100644 --- a/internal/laboratory/assert.go +++ b/internal/laboratory/assert.go @@ -13,7 +13,7 @@ import ( ) type TestOptions struct { - FS fstest.MapFS + FS *TestTraverseFS Recording RecordingMap Path string Result core.TraverseResult @@ -34,18 +34,14 @@ func AssertNavigation(entry *NaviTE, to *TestOptions) { _ = to.Result.Session().StartedAt() _ = to.Result.Session().Elapsed() - if entry.Visit { - _ = fs.WalkDir(to.FS, to.Path, func(path string, de fs.DirEntry, _ error) error { - if strings.HasSuffix(path, ".DS_Store") { - return nil - } - - if subscribes(entry.Subscription, de) { - visited = append(visited, path) + if entry.Visit && to.FS != nil { + for path, file := range to.FS.MapFS { + if strings.HasPrefix(path, to.Path) { + if subscribes(entry.Subscription, file) { + visited = append(visited, path) + } } - - return nil - }) + } every := lo.EveryBy(visited, lo.Ternary(to.Every != nil, to.Every, func(p string) bool { @@ -61,7 +57,7 @@ func AssertNavigation(entry *NaviTE, to *TestOptions) { }), ) - Expect(every).To(BeTrue()) + Expect(every).To(BeTrue(), "Not all expected items were invoked") } for name, expected := range entry.ExpectedNoOf.Children { @@ -105,11 +101,11 @@ func assertMetrics(entry *NaviTE, to *TestOptions) { ) } -func subscribes(subscription enums.Subscription, de fs.DirEntry) bool { +func subscribes(subscription enums.Subscription, mapFile *fstest.MapFile) bool { isUniversalSubscription := (subscription == enums.SubscribeUniversal) - files := de != nil && (subscription == enums.SubscribeFiles) && (!de.IsDir()) - folders := de != nil && ((subscription == enums.SubscribeFolders) || - subscription == enums.SubscribeFoldersWithFiles) && (de.IsDir()) + files := mapFile != nil && (subscription == enums.SubscribeFiles) && ((mapFile.Mode | fs.ModeDir) == 0) + folders := mapFile != nil && ((subscription == enums.SubscribeFolders) || + subscription == enums.SubscribeFoldersWithFiles) && ((mapFile.Mode | fs.ModeDir) > 0) return isUniversalSubscription || files || folders } diff --git a/internal/laboratory/directory-tree-builder.go b/internal/laboratory/directory-tree-builder.go index 039de3a..8950070 100644 --- a/internal/laboratory/directory-tree-builder.go +++ b/internal/laboratory/directory-tree-builder.go @@ -21,15 +21,17 @@ const ( doWrite = true ) -func Musico(verbose bool, portions ...string) (msys fstest.MapFS, root string) { - msys = fstest.MapFS{ - ".": &fstest.MapFile{ - Mode: os.ModeDir, +func Musico(verbose bool, portions ...string) (tsys *TestTraverseFS, root string) { + tsys = &TestTraverseFS{ + fstest.MapFS{ + ".": &fstest.MapFile{ + Mode: os.ModeDir, + }, }, } - return msys, Provision( - NewMemWriteProvider(msys, os.ReadFile, portions...), + return tsys, Provision( + NewMemWriteProvider(tsys, os.ReadFile, portions...), verbose, portions..., ) @@ -90,8 +92,7 @@ func TrimRoot(root string) string { return re.ReplaceAllString(root, "") } -// NewMemWriteProvider -func NewMemWriteProvider(store fstest.MapFS, +func NewMemWriteProvider(store *TestTraverseFS, indexReader readFile, portions ...string, ) *IOProvider { @@ -124,12 +125,12 @@ func NewMemWriteProvider(store fstest.MapFS, if filter(name) { trimmed := TrimRoot(name) - store[trimmed] = &fstest.MapFile{ + store.MapFS[trimmed] = &fstest.MapFile{ Data: data, Mode: mode, } show(trimmed, func(path string) bool { - entry, ok := store[path] + entry, ok := store.MapFS[path] return ok && !entry.Mode.IsDir() }) } @@ -145,11 +146,11 @@ func NewMemWriteProvider(store fstest.MapFS, if isRoot || filter(path) { trimmed := TrimRoot(path) - store[trimmed] = &fstest.MapFile{ + store.MapFS[trimmed] = &fstest.MapFile{ Mode: mode | os.ModeDir, } show(trimmed, func(path string) bool { - entry, ok := store[path] + entry, ok := store.MapFS[path] return ok && entry.Mode.IsDir() }) } diff --git a/internal/laboratory/traverse-fs.go b/internal/laboratory/traverse-fs.go new file mode 100644 index 0000000..cf57007 --- /dev/null +++ b/internal/laboratory/traverse-fs.go @@ -0,0 +1,45 @@ +package lab + +import ( + "os" + "testing/fstest" +) + +type TestTraverseFS struct { + fstest.MapFS +} + +func (f *TestTraverseFS) FileExists(path string) bool { + if mapFile, found := f.MapFS[path]; found && !mapFile.Mode.IsDir() { + return true + } + + return false +} + +func (f *TestTraverseFS) DirectoryExists(path string) bool { + if mapFile, found := f.MapFS[path]; found && mapFile.Mode.IsDir() { + return true + } + + return false +} + +func (f *TestTraverseFS) Create(name string) (*os.File, error) { + _ = name + panic("NOT-IMPL: TestTraverseFS.Create") +} + +func (f *TestTraverseFS) MkDirAll(path string, perm os.FileMode) error { + _ = path + _ = perm + panic("NOT-IMPL: TestTraverseFS.MkDirAll") +} + +func (f *TestTraverseFS) WriteFile(name string, data []byte, perm os.FileMode) error { + _ = name + _ = data + _ = perm + + panic("NOT-IMPL: TestTraverseFS.WriteFile") +} diff --git a/lfs/file-systems.go b/lfs/file-systems.go index b885cf7..939b078 100644 --- a/lfs/file-systems.go +++ b/lfs/file-systems.go @@ -5,41 +5,375 @@ import ( "os" ) -type localFS struct { +// 🔥 An important note about using standard golang file systems (io.fs/fs.FS) +// as opposed to using the native os calls directly (os.XXX). +// Note that (io.fs/fs.FS) represents a virtual file system where as os.XXX +// represent operations on the local file system. Working with either of +// these is fundamentally different to working with the other; bear this in +// mind to avoid confusion. +// +// virtual file system +// =================== +// +// The client is expected to create a file system rooted at a particular path. +// This path must be absolute. Any function call on the resulting file system +// that requires a path must be relative to this root and therefore must not +// begin or end with a slash. +// +// When composing paths to use with a file system, one might think that using +// filepath.Separator and building paths with filepath.Join is the most +// prudent thing to do to ensure correct functioning on different platforms. When +// it comes to file systems, this is most certainly not the case. The paths +// are virtual and they are mapped into an underlying file system, which typically +// is the local file system. This means that paths used only need to use '/'. And +// the silly thing is, characters like ':', or '\' for windows should not be +// treated as separators by the underlying file system. So really using +// filepath.Separator with a virtual file system is not valid. +// + +// 🎯 openFS +type openFS struct { fsys fs.FS } -// NewLocalFS creates a native file system. -func NewLocalFS(path string) fs.ReadDirFS { - return &localFS{ - fsys: os.DirFS(path), +func (f *openFS) Open(path string) (fs.File, error) { + return f.fsys.Open(path) +} + +// 🎯 statFS +type statFS struct { + openFS +} + +func NewStatFS(path string) fs.StatFS { + ents := compose(path) + return &ents.stat +} + +func (f *statFS) Stat(path string) (fs.FileInfo, error) { + return fs.Stat(f.fsys, path) +} + +// 🧩 ---> file system query + +// 🎯 readDirFS +type readDirFS struct { + openFS +} + +// NewReadDirFS creates a native file system. +func NewReadDirFS(path string) fs.ReadDirFS { + ents := compose(path) + + return &readDirFS{ + openFS: ents.open, } } -func (n *localFS) Open(path string) (fs.File, error) { +// Open opens the named file. +// +// When Open returns an error, it should be of type *PathError +// with the Op field set to "open", the Path field set to name, +// and the Err field describing the problem. +// +// Open should reject attempts to open names that do not satisfy +// ValidPath(name), returning a *PathError with Err set to +// ErrInvalid or ErrNotExist. +func (n *readDirFS) Open(path string) (fs.File, error) { return n.fsys.Open(path) } -func (n *localFS) ReadDir(name string) ([]fs.DirEntry, error) { +// ReadDir reads the named directory +// and returns a list of directory entries sorted by filename. +// +// If fs implements [ReadDirFS], ReadDir calls fs.ReadDir. +// Otherwise ReadDir calls fs.Open and uses ReadDir and Close +// on the returned file. +func (n *readDirFS) ReadDir(name string) ([]fs.DirEntry, error) { return fs.ReadDir(n.fsys, name) } +// 🎯 queryStatusFS type queryStatusFS struct { - fsys fs.FS + statFS + readDirFS +} + +func NewQueryStatusFS(path string) fs.StatFS { + ents := compose(path) + + return &queryStatusFS{ + readDirFS: ents.read, + } } -// NewQueryStatusFS defines a file system that has a Stat +// QueryStatusFromFS defines a file system that has a Stat // method to determine the existence of a path. -func NewQueryStatusFS(fsys fs.FS) fs.StatFS { +func QueryStatusFromFS(fsys fs.FS) fs.StatFS { return &queryStatusFS{ - fsys: fsys, + readDirFS: readDirFS{ + openFS: openFS{ + fsys: fsys, + }, + }, } } func (q *queryStatusFS) Open(name string) (fs.File, error) { - return q.fsys.Open(name) + return q.statFS.fsys.Open(name) } +// Stat returns a [FileInfo] describing the named file. +// If there is an error, it will be of type [*PathError]. func (q *queryStatusFS) Stat(name string) (fs.FileInfo, error) { - return os.Stat(name) + return q.statFS.Stat(name) +} + +// 🎯 existsInFS +type existsInFS struct { + queryStatusFS +} + +// ExistsInFS +func NewExistsInFS(path string) ExistsInFS { + ents := compose(path) + + return &ents.exists +} + +// FileExists does file exist at the path specified +func (f *existsInFS) FileExists(path string) bool { + info, err := f.Stat(path) + if err != nil { + return false + } + + if info.IsDir() { + return false + } + + return true +} + +// DirectoryExists does directory exist at the path specified +func (f *existsInFS) DirectoryExists(path string) bool { + info, err := f.Stat(path) + if err != nil { + return false + } + + if !info.IsDir() { + return false + } + + return true +} + +// 🎯 readFileFS +type readFileFS struct { + queryStatusFS +} + +func NewReadFileFS(path string) ReadFileFS { + ents := compose(path) + + return &readFileFS{ + queryStatusFS: ents.query, + } +} + +// ReadFile reads the named file from the file system fs and returns its contents. +// A successful call returns a nil error, not [io.EOF]. +// (Because ReadFile reads the whole file, the expected EOF +// from the final Read is not treated as an error to be reported.) +// +// If fs implements [ReadFileFS], ReadFile calls fs.ReadFile. +// Otherwise ReadFile calls fs.Open and uses Read and Close +// on the returned [File]. +func (f *readFileFS) ReadFile(path string) ([]byte, error) { + return fs.ReadFile(f.queryStatusFS.statFS.fsys, path) +} + +// 🧩 ---> file system mutation + +// 🎯 baseWriterFS +type baseWriterFS struct { + openFS + overwrite bool +} + +// 🎯 MkDirAllFS +type mkDirAllFS struct { + existsInFS +} + +// NewMkDirAllFS +func NewMkDirAllFS() MkDirAllFS { + return &mkDirAllFS{} +} + +// MkdirAll creates a directory named path, +// along with any necessary parents, and returns nil, +// or else returns an error. +// The permission bits perm (before umask) are used for all +// directories that MkdirAll creates. +// If path is already a directory, MkdirAll does nothing +// and returns nil. +func (*mkDirAllFS) MkDirAll(path string, perm os.FileMode) error { + return os.MkdirAll(path, perm) // !!! WRONG use the correct fs +} + +// 🎯 writeFileFS +type writeFileFS struct { + baseWriterFS +} + +func NewWriteFileFS(path string, overwrite bool) WriteFileFS { + ents := compose(path) + + return &writeFileFS{ + baseWriterFS: baseWriterFS{ + openFS: ents.open, + overwrite: overwrite, + }, + } +} + +// Create creates or truncates the named file. If the file already exists, +// it is truncated. If the file does not exist, it is created with mode 0o666 +// (before umask). If successful, methods on the returned File can +// be used for I/O; the associated file descriptor has mode O_RDWR. +// If there is an error, it will be of type *PathError. +// +// We need to maintain conformity with apis in the standard library. Ideally, +// this Create method would have the overwrite bool passed in as an argument, +// but doing so would break standard lib compatibility. Instead, the underlying +// implementation has to decide wether to Create on an override basis itself. +// The disadvantage of this approach is that the client can not decide on +// the fly wether a call to Create is on a override basis or not. This decision +// has to be made at the point of creating the file system. This is less +// flexible and just results in friction, but this is out of our power. +func (f *writeFileFS) Create(name string) (*os.File, error) { + return os.Create(name) +} + +// WriteFile writes data to the named file, creating it if necessary. +// If the file does not exist, WriteFile creates it with permissions perm (before umask); +// otherwise WriteFile truncates it before writing, without changing permissions. +// Since WriteFile requires multiple system calls to complete, a failure mid-operation +// can leave the file in a partially written state. +func (f *writeFileFS) WriteFile(name string, data []byte, perm os.FileMode) error { + return os.WriteFile(name, data, perm) +} + +// 🧩 ---> file system aggregators + +// 🎯 readerFS +type readerFS struct { + readDirFS + readFileFS + existsInFS + statFS +} + +// NewReaderFS +func NewReaderFS(path string) ReaderFS { + ents := compose(path) + + return &readerFS{ + readDirFS: ents.read, + readFileFS: readFileFS{ + queryStatusFS: ents.query, + }, + existsInFS: ents.exists, + statFS: ents.stat, + } +} + +// 🎯 writerFS +type writerFS struct { + mkDirAllFS + writeFileFS +} + +func NewWriterFS(path string, overwrite bool) WriterFS { + ents := compose(path) + + return &writerFS{ + writeFileFS: writeFileFS{ + baseWriterFS: baseWriterFS{ + openFS: ents.open, + overwrite: overwrite, + }, + }, + } +} + +// 🎯 traverseFS +type traverseFS struct { + readerFS + writerFS +} + +func NewTraverseFS(path string, overwrite bool) TraverseFS { + ents := compose(path) + + return &traverseFS{ + readerFS: readerFS{ + readDirFS: ents.read, + readFileFS: readFileFS{ + queryStatusFS: ents.query, + }, + existsInFS: ents.exists, + statFS: ents.stat, + }, + writerFS: writerFS{ + mkDirAllFS: mkDirAllFS{ + existsInFS: ents.exists, + }, + writeFileFS: writeFileFS{ + baseWriterFS: baseWriterFS{ + openFS: ents.open, + overwrite: overwrite, + }, + }, + }, + } +} + +type entities struct { + open openFS + read readDirFS + stat statFS + query queryStatusFS + exists existsInFS +} + +func compose(root string) *entities { + open := openFS{ + fsys: os.DirFS(root), + } + read := readDirFS{ + openFS: open, + } + stat := statFS{ + openFS: open, + } + query := queryStatusFS{ + statFS: statFS{ + openFS: open, + }, + readDirFS: read, + } + exists := existsInFS{ + queryStatusFS: query, + } + + return &entities{ + open: open, + read: read, + stat: stat, + query: query, + exists: exists, + } } diff --git a/lfs/lfs-defs.go b/lfs/lfs-defs.go new file mode 100644 index 0000000..1cc2b19 --- /dev/null +++ b/lfs/lfs-defs.go @@ -0,0 +1,96 @@ +package lfs + +import ( + "io/fs" + "os" +) + +// 📦 pkg: lfs - contains local file system abstractions for navigation. +// Since there are no standard write-able file system interfaces, +// we need to define proprietary ones here in this package. +// This is a low level package that should not use anything else in +// traverse. + +type ( + // FileSystems contains the logical file systems required + // for navigation. + FileSystems struct { + // T is the file system that contains just the functionality required + // for traversal. It can also represent other file systems including afero. + T TraverseFS + } + + // ExistsInFS contains methods that check the existence of file system items. + ExistsInFS interface { + // FileExists does file exist at the path specified + FileExists(path string) bool + + // DirectoryExists does directory exist at the path specified + DirectoryExists(path string) bool + } + + // ReadFileFS file system non streaming reader + ReadFileFS interface { + fs.FS + // Read reads file at path, from file system specified + ReadFile(path string) ([]byte, error) + } + + // ReaderFS + ReaderFS interface { + fs.StatFS + fs.ReadDirFS + ExistsInFS + ReadFileFS + } + + // MkDirAllFS is a file system with a MkDirAll method. + MkDirAllFS interface { + ExistsInFS + MkDirAll(path string, perm os.FileMode) error + } + + // CopyFS + CopyFS interface{} + + // MoveFS + MoveFS interface{} + + // RemoveFS + RemoveFS interface{} + + // RenameFS + RenameFS interface{} + + // WriteFileFS file system non streaming writer + WriteFileFS interface { + // Create creates or truncates the named file. + Create(name string) (*os.File, error) + // Write writes file at path, to file system specified + WriteFile(name string, data []byte, perm os.FileMode) error + } + + // WriterFS + WriterFS interface { + CopyFS + MoveFS + ExistsInFS + RemoveFS + RenameFS + WriteFileFS + } + + // TraverseFS non streaming file system with reader and some + // writer capabilities + TraverseFS interface { + MkDirAllFS + ReaderFS + WriteFileFS + } + + // UniversalFS the file system that can do it all + UniversalFS interface { + ReaderFS + WriterFS + } +) diff --git a/lfs/nfs-suite_test.go b/lfs/lfs-suite_test.go similarity index 97% rename from lfs/nfs-suite_test.go rename to lfs/lfs-suite_test.go index ca07a3a..da5fdd9 100644 --- a/lfs/nfs-suite_test.go +++ b/lfs/lfs-suite_test.go @@ -12,9 +12,9 @@ import ( . "github.com/onsi/gomega" //nolint:revive // ok ) -func TestNfs(t *testing.T) { +func TestLfs(t *testing.T) { RegisterFailHandler(Fail) - RunSpecs(t, "Nfs Suite") + RunSpecs(t, "Lfs Suite") } type ( diff --git a/lfs/nfs-defs.go b/lfs/nfs-defs.go deleted file mode 100644 index 3d19499..0000000 --- a/lfs/nfs-defs.go +++ /dev/null @@ -1,46 +0,0 @@ -package lfs - -import ( - "io/fs" - "os" -) - -// 📦 pkg: lfs - contains local file system abstractions for navigation. -// Since there are no standard write-able file system interfaces, -// we need to define proprietary ones here in this package. -// This is a low level package that should not use anything else in -// traverse. - -type ( - // FileSystems contains the logical file systems required - // for navigation. - FileSystems struct { - // N represents the read only navigation file system. Uses - // of the shelf interface as defined by the standard library. - N fs.ReadDirFS - - // Q represents the file system instance that can perform a query - // status via an Lstat call. - Q fs.StatFS - - // R represents the resume/save file system that requires - // write access and whose path should be outside of the path - // represented by N, the navigation file system. - R fs.FS - } - - // ExistsInFS contains methods that check the existence of file system items. - ExistsInFS interface { - // FileExists does file exist at the path specified - FileExists(path string) bool - - // DirectoryExists does directory exist at the path specified - DirectoryExists(path string) bool - } - - // MkDirAllFS is a file system with a MkDirAll method. - MkDirAllFS interface { - ExistsInFS - MkDirAll(path string, perm os.FileMode) error - } -) diff --git a/pref/using.go b/pref/using.go index 56346d9..f972151 100644 --- a/pref/using.go +++ b/pref/using.go @@ -1,10 +1,9 @@ package pref import ( - "io/fs" - "github.com/snivilised/traverse/core" "github.com/snivilised/traverse/enums" + "github.com/snivilised/traverse/lfs" "github.com/snivilised/traverse/locale" ) @@ -32,15 +31,9 @@ type Using struct { // property. O *Options - // GetReadDirFS is optional and enables the client to specify how the + // GetTraverseFS is optional and enables the client to specify how the // file system for a path is created - GetReadDirFS CreateReadDirFS - - // GetQueryStatusFS is optional and enables the client to specify how the - // file system for a path is created. When specified can probably use - // the same instance used to create the ReadDi fs, that is because - // fstest.MapFS implements the required method Stat. - GetQueryStatusFS CreateQueryStatusFS + GetTraverseFS CreateTraverseFS } // Validate checks that the properties on Using are all valid. @@ -67,7 +60,7 @@ type Was struct { } // Validate checks that the properties on Using and Was are all valid. -func (a Was) Validate() error { //nolint:gocritic // heavy, so what, low frequency +func (a Was) Validate() error { if a.From == "" { return locale.ErrUsageMissingRestorePath } @@ -92,25 +85,13 @@ func validate(using *Using) error { } type ( - ReadDirFileSystemBuilder interface { - Build() fs.ReadDirFS - } - - CreateReadDirFS func() fs.ReadDirFS -) - -func (fn CreateReadDirFS) Build() fs.ReadDirFS { - return fn() -} - -type ( - QueryStatusFileSystemBuilder interface { - Build(fsys fs.FS) fs.StatFS + TraverseFileSystemBuilder interface { + Build(root string) lfs.TraverseFS } - CreateQueryStatusFS func(fsys fs.FS) fs.StatFS + CreateTraverseFS func(root string) lfs.TraverseFS ) -func (fn CreateQueryStatusFS) Build(fsys fs.FS) fs.StatFS { - return fn(fsys) +func (fn CreateTraverseFS) Build(root string) lfs.TraverseFS { + return fn(root) } diff --git a/tapable/tapable_test.go b/tapable/tapable_test.go index e3565ea..c3bfcfe 100644 --- a/tapable/tapable_test.go +++ b/tapable/tapable_test.go @@ -3,7 +3,6 @@ package tapable_test import ( "io/fs" "path/filepath" - "testing/fstest" . "github.com/onsi/ginkgo/v2" //nolint:revive // ok . "github.com/onsi/gomega" //nolint:revive // ok @@ -36,7 +35,7 @@ var _ = Describe("Tapable", Ordered, func() { invoked bool o *pref.Options err error - FS fstest.MapFS // don't forget to use TrimRoot + FS *lab.TestTraverseFS // don't forget to use TrimRoot root string ) diff --git a/traverse-api.go b/traverse-api.go index b375b1c..8e38008 100644 --- a/traverse-api.go +++ b/traverse-api.go @@ -83,12 +83,15 @@ var ( // 🌀 lfs // NewLocalFS creates a native file system. - NewLocalFS = lfs.NewLocalFS + NewLocalFS = lfs.NewReadDirFS // TODO: check this name // NewQueryStatusFS defines a file system that has a Stat // method to determine the existence of a path. NewQueryStatusFS = lfs.NewQueryStatusFS + // QueryStatusFromFS + QueryStatusFromFS = lfs.QueryStatusFromFS + // 🌀 filtering // NewCustomSampleFilter only needs to be called explicitly when defining