From 9d15bf01b79f1c88e4b15ccfd75c75a4fa7b89ce Mon Sep 17 00:00:00 2001 From: plastikfan Date: Tue, 13 Feb 2024 16:38:53 +0000 Subject: [PATCH] fix(command,cfg,proxy): correct processing of paths by path-finder (#172) fix(proxy): minor renames (#172) fix(proxy): add path finder observer (#172) fix(cfg,proxy): start off stricter path finder tests (#172) fix(command,proxy): add --cuddle flag (#172) fix(proxy): add path-fnder unit tests (#172) fix(proxy): add transparent/profile/non-cuddled path finder tests (#172) fix(proxy): add transparent/profile/cuddled path finder tests (#172) fix(proxy): add transparent/scheme path finder tests (#172) fix(command): make cuddle incompatible with both trash and output (#172) fix(proxy): add transparent with trash path finder unit tests (#172) fix(proxy): non transparent with output path finder unit tests (#172) --- src/app/cfg/default-pixa.yml | 1 + src/app/cfg/ms-config.go | 15 +- src/app/command/bootstrap.go | 1 + src/app/command/root-cmd.go | 1 + src/app/command/shrink-cmd.go | 30 + src/app/command/shrink-cmd_test.go | 71 ++- src/app/proxy/common/config-defs.go | 1 + src/app/proxy/common/const-defs.go | 10 +- src/app/proxy/common/filing-defs.go | 14 +- src/app/proxy/common/input-defs.go | 6 + src/app/proxy/common/static-info.go | 23 +- src/app/proxy/enter-shrink.go | 15 +- src/app/proxy/entry-base.go | 11 +- .../proxy/filing/dry-run-finder-decorator.go | 4 + src/app/proxy/filing/filing-defs.go | 15 +- src/app/proxy/filing/filing_suite_test.go | 13 + src/app/proxy/filing/path-finder.go | 273 +++++---- src/app/proxy/filing/path-finder_test.go | 546 ++++++++++++++++++ src/app/proxy/orc/controller-step.go | 7 +- src/app/proxy/orc/controller.go | 7 + src/app/proxy/path-finder-observer_test.go | 154 +++++ src/app/proxy/pixa_test.go | 230 +++++--- src/app/proxy/user/model.go | 4 +- src/app/proxy/user/ui-linear.go | 2 +- src/i18n/messages-command.go | 14 + test/data/configuration/pixa-test.yml | 1 + 26 files changed, 1240 insertions(+), 229 deletions(-) create mode 100644 src/app/proxy/filing/filing_suite_test.go create mode 100644 src/app/proxy/filing/path-finder_test.go create mode 100644 src/app/proxy/path-finder-observer_test.go diff --git a/src/app/cfg/default-pixa.yml b/src/app/cfg/default-pixa.yml index 662e20e..2a1609d 100644 --- a/src/app/cfg/default-pixa.yml +++ b/src/app/cfg/default-pixa.yml @@ -32,6 +32,7 @@ advanced: journal-suffix: journal trash: TRASH fake: .FAKE + supplement: SUPP extensions: suffixes-csv: "jpg,jpeg,png" transforms-csv: lower diff --git a/src/app/cfg/ms-config.go b/src/app/cfg/ms-config.go index 065e712..2bab1b4 100644 --- a/src/app/cfg/ms-config.go +++ b/src/app/cfg/ms-config.go @@ -86,11 +86,12 @@ func (c *MsSamplerConfig) NoFolders() uint { } type MsLabelsConfig struct { - Adhoc string `mapstructure:"adhoc"` - Journal string `mapstructure:"journal-suffix"` - Legacy string `mapstructure:"legacy"` - Trash string `mapstructure:"trash"` - Fake string `mapstructure:"fake"` + Adhoc string `mapstructure:"adhoc"` + Journal string `mapstructure:"journal-suffix"` + Legacy string `mapstructure:"legacy"` + Trash string `mapstructure:"trash"` + Fake string `mapstructure:"fake"` + Supplement string `mapstructure:"supplement"` } type MsExtensionsConfig struct { @@ -160,6 +161,10 @@ func (c *MsAdvancedConfig) FakeLabel() string { return c.LabelsCFG.Fake } +func (c *MsAdvancedConfig) SupplementLabel() string { + return c.LabelsCFG.Supplement +} + func (c *MsAdvancedConfig) Extensions() common.ExtensionsConfig { return &c.ExtensionsCFG } diff --git a/src/app/command/bootstrap.go b/src/app/command/bootstrap.go index 5709348..68dfff7 100644 --- a/src/app/command/bootstrap.go +++ b/src/app/command/bootstrap.go @@ -66,6 +66,7 @@ type Bootstrap struct { Vfs storage.VirtualFS Logger *slog.Logger Presentation common.PresentationOptions + Observers common.Observers } type ConfigureOptionsInfo struct { diff --git a/src/app/command/root-cmd.go b/src/app/command/root-cmd.go index 1463762..3e99aa4 100644 --- a/src/app/command/root-cmd.go +++ b/src/app/command/root-cmd.go @@ -112,5 +112,6 @@ func (b *Bootstrap) getRootInputs() *common.RootCommandInputs { ).(*assistant.ParamSet[store.CascadeParameterSet]), Configs: b.Configs, Presentation: &b.Presentation, + Observers: &b.Observers, } } diff --git a/src/app/command/shrink-cmd.go b/src/app/command/shrink-cmd.go index 48aa46b..3eda3db 100644 --- a/src/app/command/shrink-cmd.go +++ b/src/app/command/shrink-cmd.go @@ -47,6 +47,7 @@ var shrinkShortFlags = cobrass.KnownByCollection{ // "output": "o", "trash": "t", + "cuddle": "c", // families: // "cpu": "C", // family: worker-pool @@ -198,6 +199,20 @@ func (b *Bootstrap) buildShrinkCommand(container *assistant.CobraContainer) *cob }, ) + // --cuddle(c) + // + const ( + defaultCuddle = false + ) + + paramSet.BindBool( + newShrinkFlagInfoWithShort( + xi18n.Text(i18n.ShrinkCmdCuddleParamUsageTemplData{}), + defaultCuddle, + ), + ¶mSet.Native.Cuddle, + ) + // --gaussian-blur(b) // const ( @@ -333,6 +348,21 @@ func (b *Bootstrap) buildShrinkCommand(container *assistant.CobraContainer) *cob // // shrinkCommand.Args = validatePositionalArgs + // If we allowed --output to be specified with --cuddle, then that would + // mean the result files would be written to the output location and then input + // files would have to follow the results, leaving the origin without + // the input or the output. This could be seen as excessive and unnecessary. + // The cuddle option is most useful to the user when running a sample to + // enable easier comparison of the result with the input. If the user + // really wants to cuddle, then there should be no need to specify an output. + // We need to reduce the number of permutations to reduce complexity and + // the number of required unit tests; particularly for the path-finder. + // The same logic applies to cuddle with trash, except that's its even more + // acute in this usage scenario, because you never want your new results + // to be cuddled into the trash location. + paramSet.Command.MarkFlagsMutuallyExclusive("output", "cuddle") + paramSet.Command.MarkFlagsMutuallyExclusive("trash", "cuddle") + return shrinkCommand } diff --git a/src/app/command/shrink-cmd_test.go b/src/app/command/shrink-cmd_test.go index ea6a70f..8f03c74 100644 --- a/src/app/command/shrink-cmd_test.go +++ b/src/app/command/shrink-cmd_test.go @@ -31,6 +31,7 @@ type commandTE struct { outputFlag string outputValue string configPath string + expectError bool } type shrinkTE struct { @@ -38,7 +39,7 @@ type shrinkTE struct { directory string } -func expectValidShrinkCmdInvocation(vfs storage.VirtualFS, entry *shrinkTE, root string) { +func assertShrinkCmdInvocation(vfs storage.VirtualFS, entry *shrinkTE, root string) { bootstrap := command.Bootstrap{ Vfs: vfs, } @@ -69,9 +70,16 @@ func expectValidShrinkCmdInvocation(vfs storage.VirtualFS, entry *shrinkTE, root } _, err := tester.Execute() - Expect(err).Error().To(BeNil(), - "should pass validation due to all flag being valid", - ) + + if entry.expectError { + Expect(err).Error().NotTo(BeNil(), + "expected error due to invalid flag combination", + ) + } else { + Expect(err).Error().To(BeNil(), + "should pass validation due to all flag being valid", + ) + } } var _ = Describe("ShrinkCmd", Ordered, func() { @@ -99,12 +107,17 @@ var _ = Describe("ShrinkCmd", Ordered, func() { func(entry *shrinkTE) { entry.directory = BackyardWorldsPlanet9Scan01 entry.configPath = configPath - expectValidShrinkCmdInvocation(vfs, entry, root) + + assertShrinkCmdInvocation(vfs, entry, root) }, func(entry *shrinkTE) string { return fmt.Sprintf("๐Ÿงช ===> given: '%v'", entry.message) }, + // vanilla in this context just means that other options + // such as "--strip", "--interlace", "plane", "--quality", "85", + // are provided, in addition to the option being tested + Entry(nil, &shrinkTE{ commandTE: commandTE{ message: "vanilla with long form options", @@ -218,6 +231,46 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, }, }), + + // ----> + Entry(nil, &shrinkTE{ + commandTE: commandTE{ + message: "long form cuddle", + args: []string{ + "--cuddle", + }, + }, + }), + + Entry(nil, &shrinkTE{ + commandTE: commandTE{ + message: "short form cuddle", + args: []string{ + "-c", + }, + }, + }), + + Entry(nil, &shrinkTE{ + commandTE: commandTE{ + message: "expect error since cuddle not compatible with output", + expectError: true, + args: []string{ + "--cuddle", "--output", "results", + }, + }, + }), + + Entry(nil, &shrinkTE{ + commandTE: commandTE{ + message: "expect error since cuddle not compatible with trash", + expectError: true, + args: []string{ + "--cuddle", "--trash", "results", + }, + }, + }), + // <---- ) // NB: these tests are required because state does not work with @@ -236,7 +289,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(vfs, entry, root) + assertShrinkCmdInvocation(vfs, entry, root) }) It("๐Ÿงช should: execute successfully", func() { @@ -251,7 +304,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(vfs, entry, root) + assertShrinkCmdInvocation(vfs, entry, root) }) }) @@ -268,7 +321,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(vfs, entry, root) + assertShrinkCmdInvocation(vfs, entry, root) }) It("๐Ÿงช should: execute successfully", func() { @@ -283,7 +336,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { }, } - expectValidShrinkCmdInvocation(vfs, entry, root) + assertShrinkCmdInvocation(vfs, entry, root) }) }) }) diff --git a/src/app/proxy/common/config-defs.go b/src/app/proxy/common/config-defs.go index a824a9a..3466f1b 100644 --- a/src/app/proxy/common/config-defs.go +++ b/src/app/proxy/common/config-defs.go @@ -92,6 +92,7 @@ type ( LegacyLabel() string TrashLabel() string FakeLabel() string + SupplementLabel() string Extensions() ExtensionsConfig Executable() ExecutableConfig } diff --git a/src/app/proxy/common/const-defs.go b/src/app/proxy/common/const-defs.go index cd46509..62c700d 100644 --- a/src/app/proxy/common/const-defs.go +++ b/src/app/proxy/common/const-defs.go @@ -49,9 +49,8 @@ type ( } filingDefs struct { - DejaVu string - JournalExt string - JournalTag string + JournalExt string + Discriminator string // helps to identify files that should be filtered out } definitions struct { @@ -93,8 +92,7 @@ var Definitions = definitions{ Home: "PIXA_HOME", }, Filing: filingDefs{ - DejaVu: fmt.Sprintf("$%v$", appName), - JournalExt: ".txt", - JournalTag: ".$", + JournalExt: ".txt", + Discriminator: ".$", }, } diff --git a/src/app/proxy/common/filing-defs.go b/src/app/proxy/common/filing-defs.go index bc01b58..f2b4c4e 100644 --- a/src/app/proxy/common/filing-defs.go +++ b/src/app/proxy/common/filing-defs.go @@ -8,11 +8,11 @@ import ( type ( JournalMetaInfo struct { - Core string // without any decoration - Journal string // used as part of the journal file name - WithoutExt string - Extension string // .txt - Tag string // the journal file discriminator (.$) + Core string // without any decoration + Actual string // used as part of the journal file name + WithoutExt string + Extension string // .txt + Discriminator string // the journal/sample file discriminator (.$) } PathInfo struct { @@ -21,6 +21,9 @@ type ( Scheme string Profile string RunStep RunStepInfo + Cuddle bool + Output string + Trash string } PathFinder interface { @@ -30,6 +33,7 @@ type ( JournalFullPath(item *nav.TraverseItem) string Statics() *StaticInfo Scheme() string + Observe(o PathFinder) PathFinder } FileManager interface { diff --git a/src/app/proxy/common/input-defs.go b/src/app/proxy/common/input-defs.go index fc5554f..abe7507 100644 --- a/src/app/proxy/common/input-defs.go +++ b/src/app/proxy/common/input-defs.go @@ -87,6 +87,11 @@ type ShrinkParameterSet struct { // OutputPath string TrashPath string + Cuddle bool +} + +type Observers struct { + PathFinder PathFinder } type RootCommandInputs struct { @@ -100,6 +105,7 @@ type RootCommandInputs struct { TextualFam *assistant.ParamSet[store.TextualInteractionParameterSet] Configs *Configs Presentation *PresentationOptions + Observers *Observers } type ShrinkCommandInputs struct { diff --git a/src/app/proxy/common/static-info.go b/src/app/proxy/common/static-info.go index 7e1ef8d..1fcaa2d 100644 --- a/src/app/proxy/common/static-info.go +++ b/src/app/proxy/common/static-info.go @@ -7,22 +7,23 @@ import ( ) type StaticInfo struct { - Adhoc string - Meta JournalMetaInfo - Legacy string - Trash string - Fake string + Adhoc string + Journal JournalMetaInfo + Legacy string + Trash string + Fake string + Supplement string } func (i *StaticInfo) JournalLocation(name, parent string) string { - file := name + i.Meta.Journal + file := name + i.Journal.Actual journalFile := filepath.Join(parent, file) return journalFile } func (i *StaticInfo) JournalFilterGlob() string { - return fmt.Sprintf("*%v%v*", i.Meta.Tag, i.Meta.Core) + return fmt.Sprintf("*%v%v*", i.Journal.Discriminator, i.Journal.Core) } func (i *StaticInfo) JournalFilterRegex(sourcePattern, suffixesCSV string) string { @@ -35,3 +36,11 @@ func (i *StaticInfo) JournalFilterRegex(sourcePattern, suffixesCSV string) strin // return fmt.Sprintf("(?i).%v.*(%v)$", sourcePattern, strings.Join(suffixes, "|")) } + +func (i *StaticInfo) FileSupplement(baseFilename, supp string) string { + return fmt.Sprintf("%v.%v", baseFilename, supp) +} + +func (i *StaticInfo) TrashTag() string { + return fmt.Sprintf("$%v$", i.Trash) +} diff --git a/src/app/proxy/enter-shrink.go b/src/app/proxy/enter-shrink.go index e30d550..2683e58 100644 --- a/src/app/proxy/enter-shrink.go +++ b/src/app/proxy/enter-shrink.go @@ -27,7 +27,7 @@ func (e *ShrinkEntry) DiscoverOptionsFn(o *nav.TraverseOptions) { o.Callback = &nav.LabelledTraverseCallback{ Label: "Discovery: Shrink Entry Callback", Fn: func(item *nav.TraverseItem) error { - if strings.Contains(item.Path, common.Definitions.Filing.DejaVu) { + if strings.Contains(item.Path, e.FileManager.Finder().Statics().TrashTag()) { return fs.SkipDir } journal := e.FileManager.Finder().JournalFullPath(item) @@ -58,7 +58,7 @@ func (e *ShrinkEntry) PrincipalOptionsFn(o *nav.TraverseOptions) { o.Callback = e.EntryBase.Interaction.Decorate(&nav.LabelledTraverseCallback{ Label: "Principal: Shrink Entry Callback", Fn: func(item *nav.TraverseItem) error { - if strings.Contains(item.Path, common.Definitions.Filing.DejaVu) { + if strings.Contains(item.Path, e.FileManager.Finder().Statics().TrashTag()) { return fs.SkipDir } @@ -101,7 +101,7 @@ func clearResumeFromWith(with nav.CreateNewRunnerWith) nav.CreateNewRunnerWith { } func (e *ShrinkEntry) resumeFn(item *nav.TraverseItem) error { - if strings.HasPrefix(item.Extension.Name, common.Definitions.Filing.DejaVu) { + if strings.HasPrefix(item.Extension.Name, e.FileManager.Finder().Statics().TrashTag()) { return fs.SkipDir } @@ -165,7 +165,14 @@ func EnterShrink( err error ) - finder := filing.NewFinder(params.Inputs) + finder := filing.NewFinder(&filing.NewFinderInfo{ + Advanced: params.Inputs.Root.Configs.Advanced, + Schemes: params.Inputs.Root.Configs.Schemes, + OutputPath: params.Inputs.ParamSet.Native.OutputPath, + TrashPath: params.Inputs.ParamSet.Native.TrashPath, + DryRun: params.Inputs.Root.PreviewFam.Native.DryRun, + Observer: params.Inputs.Root.Observers.PathFinder, + }) fileManager := filing.NewManager(params.Vfs, finder) if agent, err = ipc.New( diff --git a/src/app/proxy/entry-base.go b/src/app/proxy/entry-base.go index 3313bf0..ed4e737 100644 --- a/src/app/proxy/entry-base.go +++ b/src/app/proxy/entry-base.go @@ -63,11 +63,18 @@ func (e *EntryBase) ConfigureOptions(o *nav.TraverseOptions) { return nil, err } + statics := e.FileManager.Finder().Statics() + jWithoutExt := statics.Journal.WithoutExt + trash := statics.TrashTag() + return lo.Filter(contents, func(item fs.DirEntry, index int) bool { name := item.Name() - withoutExt := e.FileManager.Finder().Statics().Meta.WithoutExt - return !strings.HasPrefix(name, ".") && !strings.Contains(name, withoutExt) + // todo: filter out sample files + + return !strings.HasPrefix(name, ".") && + !strings.Contains(name, jWithoutExt) && + !strings.Contains(name, trash) }), nil } diff --git a/src/app/proxy/filing/dry-run-finder-decorator.go b/src/app/proxy/filing/dry-run-finder-decorator.go index 89f21bf..8309e0a 100644 --- a/src/app/proxy/filing/dry-run-finder-decorator.go +++ b/src/app/proxy/filing/dry-run-finder-decorator.go @@ -52,3 +52,7 @@ func (d *dryRunPathFinderDecorator) Statics() *common.StaticInfo { func (d *dryRunPathFinderDecorator) Scheme() string { return d.decorated.Scheme() } + +func (d *dryRunPathFinderDecorator) Observe(t common.PathFinder) common.PathFinder { + return t +} diff --git a/src/app/proxy/filing/filing-defs.go b/src/app/proxy/filing/filing-defs.go index d43c262..a40bea5 100644 --- a/src/app/proxy/filing/filing-defs.go +++ b/src/app/proxy/filing/filing-defs.go @@ -2,13 +2,22 @@ package filing import ( "path" + "path/filepath" "strings" -) -var ( - DejaVu = "$pixa$" + "github.com/snivilised/pixa/src/app/proxy/common" ) func FilenameWithoutExtension(name string) string { return strings.TrimSuffix(name, path.Ext(name)) } + +func SupplementFilename(name, supp string, statics *common.StaticInfo) string { + withoutExt := FilenameWithoutExtension(name) + + return statics.FileSupplement(withoutExt, supp) + path.Ext(name) +} + +func SupplementFolder(directory, supp string) string { + return filepath.Join(directory, supp) +} diff --git a/src/app/proxy/filing/filing_suite_test.go b/src/app/proxy/filing/filing_suite_test.go new file mode 100644 index 0000000..4ffe1f4 --- /dev/null +++ b/src/app/proxy/filing/filing_suite_test.go @@ -0,0 +1,13 @@ +package filing_test + +import ( + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" +) + +func TestFiling(t *testing.T) { + RegisterFailHandler(Fail) + RunSpecs(t, "Filing Suite") +} diff --git a/src/app/proxy/filing/path-finder.go b/src/app/proxy/filing/path-finder.go index 9ab6623..3a2db5b 100644 --- a/src/app/proxy/filing/path-finder.go +++ b/src/app/proxy/filing/path-finder.go @@ -1,6 +1,7 @@ package filing import ( + "fmt" "path/filepath" "strings" @@ -9,20 +10,30 @@ import ( "github.com/snivilised/pixa/src/app/proxy/common" ) +type NewFinderInfo struct { + Advanced common.AdvancedConfig + Schemes common.SchemesConfig + Scheme string + Profile string + OutputPath string + TrashPath string + DryRun bool + Observer common.PathFinder +} + func NewFinder( - inputs *common.ShrinkCommandInputs, + info *NewFinderInfo, ) common.PathFinder { - advanced := inputs.Root.Configs.Advanced + advanced := info.Advanced extensions := advanced.Extensions() finder := &PathFinder{ - scheme: inputs.Root.ProfileFam.Native.Scheme, - ExplicitProfile: inputs.Root.ProfileFam.Native.Profile, - Arity: 1, - statics: &common.StaticInfo{ - Adhoc: advanced.AdhocLabel(), - Legacy: advanced.LegacyLabel(), - Trash: advanced.TrashLabel(), - Fake: advanced.FakeLabel(), + Sch: info.Scheme, + Stats: &common.StaticInfo{ + Adhoc: advanced.AdhocLabel(), + Legacy: advanced.LegacyLabel(), + Trash: advanced.TrashLabel(), + Fake: advanced.FakeLabel(), + Supplement: advanced.SupplementLabel(), }, Ext: &ExtensionTransformation{ Transformers: strings.Split(extensions.Transforms(), ","), @@ -30,49 +41,25 @@ func NewFinder( }, } - schemes := inputs.Root.Configs.Schemes - if finder.scheme != "" { - schemeCFG, _ := schemes.Scheme(finder.scheme) - finder.Arity = len(schemeCFG.Profiles()) - } - - if inputs.ParamSet.Native.OutputPath != "" { - finder.Output = inputs.ParamSet.Native.OutputPath - } else { - finder.transparentInput = true - } + finder.init(&NewFinderInfo{ + Advanced: info.Advanced, + Schemes: info.Schemes, + OutputPath: info.OutputPath, + TrashPath: info.TrashPath, + DryRun: info.DryRun, + Observer: info.Observer, + }) - if inputs.ParamSet.Native.TrashPath != "" { - finder.Trash = inputs.ParamSet.Native.TrashPath - } - - journal := advanced.JournalLabel() - - if !strings.HasSuffix(journal, common.Definitions.Filing.JournalExt) { - journal += common.Definitions.Filing.JournalExt - } - - if !strings.HasPrefix(journal, common.Definitions.Filing.JournalTag) { - journal = common.Definitions.Filing.JournalTag + journal - } - - withoutExt := strings.TrimSuffix(journal, common.Definitions.Filing.JournalExt) - core := strings.TrimPrefix(withoutExt, common.Definitions.Filing.JournalTag) - - finder.statics.Meta = common.JournalMetaInfo{ - Core: core, - Journal: journal, - WithoutExt: withoutExt, - Extension: common.Definitions.Filing.JournalExt, - Tag: common.Definitions.Filing.JournalTag, - } - - if inputs.Root.PreviewFam.Native.DryRun { + if info.DryRun { return &dryRunPathFinderDecorator{ decorated: finder, } } + if info.Observer != nil { + return info.Observer.Observe(finder) + } + return finder } @@ -82,9 +69,7 @@ const ( pfPathUndefined pfPath = iota pfPathInputTransferFolder pfPathTxInputDestinationFolder - pfPathInputDestinationFileOriginalExt pfPathResultFolder - pfPathResultFile ) const ( @@ -104,7 +89,7 @@ var ( /* ๐Ÿ“š FIELD DICTIONARY: - ADHOC: (static): tag that indicates no profile or scheme is active -- INPUT-DESTINATION: the path where the input file is moved to +- TRANSFER-DESTINATION: the path where the input file is moved to - ITEM-FULL-NAME: the original item.Name, which includes the original extension - OUTPUT-ROOT: --output flag - ITEM-SUB-PATH: item.Extension.SubPath @@ -123,26 +108,19 @@ where would be skipped by he navigation process. func init() { pfTemplates = pfTemplatesCollection{ pfPathInputTransferFolder: templateSegments{ - "${{INPUT-DESTINATION}}", + "${{TRANSFER-DESTINATION}}", "${{ITEM-SUB-PATH}}", "${{DEJA-VU}}", "${{SUPPLEMENT}}", - "${{TRASH-LABEL}}", }, pfPathTxInputDestinationFolder: templateSegments{ "${{OUTPUT-ROOT}}", }, - pfPathInputDestinationFileOriginalExt: templateSegments{ - "${{ITEM-FULL-NAME}}", - }, pfPathResultFolder: templateSegments{ "${{OUTPUT-ROOT}}", "${{ITEM-SUB-PATH}}", "${{SUPPLEMENT}}", }, - pfPathResultFile: templateSegments{ - "${{RESULT-NAME}}", - }, } } @@ -194,11 +172,8 @@ type ExtensionTransformation struct { // PathFinder provides the common paths required, but its the controller that know // the specific paths based around this common framework type PathFinder struct { - scheme string - ExplicitProfile string - // Origin is the parent of the item (item.Parent) - // - Origin string + Sch string + Origin string // Origin is the parent of the item (item.Parent) // Output is the output as indicated by --output. If not set, then it is // derived: @@ -206,28 +181,56 @@ type PathFinder struct { // - full: (inline) -- item.parent Output string Trash string - Arity int Ext *ExtensionTransformation transparentInput bool - statics *common.StaticInfo + Stats *common.StaticInfo +} + +func (f *PathFinder) init(info *NewFinderInfo) { + f.Output = info.OutputPath + f.Trash = info.TrashPath + + // When the user specifies an alternative location for the results to be sent to + // with the --output flag, then the input is no longer transparent, as the user has + // to go to the output location to see the result. + f.transparentInput = info.OutputPath == "" + + journal := info.Advanced.JournalLabel() + + if !strings.HasSuffix(journal, common.Definitions.Filing.JournalExt) { + journal += common.Definitions.Filing.JournalExt + } + + if !strings.HasPrefix(journal, common.Definitions.Filing.Discriminator) { + journal = common.Definitions.Filing.Discriminator + journal + } + + withoutExt := strings.TrimSuffix(journal, common.Definitions.Filing.JournalExt) + core := strings.TrimPrefix(withoutExt, common.Definitions.Filing.Discriminator) + + f.Stats.Journal = common.JournalMetaInfo{ + Core: core, + Actual: journal, + WithoutExt: withoutExt, + Extension: common.Definitions.Filing.JournalExt, + Discriminator: common.Definitions.Filing.Discriminator, + } } func (f *PathFinder) JournalFullPath(item *nav.TraverseItem) string { - file := f.statics.JournalLocation( + file := f.Stats.JournalLocation( item.Extension.Name, item.Extension.Parent, ) - // ---> fmt.Printf("๐Ÿ”ฅ๐Ÿ”ฅ๐Ÿ”ฅ JOURNAL-FILE: '%v'\n", file) - return file } func (f *PathFinder) Statics() *common.StaticInfo { - return f.statics + return f.Stats } func (f *PathFinder) Scheme() string { - return f.scheme + return f.Sch } // Transfer creates a path for the input; should return empty @@ -236,33 +239,56 @@ func (f *PathFinder) Scheme() string { // is not transparent. When the --Trash option is present, it will // determine the destination path for the input. func (f *PathFinder) Transfer(info *common.PathInfo) (folder, file string) { - to := lo.TernaryF(f.Trash != "", - func() string { - return f.Trash - }, - func() string { - return info.Origin - }, - ) - folder = func() string { - segments := pfTemplates[pfPathInputTransferFolder] + if info.Cuddle { + return info.Origin // should we return empty string here? + } + if info.Output != "" && info.Trash == "" { + // When output folder is specified, then the results will be diverted there. + // This means there is no need to transfer the input, unless trash has + // also been specified. + // + return "" + } + + segments := pfTemplates[pfPathInputTransferFolder] + to := lo.TernaryF(f.Trash != "", + func() string { + return f.Trash + }, + func() string { + return info.Origin + }, + ) + // eventually, we need to use the cuddle option here + // return pfTemplates.evaluate(pfFieldValues{ - "${{INPUT-DESTINATION}}": to, - "${{ITEM-SUB-PATH}}": info.Item.Extension.SubPath, - "${{DEJA-VU}}": DejaVu, - "${{SUPPLEMENT}}": f.supplement(), - "${{TRASH-LABEL}}": f.statics.Trash, + "${{TRANSFER-DESTINATION}}": to, + "${{ITEM-SUB-PATH}}": info.Item.Extension.SubPath, + "${{DEJA-VU}}": f.Stats.TrashTag(), + "${{SUPPLEMENT}}": f.folderProfileSupplement(info.Profile), }, segments...) }() file = func() string { - segments := pfTemplates[pfPathInputDestinationFileOriginalExt] + if info.Output != "" && info.Trash == "" { + // When output folder is specified, then the results will be diverted there. + // This means there is no need to transfer the input, unless trash has + // also been specified. + // + return "" + } - return pfTemplates.evaluate(pfFieldValues{ - "${{ITEM-FULL-NAME}}": info.Item.Extension.Name, - }, segments...) + if info.Cuddle { + supp := fmt.Sprintf("%v.%v", f.Stats.TrashTag(), f.fileProfileSupplement(info.Profile)) + + return SupplementFilename( + info.Item.Extension.Name, supp, f.Stats, + ) + } + + return info.Item.Extension.Name }() return folder, file @@ -297,39 +323,38 @@ func (f *PathFinder) mutateExtension(file string) string { // Result creates a path for each result so should be called by the // execution step func (f *PathFinder) Result(info *common.PathInfo) (folder, file string) { - to := lo.TernaryF(f.Output != "", - func() string { - return f.Output - }, - func() string { - return info.Origin - }, - ) - folder = func() string { - segments := pfTemplates[pfPathInputTransferFolder] - - return lo.TernaryF(f.transparentInput && f.Arity == 1, + return lo.TernaryF(f.transparentInput || info.Cuddle, func() string { // The result file has to be in the same folder // as the input // - segments = pfTemplates[pfPathTxInputDestinationFolder] + segments := pfTemplates[pfPathTxInputDestinationFolder] return pfTemplates.evaluate(pfFieldValues{ "${{OUTPUT-ROOT}}": info.Origin, }, segments...) }, func() string { + segments := pfTemplates[pfPathResultFolder] + to := lo.TernaryF(f.Output != "", + func() string { + return f.Output + }, + func() string { + return info.Origin + }, + ) // If there is no scheme or profile, then the user is - // only relying flags on the command line, ie running adhoc + // only relying on flags on the command line, ie running adhoc // so the result path should include an adhoc label. Otherwise, // the result should reflect the supplementary path. // + return pfTemplates.evaluate(pfFieldValues{ "${{OUTPUT-ROOT}}": to, - "${{SUPPLEMENT}}": f.supplement(), "${{ITEM-SUB-PATH}}": info.Item.Extension.SubPath, + "${{SUPPLEMENT}}": f.folderProfileSupplement(info.Profile), }, segments...) }, ) @@ -339,28 +364,50 @@ func (f *PathFinder) Result(info *common.PathInfo) (folder, file string) { // The file name just matches the input file name. The folder name // provides the context. // - segments := pfTemplates[pfPathResultFile] - - return pfTemplates.evaluate(pfFieldValues{ - "${{RESULT-NAME}}": info.Item.Extension.Name, - }, segments...) + return info.Item.Extension.Name }() return folder, f.mutateExtension(file) } -func (f *PathFinder) supplement() string { - return lo.TernaryF(f.scheme == "" && f.ExplicitProfile == "", +func (f *PathFinder) folderProfileSupplement(profile string) string { + return lo.TernaryF(f.Sch == "" && profile == "", func() string { - adhocLabel := f.statics.Adhoc + adhocLabel := f.Stats.Adhoc return adhocLabel }, func() string { - return filepath.Join(f.scheme, f.ExplicitProfile) + return filepath.Join(f.Sch, profile) }, ) } +func (f *PathFinder) fileProfileSupplement(profile string) string { + var ( + result string + ) + + switch { + case f.Sch != "" && profile != "": + result = fmt.Sprintf("%v.%v", f.Sch, profile) + + case f.Sch != "": + result = f.Sch + + case profile != "": + result = profile + + default: + result = f.Stats.Adhoc + } + + return result +} + func (f *PathFinder) TransparentInput() bool { return f.transparentInput } + +func (f *PathFinder) Observe(t common.PathFinder) common.PathFinder { + return t +} diff --git a/src/app/proxy/filing/path-finder_test.go b/src/app/proxy/filing/path-finder_test.go new file mode 100644 index 0000000..0da6581 --- /dev/null +++ b/src/app/proxy/filing/path-finder_test.go @@ -0,0 +1,546 @@ +package filing_test + +import ( + "fmt" + "path/filepath" + "strings" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/snivilised/extendio/xfs/nav" + "github.com/snivilised/pixa/src/app/cfg" + "github.com/snivilised/pixa/src/app/proxy/common" + "github.com/snivilised/pixa/src/app/proxy/filing" +) + +type supplements struct { + folder string + file string +} + +type reasons = supplements + +type asserter func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) + +type pfTE struct { + given string + should string + reasons supplements + scheme string + profile string + supplements supplements + output string + trash string + cuddle bool + dry bool + actionTransfer bool + assert asserter +} + +func because(reason string, extras ...string) string { + if len(extras) == 0 { + return fmt.Sprintf("๐Ÿ”ฅ %v", reason) + } + + return fmt.Sprintf("๐Ÿ”ฅ %v (%v)", reason, strings.Join(extras, ",")) +} + +var _ = Describe("PathFinder", Ordered, func() { + var ( + advanced *cfg.MsAdvancedConfig + schemes *cfg.MsSchemesConfig + ) + + BeforeAll(func() { + schemes = &cfg.MsSchemesConfig{ + "blur-sf": &cfg.MsSchemeConfig{ + ProfilesData: []string{"blur", "sf"}, + }, + "adaptive-sf": &cfg.MsSchemeConfig{ + ProfilesData: []string{"adaptive", "sf"}, + }, + "adaptive-blur": &cfg.MsSchemeConfig{ + ProfilesData: []string{"adaptive", "blur"}, + }, + "singleton": &cfg.MsSchemeConfig{ + ProfilesData: []string{"adaptive"}, + }, + } + + advanced = &cfg.MsAdvancedConfig{ + Abort: false, + LabelsCFG: cfg.MsLabelsConfig{ + Adhoc: "ADHOC", + Journal: "journal", + Legacy: ".LEGACY", + Trash: "TRASH", + Fake: ".FAKE", + Supplement: "SUPP", + }, + ExtensionsCFG: cfg.MsExtensionsConfig{ + FileSuffixes: "jpg,jpeg,png", + TransformsCSV: "lower", + Remap: map[string]string{ + "jpeg": "jpg", + }, + }, + ExecutableCFG: cfg.MsExecutableConfig{ + ProgramName: "dummy", + Timeout: "1s", + NoProgramRetries: 3, + }, + } + }) + + DescribeTable("core", + func(entry *pfTE) { + finder := filing.NewFinder(&filing.NewFinderInfo{ + Advanced: advanced, + Schemes: schemes, + Scheme: entry.scheme, + OutputPath: entry.output, + TrashPath: entry.trash, + DryRun: entry.dry, + }) + + origin := filepath.Join("foo", "sessions", "scan01") + pi := &common.PathInfo{ + Item: &nav.TraverseItem{ + Path: origin, + Extension: nav.ExtendedItem{ + Name: "01_Backyard-Worlds-Planet-9_s01.jpg", + }, + }, + Origin: origin, + Profile: entry.profile, + Scheme: entry.scheme, + Cuddle: entry.cuddle, + Output: entry.output, + Trash: entry.trash, + } + + var ( + folder, file string + ) + + if entry.actionTransfer { + folder, file = finder.Transfer(pi) + } else { + folder, file = finder.Result(pi) + } + entry.assert(folder, file, pi, finder.Statics(), entry) + }, + func(entry *pfTE) string { + return fmt.Sprintf("๐Ÿงช ===> given: '%v', should: '%v'", + entry.given, entry.should, + ) + }, + + // + // === TRANSPARENT / PROFILE + // + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/profile/non-cuddled (๐ŸŽฏ @TID-CORE-1_TR-PR-NC_T)", + should: "redirect input to supplemented folder // filename not modified", + reasons: reasons{ + folder: "transparency, result should take place of input in same folder", + file: "file should be moved out of the way and not cuddled", + }, + profile: "blur", + supplements: supplements{ + folder: filepath.Join("$TRASH$", "blur"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(filepath.Join(pi.Origin, entry.supplements.folder)), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + Entry(nil, &pfTE{ + given: "๐ŸŽ RESULT: transparent/profile (๐ŸŽฏ @TID-CORE-2_TR-PR-NC_R)", + should: "not modify folder // not modify filename", + reasons: reasons{ + folder: "transparency, result should take place of input", + file: "file should be moved out of the way and not cuddled", + }, + profile: "blur", + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(pi.Origin), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/profile/cuddled (๐ŸŽฏ @TID-CORE-3_TR-PR-CU_T)", + should: "not modify folder // file decorated with supplement", + reasons: reasons{ + folder: "not modify folder to enable cuddle", + file: "cuddled file needs to be disambiguated from the input", + }, + profile: "blur", + supplements: supplements{ + file: fmt.Sprintf("%v.%v", "$TRASH$", "blur"), + }, + actionTransfer: true, + cuddle: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(pi.Origin), because(entry.reasons.folder)) + supplemented := filing.SupplementFilename( + pi.Item.Extension.Name, entry.supplements.file, statics, + ) + Expect(file).To(Equal(supplemented), because(entry.reasons.file, file)) + }, + }), + + Entry(nil, &pfTE{ + given: "๐ŸŽ RESULT: transparent/profile/cuddled (๐ŸŽฏ @TID-CORE-4_TR-PR-CU_R)", + should: "not modify folder // not modify filename", + reasons: reasons{ + folder: "not modify folder to enable cuddle", + file: "cuddled result file needs to replace the input", + }, + profile: "blur", + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(pi.Origin), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // === TRANSPARENT / SCHEME (non-cuddle) [BLUE] + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/scheme/non-cuddled (๐ŸŽฏ @TID-CORE-5_TR-SC-NC_BLUR_T)", + should: "redirect input to supplemented folder // filename not modified", + reasons: reasons{ + folder: "transparency, result should take place of input in same folder", + file: "file should be moved out of the way and not cuddled", + }, + scheme: "blur-sf", + profile: "blur", + supplements: supplements{ + folder: filepath.Join("$TRASH$", "blur-sf", "blur"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(filepath.Join(pi.Origin, entry.supplements.folder)), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // ... + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/scheme/non-cuddled (๐ŸŽฏ @TID-CORE-6_TR-SC-NC_SF_T)", + should: "redirect input to supplemented folder // filename not modified", + reasons: reasons{ + folder: "transparency, result should take place of input in same folder", + file: "file should be moved out of the way and not cuddled", + }, + scheme: "blur-sf", + profile: "sf", + supplements: supplements{ + folder: filepath.Join("$TRASH$", "blur-sf", "sf"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(filepath.Join(pi.Origin, entry.supplements.folder)), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // === TRANSPARENT / SCHEME (cuddle) [GREEN] + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/scheme/cuddled (๐ŸŽฏ @TID-CORE-7_TR-SC-CU_BLUR_T)", + should: "not modify folder // file decorated with supplement", + reasons: reasons{ + folder: "not modify folder to enable cuddle", + file: "cuddled file needs to be disambiguated from the input", + }, + scheme: "blur-sf", + profile: "blur", + supplements: supplements{ + folder: "", + file: "$TRASH$.blur-sf.blur", + }, + actionTransfer: true, + cuddle: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(pi.Origin), because(entry.reasons.folder)) + supplemented := filing.SupplementFilename( + pi.Item.Extension.Name, entry.supplements.file, statics, + ) + Expect(file).To(Equal(supplemented), because(entry.reasons.file, file)) + }, + }), + + // ... + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/scheme/cuddled (๐ŸŽฏ @TID-CORE-8_TR-SC-CU_SF_T)", + should: "not modify folder // file decorated with supplement", + reasons: reasons{ + folder: "not modify folder to enable cuddle", + file: "cuddled file needs to be disambiguated from the input", + }, + scheme: "blur-sf", + profile: "sf", + supplements: supplements{ + folder: "", + file: "$TRASH$.blur-sf.sf", + }, + actionTransfer: true, + cuddle: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(pi.Origin), because(entry.reasons.folder)) + supplemented := filing.SupplementFilename( + pi.Item.Extension.Name, entry.supplements.file, statics, + ) + Expect(file).To(Equal(supplemented), because(entry.reasons.file)) + }, + }), + + // + // === TRANSPARENT / ADHOC + // + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/adhoc/non-cuddled (๐ŸŽฏ @TID-CORE-9_TR-AD-NC_SF_T)", + should: "redirect input to supplemented folder // filename not modified", + reasons: reasons{ + folder: "transparency, result should take place of input in same folder", + file: "file should be moved out of the way and not cuddled", + }, + supplements: supplements{ + folder: filepath.Join("$TRASH$", "ADHOC"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(filepath.Join(pi.Origin, entry.supplements.folder)), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + Entry(nil, &pfTE{ + given: "๐ŸŽ RESULT: transparent/adhoc (๐ŸŽฏ @TID-CORE-10_TR-AD_R)", + should: "not modify folder // not modify filename", + reasons: reasons{ + folder: "transparency, result should take place of input", + file: "file should be moved out of the way and not cuddled", + }, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(pi.Origin), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // + // TRANSPARENT --trash SPECIFIED + // + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/profile/trash (๐ŸŽฏ @TID-CORE-11_TR-PR-TRA_T)", + should: "redirect input to supplemented folder // filename not modified", + reasons: reasons{ + folder: "transparency, result should take place of input in same folder", + file: "file should be moved out of the way and not cuddled", + }, + profile: "blur", + trash: filepath.Join("foo", "sessions", "scan01", "rubbish"), + supplements: supplements{ + folder: filepath.Join("$TRASH$", "blur"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(filepath.Join(entry.trash, entry.supplements.folder)), because(entry.reasons.folder, folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + Entry(nil, &pfTE{ + given: "๐ŸŽ RESULT: transparent/profile/trash (๐ŸŽฏ @TID-CORE-12_TR-PR-TRA_R)", + should: "not modify folder // not modify filename", + reasons: reasons{ + folder: "transparency, result should take place of input", + file: "file should be moved out of the", + }, + profile: "blur", + trash: filepath.Join("foo", "sessions", "scan01", "rubbish"), + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(pi.Origin), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // === TRANSPARENT / SCHEME (non-cuddle) [BLUE] + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/scheme/trash (๐ŸŽฏ @TID-CORE-13_TR-SC-TRA_BLUR_T)", + should: "redirect input to supplemented folder // filename not modified", + reasons: reasons{ + folder: "transparency, result should take place of input in same folder", + file: "file should be moved out of the way", + }, + scheme: "blur-sf", + profile: "blur", + trash: filepath.Join("foo", "sessions", "scan01", "rubbish"), + supplements: supplements{ + folder: filepath.Join("rubbish", "$TRASH$", "blur-sf", "blur"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(filepath.Join(pi.Origin, entry.supplements.folder)), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // ... + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: transparent/scheme/trash (๐ŸŽฏ @TID-CORE-14_TR-SC-TRA_SF_T)", + should: "redirect input to supplemented folder // filename not modified", + reasons: reasons{ + folder: "transparency, result should take place of input in same folder", + file: "file should be moved out of the way", + }, + scheme: "blur-sf", + profile: "sf", + trash: filepath.Join("foo", "sessions", "scan01", "rubbish"), + supplements: supplements{ + folder: filepath.Join("rubbish", "$TRASH$", "blur-sf", "sf"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(Equal(filepath.Join(pi.Origin, entry.supplements.folder)), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // + // NON-TRANSPARENT --output SPECIFIED + // + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: profile/output (๐ŸŽฏ @TID-CORE-15_NT-PR-OUT_T)", + should: "return empty folder and file", + reasons: reasons{ + folder: "no transfer required", + file: "input file left alone", + }, + profile: "blur", + output: filepath.Join("foo", "sessions", "scan01", "results"), + supplements: supplements{ + folder: filepath.Join("$TRASH$", "blur"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(BeEmpty(), because(entry.reasons.folder, folder)) + Expect(file).To(BeEmpty(), because(entry.reasons.file)) + }, + }), + + Entry(nil, &pfTE{ + given: "๐ŸŽ RESULT: profile/output (๐ŸŽฏ @TID-CORE-16_NT-PR-OUT_R)", + should: "redirect result to output // supplement folder // not modify filename", + reasons: reasons{ + folder: "result should be send to supplemented output folder", + file: "filename only needs to match input filename because the folder is supplemented", + }, + profile: "blur", + output: filepath.Join("foo", "sessions", "scan01", "results"), + supplements: supplements{ + folder: "blur", + }, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + supplemented := filing.SupplementFolder( + entry.output, entry.supplements.folder, + ) + Expect(folder).To(Equal(supplemented), because(entry.reasons.folder, folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + + // === NON-TRANSPARENT / SCHEME (non-cuddle) [BLUE] + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: scheme/output (๐ŸŽฏ @TID-CORE-17_NT-SC-OUT_BLUR_T)", + should: "return empty folder and file", + reasons: reasons{ + folder: "no transfer required", + file: "input file left alone", + }, + scheme: "blur-sf", + profile: "blur", + output: filepath.Join("foo", "sessions", "scan01", "results"), + supplements: supplements{ + folder: filepath.Join("rubbish", "$TRASH$", "blur-sf", "blur"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(BeEmpty(), because(entry.reasons.folder)) + Expect(file).To(BeEmpty(), because(entry.reasons.file)) + }, + }), + + // ... + + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: scheme/output (๐ŸŽฏ @TID-CORE-18_NT-SC-OUT_SF_T)", + should: "return empty folder and file", + reasons: reasons{ + folder: "no transfer required", + file: "input file left alone", + }, + scheme: "blur-sf", + profile: "sf", + output: filepath.Join("foo", "sessions", "scan01", "results"), + supplements: supplements{ + folder: filepath.Join("rubbish", "$TRASH$", "blur-sf", "sf"), + }, + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(BeEmpty(), because(entry.reasons.folder)) + Expect(file).To(BeEmpty(), because(entry.reasons.file)) + }, + }), + + // + // === NON-TRANSPARENT / ADHOC + // + Entry(nil, &pfTE{ + given: "๐ŸŒ€ TRANSFER: adhoc/output (๐ŸŽฏ @TID-CORE-19_NT-AD-OUT_SF_T)", + should: "return empty folder and file", + reasons: reasons{ + folder: "no transfer required", + file: "input file left alone", + }, + output: filepath.Join("foo", "sessions", "scan01", "results"), + actionTransfer: true, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + Expect(folder).To(BeEmpty(), because(entry.reasons.folder)) + Expect(file).To(BeEmpty(), because(entry.reasons.file)) + }, + }), + + Entry(nil, &pfTE{ + given: "๐ŸŽ RESULT: adhoc/output (๐ŸŽฏ @TID-CORE-20_NT-AD-OUT_SF_R)", + should: "redirect result to output // supplement folder // not modify filename", + reasons: reasons{ + folder: "result should be send to supplemented output folder", + file: "filename only needs to match input filename because the folder is supplemented", + }, + output: filepath.Join("foo", "sessions", "scan01", "results"), + supplements: supplements{ + folder: "ADHOC", + }, + assert: func(folder, file string, pi *common.PathInfo, statics *common.StaticInfo, entry *pfTE) { + supplemented := filing.SupplementFolder( + entry.output, entry.supplements.folder, + ) + Expect(folder).To(Equal(supplemented), because(entry.reasons.folder)) + Expect(file).To(Equal(pi.Item.Extension.Name), because(entry.reasons.file)) + }, + }), + ) +}) diff --git a/src/app/proxy/orc/controller-step.go b/src/app/proxy/orc/controller-step.go index 94c12fc..372669b 100644 --- a/src/app/proxy/orc/controller-step.go +++ b/src/app/proxy/orc/controller-step.go @@ -22,14 +22,9 @@ type controllerStep struct { // Run func (s *controllerStep) Run(pi *common.PathInfo) error { + pi.Profile = s.profile folder, file := s.session.FileManager.Finder().Result(pi) destination := filepath.Join(folder, file) - - // if transparent, then we need to ask the fm to move the - // existing file out of the way. But shouldn't that already have happened - // during setup? See, which mean setup in not working properly in - // this scenario. - err := s.session.Agent.Invoke( s.thirdPartyCL, pi.RunStep.Source, destination, ) diff --git a/src/app/proxy/orc/controller.go b/src/app/proxy/orc/controller.go index df62afc..98c271c 100644 --- a/src/app/proxy/orc/controller.go +++ b/src/app/proxy/orc/controller.go @@ -34,6 +34,9 @@ func (c *Controller) OnNewShrinkItem(item *nav.TraverseItem) error { Scheme: c.session.Inputs.Root.ProfileFam.Native.Scheme, Profile: c.session.Inputs.Root.ProfileFam.Native.Profile, Origin: item.Extension.Parent, + Cuddle: c.session.Inputs.ParamSet.Native.Cuddle, + Output: c.session.Inputs.ParamSet.Native.OutputPath, + Trash: c.session.Inputs.ParamSet.Native.TrashPath, } var sequence common.Sequence @@ -129,6 +132,7 @@ func (c *Controller) Run(item *nav.TraverseItem, sequence common.Sequence) error // profile here on pi not set when profile set on command line // but thats ok, since a profile sequence is created and the // executive step itself does have the profile. + // return step.Run(&c.private.Pi) } while := func(_ common.Step, e error) bool { @@ -146,6 +150,9 @@ func (c *Controller) Run(item *nav.TraverseItem, sequence common.Sequence) error Item: item, Origin: item.Parent.Path, Scheme: c.session.FileManager.Finder().Scheme(), + Cuddle: c.session.Inputs.ParamSet.Native.Cuddle, + Output: c.session.Inputs.ParamSet.Native.OutputPath, + Trash: c.session.Inputs.ParamSet.Native.TrashPath, } // TODO: need to decide a proper policy for cleaning up diff --git a/src/app/proxy/path-finder-observer_test.go b/src/app/proxy/path-finder-observer_test.go new file mode 100644 index 0000000..3955da1 --- /dev/null +++ b/src/app/proxy/path-finder-observer_test.go @@ -0,0 +1,154 @@ +package proxy_test + +import ( + "fmt" + "path/filepath" + + "github.com/samber/lo" + "github.com/snivilised/extendio/xfs/nav" + "github.com/snivilised/extendio/xfs/storage" + "github.com/snivilised/pixa/src/app/proxy/common" + "github.com/snivilised/pixa/src/app/proxy/filing" + "github.com/snivilised/pixa/src/internal/matchers" + + . "github.com/onsi/gomega" +) + +type pathAssertion struct { + file string + folder string +} + +type observerAssertions map[string]*pathAssertion + +type testPathFinderObserver struct { + target common.PathFinder + + transfers observerAssertions + results observerAssertions +} + +// โœจ BEFORE A NON TRANSPARENT RUN, ANY SAMPLE FILES FOUND IN THE SAME DIR AS THE ORIGIN +// SHOULD BE MOVED TO THE --output LOCATION. THE REASON FOR THIS IS THE USER +// MAY PERFORM A TRANSPARENT SAMPLE RUN, FOLLOWED BY A NON TRANSPARENT FULL RUN. IN THIS +// CASE, THE SAMPLE FILES WILL BE IN THE WRONG LOCATION AND SHOULD BE MOVED TO THE --output + +// --output(RESULT) specifies where the result goes to +// --trash(INPUT) specified where the input goes to; also, means not-transparent +// usually, the user would specify either the --output or --input; not both, +// but not prohibited, but not in the user's interest +// sample should follow the output, so that when full run occurs, the sample is easily found, +// and the user can perform direct comparison to the input. the sample is always decorated by +// the supplement, which has an additional '.$sample' label, eg +// 01_Backyard-Worlds-Planet-9_s01.jpg -> 01_Backyard-Worlds-Planet-9_s01.blur-sf.blur.$sample.jpg +// where blur-sf.blur.$sample is the full supplement (scheme=blur-sf;profile=blur;sample=.$sample) + +// combinations out of dry-run/scheme(ar>1)/transparency | input -> ? | output -> +// /null = no move +// //R = output, either --output or --trash +// +// - = NO-MOVE +// X = DOES-NOT-EXIST +// $OR = ORIGIN +// $N = ITEM-NAME +// $S = SUPPLEMENT +// +// WE NEED ANOTHER OPTION, --cuddle. When specified, it tries to keep the output files +// close to the input to enable easier comparison for the user. The user is more likely +// to user --cuddle when performing a sample. When cuddled, the output file name is +// decorated with the supplement; this will disambiguate the output from the input. When +// not cuddled +// +// TRANSPARENCY NOT COMPATIBLE WITH SCHEME(ar>1); CAST IN STONE +// WHEN TRANSPARENT, THE SUPPLEMENT MUST APPLY TO FOLDER AND FILE TO AVOID NAME CLASH +// +// We only need to decorate the filename, when cuddle is active, because the input file +// is in the same location as the output file. When cuddle is not active, then the folder +// is decorated with the directory supplement, therefore no class arises, so there is +// no need for a file supplement. The supplement function should be passed the cuddle +// flag so that it can encode this logic internally. +// +// +// TRANSP | SCHEME | DRY | SAMPLE | CUDDLE |TRANSFER(--trash) | RESULT(--output) +// -------------|---------|---------|---------|--------|----------------------------------|----------------------------------------- +// YES | NO | NO | NO | NO | - //OR/$N.$S | //OR/$N +// YES | YES | YES | NO | NO | input > /null | output > /null + +func (o *testPathFinderObserver) assert(entry *samplerTE, + origin string, vfs storage.VirtualFS, +) { + if entry.supplements.file != "do not enter" { + return + } + + first := lo.Keys(o.transfers)[0] + fmt.Printf("\n ๐Ÿ“‚ FOLDER: '%v'\n", o.transfers[first].folder) + + for _, v := range o.transfers { + if o.TransparentInput() { + // input should just be renamed with supplement in the origin folder + // + statics := o.target.Statics() + + originalPath := filepath.Join(origin, + filing.SupplementFilename(v.file, entry.supplements.file, statics), + ) + Expect(matchers.AsFile(originalPath)).To(matchers.ExistInFS(vfs)) + } + } + + for _, v := range o.results { + if o.TransparentInput() { // ?? CHECK-VALID FOR OUTPUT? + // result should take the place of input + // + // directory + // intermediate + // name + // + originalPath := filepath.Join(origin, v.file) + Expect(matchers.AsFile(originalPath)).To(matchers.ExistInFS(vfs)) + } + } +} + +func (o *testPathFinderObserver) Transfer(info *common.PathInfo) (folder, file string) { + folder, file = o.target.Transfer(info) + o.transfers[info.Item.Extension.Name] = &pathAssertion{ + folder: folder, + file: file, + } + + return folder, file +} + +func (o *testPathFinderObserver) Result(info *common.PathInfo) (folder, file string) { + folder, file = o.target.Result(info) + o.results[info.Item.Extension.Name] = &pathAssertion{ + folder: folder, + file: file, + } + + return folder, file +} + +func (o *testPathFinderObserver) TransparentInput() bool { + return o.target.TransparentInput() +} + +func (o *testPathFinderObserver) JournalFullPath(item *nav.TraverseItem) string { + return o.target.JournalFullPath(item) +} + +func (o *testPathFinderObserver) Statics() *common.StaticInfo { + return o.target.Statics() +} + +func (o *testPathFinderObserver) Scheme() string { + return o.target.Scheme() +} + +func (o *testPathFinderObserver) Observe(t common.PathFinder) common.PathFinder { + o.target = t + + return o +} diff --git a/src/app/proxy/pixa_test.go b/src/app/proxy/pixa_test.go index 7766f60..d894055 100644 --- a/src/app/proxy/pixa_test.go +++ b/src/app/proxy/pixa_test.go @@ -31,8 +31,14 @@ func openInputTTY() (*os.File, error) { const ( BackyardWorldsPlanet9Scan01 = "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01" BackyardWorldsPlanet9Scan02 = "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-02" + DejaVu = "$TRASH$" ) +type supplements struct { + file string + directory string +} + type controllerTE struct { given string should string @@ -40,14 +46,14 @@ type controllerTE struct { args []string isTui bool dry bool + intermediate string withFake bool outputFlag string trashFlag string profile string relative string mandatory []string - intermediate string - supplement string + supplements supplements inputs []string } @@ -63,7 +69,7 @@ func augment(entry *samplerTE, result = append(result, entry.args...) if entry.exists { - location := filepath.Join(directory, entry.intermediate, entry.supplement) + location := filepath.Join(directory, entry.intermediate, entry.supplements.directory) if err := vfs.MkdirAll(location, common.Permissions.Write); err != nil { Fail(errors.Wrap(err, err.Error()).Error()) } @@ -86,11 +92,15 @@ func augment(entry *samplerTE, return result } -func assertInFs(entry *samplerTE, bs *command.Bootstrap, directory string) { +func assertInFs(entry *samplerTE, + bs *command.Bootstrap, + directory string, + observer *testPathFinderObserver, +) { vfs := bs.Vfs - if entry.mandatory != nil && entry.dry { - dejaVu := filepath.Join(common.Definitions.Filing.DejaVu, entry.supplement) + if entry.mandatory != nil && entry.dry { // + dejaVu := filepath.Join(DejaVu, entry.supplements.directory) supplement := helpers.Path(entry.intermediate, dejaVu) for _, original := range entry.mandatory { @@ -104,6 +114,8 @@ func assertInFs(entry *samplerTE, bs *command.Bootstrap, directory string) { Expect(matchers.AsFile(originalPath)).To(matchers.ExistInFS(vfs)) } } + + observer.assert(entry, directory, vfs) } var _ = Describe("pixa", Ordered, func() { @@ -140,27 +152,33 @@ var _ = Describe("pixa", Ordered, func() { DescribeTable("interactive", func(entry *samplerTE) { - directory := helpers.Path(root, entry.relative) + origin := helpers.Path(root, entry.relative) args := augment(entry, []string{ - common.Definitions.Commands.Shrink, directory, + common.Definitions.Commands.Shrink, origin, }, - vfs, root, directory, + vfs, root, origin, ) + observer := &testPathFinderObserver{ + transfers: make(observerAssertions), + results: make(observerAssertions), + } + bootstrap := command.Bootstrap{ Vfs: vfs, Presentation: common.PresentationOptions{ WithoutRenderer: withoutRenderer, }, + Observers: common.Observers{ + PathFinder: observer, + }, } if !entry.isTui { args = append(args, "--no-tui") } - fmt.Printf("======> XTERM: '%v'\n", os.Getenv("XTERM")) - tester := helpers.CommandTester{ Args: args, Root: bootstrap.Root(func(co *command.ConfigureOptionsInfo) { @@ -176,7 +194,7 @@ var _ = Describe("pixa", Ordered, func() { fmt.Sprintf("execution result non nil (%v)", err), ) - assertInFs(entry, &bootstrap, directory) + assertInFs(entry, &bootstrap, origin, observer) }, func(entry *samplerTE) string { return fmt.Sprintf("๐Ÿงช ===> given: '%v', should: '%v'", @@ -188,9 +206,9 @@ var _ = Describe("pixa", Ordered, func() { // // full run - Entry(nil, &samplerTE{ + XEntry(nil, &samplerTE{ controllerTE: controllerTE{ - given: "full run transparent adhoc, with ex-glob", + given: "full/transparent/adhoc/ex-glob", should: "full run with ex-glob filter, result file takes place of input", relative: BackyardWorldsPlanet9Scan01, args: []string{ @@ -200,8 +218,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + file: "ADHOC.TRASH", + directory: "$pixa$/ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -217,8 +238,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -235,8 +259,13 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + file: "adaptive", + directory: "adaptive/TRASH", + }, + + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -254,8 +283,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -272,8 +304,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "singleton/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "singleton/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -290,8 +325,11 @@ var _ = Describe("pixa", Ordered, func() { trashFlag: "discard", mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "discard", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -309,8 +347,11 @@ var _ = Describe("pixa", Ordered, func() { trashFlag: "discard", mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "discard", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -328,8 +369,11 @@ var _ = Describe("pixa", Ordered, func() { trashFlag: "discard", mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "discard", - supplement: "singleton/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + file: "$SUPP/ADHOC.TRASH", + directory: "singleton/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -347,8 +391,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "blur-sf/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "blur-sf/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -365,8 +412,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -382,8 +432,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan02, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-02", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan02, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan02, }, }), @@ -403,8 +456,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -423,8 +479,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -444,8 +503,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01Last4, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01Last4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01Last4, }, }), @@ -461,8 +523,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First2, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First2, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First2, }, }), @@ -482,8 +547,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -502,8 +570,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "singleton/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "singleton/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -522,8 +593,11 @@ var _ = Describe("pixa", Ordered, func() { trashFlag: "discard", mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "discard", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -543,8 +617,11 @@ var _ = Describe("pixa", Ordered, func() { trashFlag: "discard", mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "discard", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -564,8 +641,11 @@ var _ = Describe("pixa", Ordered, func() { trashFlag: "discard", mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "discard", - supplement: "singleton/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "singleton/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -585,8 +665,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "blur-sf/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "blur-sf/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -605,8 +688,11 @@ var _ = Describe("pixa", Ordered, func() { }, mandatory: helpers.BackyardWorldsPlanet9Scan01First4, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First4, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First4, }, }), @@ -626,8 +712,11 @@ var _ = Describe("pixa", Ordered, func() { withFake: true, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -645,8 +734,11 @@ var _ = Describe("pixa", Ordered, func() { withFake: true, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -665,8 +757,11 @@ var _ = Describe("pixa", Ordered, func() { withFake: true, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "adaptive/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "adaptive/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), @@ -704,8 +799,11 @@ var _ = Describe("pixa", Ordered, func() { isTui: true, mandatory: helpers.BackyardWorldsPlanet9Scan01First6, intermediate: "nasa/exo/Backyard Worlds - Planet 9/sessions/scan-01", - supplement: "ADHOC/TRASH", - inputs: helpers.BackyardWorldsPlanet9Scan01First6, + supplements: supplements{ + // file: "$SUPP/ADHOC.TRASH", + directory: "ADHOC/TRASH", + }, + inputs: helpers.BackyardWorldsPlanet9Scan01First6, }, }), ) diff --git a/src/app/proxy/user/model.go b/src/app/proxy/user/model.go index 501b306..6d0afc2 100644 --- a/src/app/proxy/user/model.go +++ b/src/app/proxy/user/model.go @@ -150,7 +150,7 @@ type bodyContent struct { emoji string } -func (bc *bodyContent) render() string { +func (bc *bodyContent) view() string { from, source := filepath.Split(bc.source) enriched := fmt.Sprintf("%v %v", bc.emoji, source) to, destination := filepath.Split(bc.destination) @@ -195,7 +195,7 @@ func (m *model) View() string { `, m.spinner.View(), executable, dry, m.status, info, - bc.render(), + bc.view(), e, m.level, m.workload, ) diff --git a/src/app/proxy/user/ui-linear.go b/src/app/proxy/user/ui-linear.go index 3aed0d8..0814f66 100644 --- a/src/app/proxy/user/ui-linear.go +++ b/src/app/proxy/user/ui-linear.go @@ -49,7 +49,7 @@ func (ui *linearUI) Tick(msg *common.ProgressMsg) { ` === %v`, - bc.render(), + bc.view(), ) } diff --git a/src/i18n/messages-command.go b/src/i18n/messages-command.go index fdc927e..0eb8057 100644 --- a/src/i18n/messages-command.go +++ b/src/i18n/messages-command.go @@ -240,6 +240,20 @@ func (td ShrinkCmdTrashPathParamUsageTemplData) Message() *i18n.Message { } } +// ShrinkCmdCuddleParamUsageTemplData +// ๐ŸงŠ +type ShrinkCmdCuddleParamUsageTemplData struct { + pixaTemplData +} + +func (td ShrinkCmdCuddleParamUsageTemplData) Message() *i18n.Message { + return &i18n.Message{ + ID: "shrink-cmd-cuddle.param-usage", + Description: "cuddle specifies that output files to be kept in the same directory as input", + Other: "cuddle specifies that output files to be kept in the same directory as input", + } +} + // ShrinkCmdShortDefinitionTemplData // ๐ŸงŠ type ShrinkCmdShortDefinitionTemplData struct { diff --git a/test/data/configuration/pixa-test.yml b/test/data/configuration/pixa-test.yml index 204ee12..03b7825 100644 --- a/test/data/configuration/pixa-test.yml +++ b/test/data/configuration/pixa-test.yml @@ -32,6 +32,7 @@ advanced: journal-suffix: journal trash: TRASH fake: .FAKE + supplement: SUPP extensions: suffixes-csv: "jpg,jpeg,png" transforms-csv: lower