diff --git a/.vscode/settings.json b/.vscode/settings.json index f982f4e..bce455e 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -70,6 +70,7 @@ "varcheck", "watchv", "wgan", + "Wrapf", "xutils" ] } diff --git a/src/app/command/bootstrap.go b/src/app/command/bootstrap.go index 2e1e319..8879684 100644 --- a/src/app/command/bootstrap.go +++ b/src/app/command/bootstrap.go @@ -14,6 +14,7 @@ import ( "github.com/snivilised/cobrass/src/assistant/configuration" ci18n "github.com/snivilised/cobrass/src/assistant/i18n" xi18n "github.com/snivilised/extendio/i18n" + "github.com/snivilised/extendio/xfs/storage" "github.com/snivilised/extendio/xfs/utils" "github.com/snivilised/pixa/src/app/proxy" "github.com/snivilised/pixa/src/i18n" @@ -67,6 +68,7 @@ type Bootstrap struct { OptionsInfo ConfigureOptionsInfo ProfilesCFG proxy.ProfilesConfig SamplerCFG proxy.SamplerConfig + Vfs storage.VirtualFS } type ConfigureOptionsInfo struct { diff --git a/src/app/command/bootstrap_test.go b/src/app/command/bootstrap_test.go index 11793b6..45737d0 100644 --- a/src/app/command/bootstrap_test.go +++ b/src/app/command/bootstrap_test.go @@ -57,7 +57,9 @@ var _ = Describe("Bootstrap", Ordered, func() { Context("given: root defined with magick sub-command", func() { It("๐Ÿงช should: setup command without error", func() { - bootstrap := command.Bootstrap{} + bootstrap := command.Bootstrap{ + Vfs: nfs, + } rootCmd := bootstrap.Root(func(co *command.ConfigureOptionsInfo) { co.Detector = &DetectorStub{} co.Program = &ExecutorStub{ diff --git a/src/app/command/magick-cmd_test.go b/src/app/command/magick-cmd_test.go index c679d63..deead39 100644 --- a/src/app/command/magick-cmd_test.go +++ b/src/app/command/magick-cmd_test.go @@ -53,7 +53,9 @@ var _ = Describe("MagickCmd", Ordered, func() { When("specified flags are valid", func() { It("๐Ÿงช should: execute without error", func() { - bootstrap := command.Bootstrap{} + bootstrap := command.Bootstrap{ + Vfs: nfs, + } tester := helpers.CommandTester{ Args: []string{"mag"}, Root: bootstrap.Root(func(co *command.ConfigureOptionsInfo) { diff --git a/src/app/command/root-cmd_test.go b/src/app/command/root-cmd_test.go index 5571951..90bffab 100644 --- a/src/app/command/root-cmd_test.go +++ b/src/app/command/root-cmd_test.go @@ -39,7 +39,9 @@ var _ = Describe("RootCmd", Ordered, func() { }) BeforeEach(func() { - bootstrap := command.Bootstrap{} + bootstrap := command.Bootstrap{ + Vfs: nfs, + } tester = helpers.CommandTester{ Args: []string{"./"}, Root: bootstrap.Root(func(co *command.ConfigureOptionsInfo) { diff --git a/src/app/command/shrink-cmd.go b/src/app/command/shrink-cmd.go index 1f341df..2e0d2ac 100644 --- a/src/app/command/shrink-cmd.go +++ b/src/app/command/shrink-cmd.go @@ -136,6 +136,7 @@ func (b *Bootstrap) buildShrinkCommand(container *assistant.CobraContainer) *cob b.OptionsInfo.Config.Viper, b.ProfilesCFG, b.SamplerCFG, + b.Vfs, ) } else { return xvErr diff --git a/src/app/command/shrink-cmd_test.go b/src/app/command/shrink-cmd_test.go index 2082680..a2cd2b2 100644 --- a/src/app/command/shrink-cmd_test.go +++ b/src/app/command/shrink-cmd_test.go @@ -28,8 +28,10 @@ type shrinkTE struct { directory string } -func expectValidShrinkCmdInvocation(entry *shrinkTE) { - bootstrap := command.Bootstrap{} +func expectValidShrinkCmdInvocation(vfs storage.VirtualFS, entry *shrinkTE) { + bootstrap := command.Bootstrap{ + Vfs: vfs, + } // we also prepend the directory name to the command line // @@ -102,7 +104,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { // entry.directory = l10nPath entry.configPath = configPath - expectValidShrinkCmdInvocation(entry) + expectValidShrinkCmdInvocation(nfs, entry) }, func(entry *shrinkTE) string { return fmt.Sprintf("๐Ÿงช ===> given: '%v'", entry.message) @@ -203,7 +205,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(entry) + expectValidShrinkCmdInvocation(nfs, entry) }) It("๐Ÿงช should: execute successfully", func() { @@ -218,7 +220,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(entry) + expectValidShrinkCmdInvocation(nfs, entry) }) }) @@ -235,7 +237,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(entry) + expectValidShrinkCmdInvocation(nfs, entry) }) It("๐Ÿงช should: execute successfully", func() { @@ -250,7 +252,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(entry) + expectValidShrinkCmdInvocation(nfs, entry) }) }) }) diff --git a/src/app/proxy/config_test.go b/src/app/proxy/config_test.go index 0038f71..09c4c34 100644 --- a/src/app/proxy/config_test.go +++ b/src/app/proxy/config_test.go @@ -18,8 +18,10 @@ import ( "github.com/snivilised/pixa/src/internal/matchers" ) -func expectValidShrinkCmdInvocation(entry *configTE) { - bootstrap := command.Bootstrap{} +func expectValidShrinkCmdInvocation(vfs storage.VirtualFS, entry *configTE) { + bootstrap := command.Bootstrap{ + Vfs: vfs, + } options := []string{ entry.comm, entry.file, @@ -115,7 +117,7 @@ var _ = Describe("Config", Ordered, func() { _ = actual Expect(1).To(Equal(1)) - expectValidShrinkCmdInvocation(entry) + expectValidShrinkCmdInvocation(nfs, entry) } else { actual := entry.actual(entry) entry.assert(entry, actual) diff --git a/src/app/proxy/enter-shrink.go b/src/app/proxy/enter-shrink.go index d6511e0..9aada2d 100644 --- a/src/app/proxy/enter-shrink.go +++ b/src/app/proxy/enter-shrink.go @@ -10,6 +10,7 @@ import ( "github.com/samber/lo" "github.com/snivilised/cobrass/src/assistant/configuration" "github.com/snivilised/extendio/xfs/nav" + "github.com/snivilised/extendio/xfs/storage" ) type ShrinkEntry struct { @@ -100,12 +101,22 @@ func (e *ShrinkEntry) PrincipalOptionsFn(o *nav.TraverseOptions) { } func (e *ShrinkEntry) createFinder() *PathFinder { - return &PathFinder{ - Behaviours: strategies{ - output: inlineOutputStrategy{}, // ejectOutputStrategy{} - deletion: inlineDeletionStrategy{}, // ejectDeletionStrategy{} + finder := &PathFinder{ + behaviours: strategies{ + output: inlineOutputStrategy{}, + deletion: inlineDeletionStrategy{}, }, } + + if e.Inputs.ParamSet.Native.OutputPath != "" { + finder.behaviours.output = &ejectOutputStrategy{} + } + + if e.Inputs.ParamSet.Native.TrashPath != "" { + finder.behaviours.deletion = &ejectOutputStrategy{} + } + + return finder } func (e *ShrinkEntry) ConfigureOptions(o *nav.TraverseOptions) { @@ -121,6 +132,7 @@ func (e *ShrinkEntry) ConfigureOptions(o *nav.TraverseOptions) { e.EntryBase.ConfigureOptions(o) + finder := e.createFinder() e.Registry = NewRunnerRegistry(&SharedRunnerInfo{ Type: RunnerTypeSamplerEn, // TODO: to come from an arg !!! Options: e.Options, @@ -128,7 +140,11 @@ func (e *ShrinkEntry) ConfigureOptions(o *nav.TraverseOptions) { profiles: e.ProfilesCFG, sampler: e.SamplerCFG, Inputs: e.Inputs, - finder: e.createFinder(), + finder: finder, + fileManager: &FileManager{ + vfs: e.Vfs, + finder: finder, + }, }) } @@ -216,6 +232,7 @@ func EnterShrink( config configuration.ViperConfig, profilesCFG ProfilesConfig, samplerCFG SamplerConfig, + vfs storage.VirtualFS, ) error { fmt.Printf("---> ๐Ÿ”Š๐Ÿ”Š Directory: '%v'\n", inputs.Root.ParamSet.Native.Directory) @@ -226,6 +243,7 @@ func EnterShrink( Config: config, ProfilesCFG: profilesCFG, SamplerCFG: samplerCFG, + Vfs: vfs, }, Inputs: inputs, } diff --git a/src/app/proxy/entry-base.go b/src/app/proxy/entry-base.go index 82ebdee..3545520 100644 --- a/src/app/proxy/entry-base.go +++ b/src/app/proxy/entry-base.go @@ -8,6 +8,7 @@ import ( "github.com/samber/lo" "github.com/snivilised/cobrass/src/assistant/configuration" "github.com/snivilised/extendio/xfs/nav" + "github.com/snivilised/extendio/xfs/storage" "github.com/snivilised/lorax/boost" ) @@ -40,6 +41,7 @@ type EntryBase struct { Registry *RunnerRegistry ProfilesCFG ProfilesConfig SamplerCFG SamplerConfig + Vfs storage.VirtualFS } func (e *EntryBase) ConfigureOptions(o *nav.TraverseOptions) { diff --git a/src/app/proxy/execution-step.go b/src/app/proxy/execution-step.go index a002fa4..4d4be90 100644 --- a/src/app/proxy/execution-step.go +++ b/src/app/proxy/execution-step.go @@ -17,8 +17,7 @@ type Sequence []Step // and output file names; this is the responsibility of the runner, which uses // the path-finder to accomplish that task. type magickStep struct { - fileManager *FileManager - program Executor + shared *SharedRunnerInfo thirdPartyCL clif.ThirdPartyCommandLine sourcePath string outputPath string @@ -29,12 +28,7 @@ type magickStep struct { func (s *magickStep) Run() error { positional := []string{s.sourcePath} - // prepare: move existing file out of the way - - err := s.program.Execute(clif.Expand(positional, s.thirdPartyCL)...) - - // invoke deletions - // delete journal file + err := s.shared.program.Execute(clif.Expand(positional, s.thirdPartyCL)...) return err } diff --git a/src/app/proxy/file-manager.go b/src/app/proxy/file-manager.go index 34d563f..3286d47 100644 --- a/src/app/proxy/file-manager.go +++ b/src/app/proxy/file-manager.go @@ -1,3 +1,48 @@ package proxy -type FileManager struct{} +import ( + "github.com/pkg/errors" + "github.com/snivilised/extendio/xfs/storage" +) + +type FileManager struct { + vfs storage.VirtualFS + finder *PathFinder +} + +func (fm *FileManager) setup(source string) error { + // prepare: move existing file out of the way + + // https://pkg.go.dev/os#Rename LinkError may result + // + // this might not be right. it may be that we want to leave the + // original alone and create other outputs; in this scenario + // we don't want to rename/move the source... + // + destination := fm.finder.Destination(source) + + if err := fm.vfs.Rename(source, destination); err != nil { + return errors.Wrapf(err, "could not complete setup for '%v'", source) + } + + return nil +} + +func (fm *FileManager) move(from, to string) error { + _, _ = from, to + + return nil +} + +func (fm *FileManager) delete(target string) error { + _ = target + + return nil +} + +func (fm *FileManager) tidy() error { + // invoke deletions + // delete journal file + // + return nil +} diff --git a/src/app/proxy/path-finder.go b/src/app/proxy/path-finder.go index b53feca..8a1c896 100644 --- a/src/app/proxy/path-finder.go +++ b/src/app/proxy/path-finder.go @@ -1,5 +1,9 @@ package proxy +import ( + "path/filepath" +) + // INLINE-MODE: EJECT | INLINE (should we call this a strategy? // they do the same thing but create a different output structure => OutputStrategy) // @@ -81,7 +85,15 @@ type PathFinder struct { // I think this depends on the mode (tidy/preserve) Trash string - Behaviours strategies + behaviours strategies +} + +func (f *PathFinder) Destination(source string) string { + // may be we also return a bool that indicates weather a rename + // should be implemented or not. this depends on the appropriate + // strategy. Or if we dont need to rename, we return an empty string; + // this is the preferred solution. + return filepath.Join(f.Output, source) } /* diff --git a/src/app/proxy/proxy-defs.go b/src/app/proxy/proxy-defs.go index d166061..c263e3d 100644 --- a/src/app/proxy/proxy-defs.go +++ b/src/app/proxy/proxy-defs.go @@ -13,13 +13,14 @@ const ( ) type SharedRunnerInfo struct { - Type RunnerTypeEnum - Options *nav.TraverseOptions - program Executor - profiles ProfilesConfig - sampler SamplerConfig - Inputs *ShrinkCommandInputs - finder *PathFinder + Type RunnerTypeEnum + Options *nav.TraverseOptions + program Executor + profiles ProfilesConfig + sampler SamplerConfig + Inputs *ShrinkCommandInputs + finder *PathFinder + fileManager *FileManager } // ItemController diff --git a/src/app/proxy/runner-base.go b/src/app/proxy/runner-base.go index 3a61877..490f7ab 100644 --- a/src/app/proxy/runner-base.go +++ b/src/app/proxy/runner-base.go @@ -1,9 +1,11 @@ package proxy import ( + "github.com/pkg/errors" "github.com/snivilised/cobrass" "github.com/snivilised/cobrass/src/clif" "github.com/snivilised/extendio/collections" + "github.com/snivilised/extendio/xfs/nav" ) // 3rd party arguments provider: ad-hoc/profile/scheme @@ -22,8 +24,7 @@ func (r *baseRunner) profileSequence( changed := r.shared.Inputs.ParamSet.Native.ThirdPartySet.LongChangedCL cl := r.composeProfileCL(name, changed) step := &magickStep{ - // fileManager - program: r.shared.program, + shared: r.shared, thirdPartyCL: cl, sourcePath: itemPath, // outputPath: , @@ -43,8 +44,7 @@ func (r *baseRunner) schemeSequence( for _, currentProfileName := range scheme.Profiles { cl := r.composeProfileCL(currentProfileName, changed) step := &magickStep{ - // fileManager - program: r.shared.program, + shared: r.shared, thirdPartyCL: cl, sourcePath: itemPath, // outputPath: , @@ -62,8 +62,7 @@ func (r *baseRunner) adhocSequence( ) Sequence { changed := r.shared.Inputs.ParamSet.Native.ThirdPartySet.LongChangedCL step := &magickStep{ - // fileManager - program: r.shared.program, + shared: r.shared, thirdPartyCL: changed, sourcePath: itemPath, // outputPath: , @@ -77,9 +76,7 @@ func (r *baseRunner) composeProfileCL( profileName string, secondary clif.ThirdPartyCommandLine, ) clif.ThirdPartyCommandLine { - // TODO: what do we do if we don't find the profile? - // - primary, _ := r.shared.profiles.Profile(profileName) + primary, _ := r.shared.profiles.Profile(profileName) // profile already validated return cobrass.Evaluate( primary, @@ -88,7 +85,7 @@ func (r *baseRunner) composeProfileCL( ) } -func (r *baseRunner) Run(sequence Sequence) error { +func (r *baseRunner) Run(item *nav.TraverseItem, sequence Sequence) error { var ( zero Step resultErr error @@ -96,7 +93,28 @@ func (r *baseRunner) Run(sequence Sequence) error { iterator := collections.ForwardRunIt[Step, error](sequence, zero) each := func(s Step) error { - return s.Run() + // TODO: need to decide a proper policy for cleaning up + // in the presence of an error. Do we allow the journal + // file to remain in place? What happens if there is a timeout? + // There are a few more things to decide about error handling. + // Perhaps we have an error policy including one that implements + // a retry. + if err := r.shared.fileManager.setup(item.Path); err != nil { + return err + } + + err := s.Run() + te := r.shared.fileManager.tidy() + + switch { + case (err != nil) && (te != nil): + return errors.Wrap(err, te.Error()) + + case (err != nil): + return err + } + + return te } while := func(_ Step, err error) bool { if resultErr == nil { diff --git a/src/app/proxy/runner-sampler.go b/src/app/proxy/runner-sampler.go index f73cc51..78c5488 100644 --- a/src/app/proxy/runner-sampler.go +++ b/src/app/proxy/runner-sampler.go @@ -29,5 +29,5 @@ func (r *SamplerRunner) OnNewShrinkItem(item *nav.TraverseItem, sequence = r.adhocSequence(item.Path) } - return r.Run(sequence) + return r.Run(item, sequence) } diff --git a/src/app/proxy/runner-sampler_test.go b/src/app/proxy/runner-sampler_test.go index 76354d7..3f635ed 100644 --- a/src/app/proxy/runner-sampler_test.go +++ b/src/app/proxy/runner-sampler_test.go @@ -201,7 +201,9 @@ var _ = Describe("SamplerRunner", Ordered, func() { "--mode", "tidy", } - bootstrap := command.Bootstrap{} + bootstrap := command.Bootstrap{ + Vfs: nfs, + } tester := helpers.CommandTester{ Args: append(options, entry.args...), Root: bootstrap.Root(func(co *command.ConfigureOptionsInfo) {