diff --git a/.vscode/settings.json b/.vscode/settings.json index e5a0d06..8c2f53f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,6 +7,7 @@ "bodyclose", "cubiest", "deadcode", + "deepcopy", "depguard", "dogsled", "dupl", @@ -29,7 +30,9 @@ "linters", "lintv", "Magick", + "mohae", "nakedret", + "nolint", "nolintlint", "Pixa", "prealloc", diff --git a/go.mod b/go.mod index 9c28f63..a4baf3f 100644 --- a/go.mod +++ b/go.mod @@ -6,7 +6,7 @@ require ( github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.10 github.com/samber/lo v1.38.1 - github.com/snivilised/extendio v0.1.16 + github.com/snivilised/extendio v0.1.20 github.com/spf13/cobra v1.7.0 github.com/spf13/viper v1.16.0 go.uber.org/zap v1.25.0 @@ -18,6 +18,7 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/google/go-cmp v0.5.9 // indirect github.com/google/pprof v0.0.0-20230406165453-00490a63f317 // indirect + github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect github.com/onsi/ginkgo v1.16.5 // indirect github.com/pkg/errors v0.9.1 // indirect go.uber.org/multierr v1.11.0 // indirect diff --git a/go.sum b/go.sum index 22405c8..77b97af 100644 --- a/go.sum +++ b/go.sum @@ -157,6 +157,7 @@ github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3v github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw= +github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM= github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk= github.com/nicksnyder/go-i18n/v2 v2.2.1 h1:aOzRCdwsJuoExfZhoiXHy4bjruwCMdt5otbYojM/PaA= @@ -191,6 +192,8 @@ github.com/snivilised/cobrass v0.3.0 h1:ByvHGg59ctNl+OD+qCINC6vl0QtUNtZjR1ChX3V9 github.com/snivilised/cobrass v0.3.0/go.mod h1:5pf0tiAz1mWb7h+QDm/CJtukO3HXPQoKjQKhUnyX980= github.com/snivilised/extendio v0.1.16 h1:pMu1ZFKDZwRl/DB2QHL9jH02JaZMm0ax+ZZ3AZxLUUU= github.com/snivilised/extendio v0.1.16/go.mod h1:SO4udqtdfNl/KTLG9GF8kHxoMiIhnvN/9xlqkCCC6VY= +github.com/snivilised/extendio v0.1.20 h1:pEnWUHFBVf62EaDWyG2dYsJdwiyWCs/GN8pQBlBd4G4= +github.com/snivilised/extendio v0.1.20/go.mod h1:60Q6KRprh4qD7+aDXdlnKkXSgcmwsEV91OPO7j2CaEI= github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM= github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= diff --git a/src/app/command/shrink-cmd.go b/src/app/command/shrink-cmd.go index a872f57..a4a14de 100644 --- a/src/app/command/shrink-cmd.go +++ b/src/app/command/shrink-cmd.go @@ -2,6 +2,7 @@ package command import ( "fmt" + "path/filepath" "strings" "github.com/snivilised/cobrass/src/assistant" @@ -95,15 +96,18 @@ func buildShrinkCommand(container *assistant.CobraContainer) *cobra.Command { fmt.Printf("๐Ÿ’  Blur defined with value: '%v'\n", cmd.Flag("sampling-factor").Value) } + ps.Native.Directory = args[0] + if absolute, absErr := filepath.Abs(args[0]); absErr == nil { + ps.Native.Directory = absolute + } + // Get inherited parameters // rps := container.MustGetParamSet(RootPsName).(magick.RootParameterSetPtr) //nolint:errcheck // is Must call - _ = rps // ---> execute application core with the parameter set (native) // - // appErr = runApplication(native) - // + appErr = magick.EnterShrink(rps, ps) } else { return xvErr } @@ -241,8 +245,38 @@ func buildShrinkCommand(container *assistant.CobraContainer) *cobra.Command { }, ) + // ๐Ÿ“ŒA note about cobra args validation: cmd.ValidArgs lets you define + // a list of all allowable tokens for positional args. Just define + // ValidArgs, eg: + // shrinkCommand.ValidArgs = []string{"foo", "bar", "baz"} + // and then set the args validation function cmd.Args to cobra.OnlyValidArgs + // ie: + // shrinkCommand.Args = cobra.OnlyValidArgs + // With this in place, the user can only type positional args which are in + // the set defined, ie {"foo", "bar", "baz"}. + // + // Since the shrink command only needs a single 'directory' positional arg, + // all we need is to set the exact no of args to 1. We don;t need to define + // ValidArgs since there is no closed set directories we can define. ValidArgs + // is suitable when all positional args can behave like an enum, where there + // is a finite set of valid values. + // container.MustRegisterRootedCommand(shrinkCommand) container.MustRegisterParamSet(shrinkPsName, paramSet) + shrinkCommand.Args = func(cmd *cobra.Command, args []string) error { + if err := cobra.ExactArgs(1)(cmd, args); err != nil { + return err + } + + directory := args[0] + + if !utils.Exists(directory) { + return xi18n.NewPathNotFoundError("shrink directory", directory) + } + + return nil + } + return shrinkCommand } diff --git a/src/app/command/shrink-cmd_test.go b/src/app/command/shrink-cmd_test.go index 4f5df88..b489632 100644 --- a/src/app/command/shrink-cmd_test.go +++ b/src/app/command/shrink-cmd_test.go @@ -22,9 +22,10 @@ type commandTE struct { type shrinkTE struct { commandTE + directory string } -func coreShrinkCmdTest(entry *shrinkTE) { +func expectValidShrinkCmdInvocation(entry *shrinkTE) { bootstrap := command.Bootstrap{ Detector: &DetectorStub{}, } @@ -33,7 +34,9 @@ func coreShrinkCmdTest(entry *shrinkTE) { prog = "shrink" ) - options := append([]string{prog}, []string{ + // we also prepend the directory name to the command line + // + options := append([]string{prog, entry.directory}, []string{ "--preview", "--mode", "tidy", }...) @@ -85,7 +88,11 @@ var _ = Describe("ShrinkCmd", Ordered, func() { DescribeTable("ShrinkCmd", func(entry *shrinkTE) { - coreShrinkCmdTest(entry) + // set directory here, because during discovery phase of unit test , + // l10nPath is not set, so we can't set it inside the Entry + // + entry.directory = l10nPath + expectValidShrinkCmdInvocation(entry) }, func(entry *shrinkTE) string { return fmt.Sprintf("๐Ÿงช ===> given: '%v'", entry.message) @@ -95,7 +102,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "vanilla with long form options", args: []string{ - "source.jpg", "--strip", "--interlace", "plane", "--quality", "85", "result.jpg", + "--strip", "--interlace", "plane", "--quality", "85", }, }, }), @@ -104,7 +111,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "vanilla with short form options", args: []string{ - "source.jpg", "-s", "-i", "plane", "-q", "85", "result.jpg", + "-s", "-i", "plane", "-q", "85", }, }, }), @@ -113,7 +120,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "blur with long form options", args: []string{ - "source.jpg", "result.jpg", "--strip", "--interlace", "plane", "--quality", "85", + "--strip", "--interlace", "plane", "--quality", "85", "--gaussian-blur", "0.85", }, }, @@ -123,7 +130,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "blur with short form options", args: []string{ - "source.jpg", "result.jpg", "-s", "-i", "plane", "-q", "85", + "-s", "-i", "plane", "-q", "85", "-b", "0.85", }, }, @@ -133,7 +140,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "sampling factor with long form options", args: []string{ - "source.jpg", "result.jpg", "--strip", "--interlace", "plane", "--quality", "85", + "--strip", "--interlace", "plane", "--quality", "85", "--sampling-factor", "4:2:0", }, }, @@ -143,7 +150,7 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "sampling factor with short form options", args: []string{ - "source.jpg", "result.jpg", "-s", "-i", "Plane", "-q", "85", + "-s", "-i", "Plane", "-q", "85", "-f", "4:2:0", }, }, @@ -153,7 +160,6 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "with long form glob filtering options options", args: []string{ - "source.jpg", "result.jpg", "--folder-gb", "A*", "--files-gb", "*.jpg", }, @@ -164,7 +170,6 @@ var _ = Describe("ShrinkCmd", Ordered, func() { commandTE: commandTE{ message: "with short form regex filtering options options", args: []string{ - "source.jpg", "result.jpg", "--folder-rx", "^A", "--files-rx", "\\.jpg$", }, @@ -178,16 +183,16 @@ var _ = Describe("ShrinkCmd", Ordered, func() { When("with general long form parameters", func() { It("๐Ÿงช should: execute successfully", func() { entry := &shrinkTE{ + directory: l10nPath, commandTE: commandTE{ message: "with general long form parameters", args: []string{ - "source.jpg", "result.jpg", "--mirror-path", l10nPath, }, }, } - coreShrinkCmdTest(entry) + expectValidShrinkCmdInvocation(entry) }) }) }) diff --git a/src/app/magick/enter-shrink.go b/src/app/magick/enter-shrink.go new file mode 100644 index 0000000..1761114 --- /dev/null +++ b/src/app/magick/enter-shrink.go @@ -0,0 +1,110 @@ +package magick + +import ( + "fmt" + "io/fs" + "path/filepath" + + "github.com/samber/lo" + "github.com/snivilised/cobrass/src/assistant" + "github.com/snivilised/extendio/xfs/nav" +) + +type EntryBase struct { + session nav.TraverseSession +} + +func (e *EntryBase) ConfigureOptions(o *nav.TraverseOptions) { + o.Store.FilterDefs = &nav.FilterDefinitions{ + Children: nav.CompoundFilterDef{ + Type: nav.FilterTypeRegexEn, + Description: "Image types supported by pixa", + Pattern: "\\.(jpe?g|png|gif)$", + }, + } +} + +type ShrinkEntry struct { + EntryBase + + rps *assistant.ParamSet[RootParameterSet] + ps *assistant.ParamSet[ShrinkParameterSet] + jobs []string +} + +func (e *ShrinkEntry) ConfigureOptions(o *nav.TraverseOptions) { + o.Notify.OnBegin = func(_ *nav.NavigationState) { + fmt.Printf("===> ๐Ÿ›ก๏ธ beginning traversal ...\n") + } + o.Notify.OnEnd = func(result *nav.TraverseResult) { + fmt.Printf("===> ๐Ÿšฉ finished traversal - folders '%v'\n", + result.Metrics.Count(nav.MetricNoFoldersInvokedEn), + ) + } + o.Callback = nav.LabelledTraverseCallback{ + Label: "Shrink Entry Callback", + Fn: func(item *nav.TraverseItem) error { + depth := item.Extension.Depth + indicator := lo.Ternary(len(item.Children) > 0, "โ˜€๏ธ", "๐ŸŒŠ") + + lo.ForEach(item.Children, func(de fs.DirEntry, index int) { + fullPath := filepath.Join(item.Path, de.Name()) + e.jobs = append(e.jobs, fullPath) + }) + + fmt.Printf( + "---> %v SHRINK-CALLBACK: (depth:%v, files:%v) '%v'\n", + indicator, + depth, len(item.Children), + item.Path, + ) + + return nil + }, + } + o.Store.Subscription = nav.SubscribeFoldersWithFiles + o.Store.DoExtend = true + e.EntryBase.ConfigureOptions(o) +} + +func Configure(entry *ShrinkEntry, o *nav.TraverseOptions) { + entry.ConfigureOptions(o) +} + +func GetTraverseOptionsFunc(entry *ShrinkEntry) func(o *nav.TraverseOptions) { + // make this a generic? + // + return func(o *nav.TraverseOptions) { + Configure(entry, o) + } +} + +func EnterShrink( + rps *assistant.ParamSet[RootParameterSet], + ps *assistant.ParamSet[ShrinkParameterSet], +) error { + fmt.Printf("---> ๐Ÿฆ ๐Ÿฆ ๐Ÿฆ  Directory: '%v'\n", ps.Native.Directory) + + entry := &ShrinkEntry{ + rps: rps, + ps: ps, + } + session := &nav.PrimarySession{ + Path: ps.Native.Directory, + OptionFn: GetTraverseOptionsFunc(entry), + } + _, err := session.Init().Run() + + lo.ForEach(entry.jobs, func(i string, index int) { + fmt.Printf(" ===> โœจ job: '%v'\n", i) + }) + + summary := fmt.Sprintf("files: %v", len(entry.jobs)) + message := lo.Ternary(err == nil, + fmt.Sprintf("navigation completed (%v) โœ”๏ธ", summary), + fmt.Sprintf("error occurred during navigation (%v)โŒ\n", err), + ) + fmt.Println(message) + + return err +} diff --git a/src/app/magick/image-defs.go b/src/app/magick/image-defs.go index fbfdc3b..1c0a6d3 100644 --- a/src/app/magick/image-defs.go +++ b/src/app/magick/image-defs.go @@ -92,6 +92,7 @@ type CoreParameters struct { type ShrinkParameterSet struct { CoreParameters // + Directory string MirrorPath string ModeEn assistant.EnumValue[ModeEnum] } diff --git a/src/i18n/messages.go b/src/i18n/messages-errors.go similarity index 97% rename from src/i18n/messages.go rename to src/i18n/messages-errors.go index 87867ff..581bdc5 100644 --- a/src/i18n/messages.go +++ b/src/i18n/messages-errors.go @@ -21,7 +21,7 @@ type FooBarTemplData struct { // name of the library implementing this template project. func (td FooBarTemplData) Message() *i18n.Message { return &i18n.Message{ - ID: "foo-bar.arcadia.nav", + ID: "foo-bar.pixa.nav", Description: "Foo Bar description", Other: "foo bar failure '{{.Path}}' (reason: {{.Reason}})", }