diff --git a/.vscode/settings.json b/.vscode/settings.json index 104150f..b38a00a 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,10 +1,12 @@ { "cSpell.words": [ + "argh", "avfs", "beezledub", "bindnative", "bodyclose", "clif", + "cobrass", "cogen", "colors", "Comparables", @@ -26,6 +28,7 @@ "gogen", "goimports", "gola", + "gomega", "gomnd", "gosec", "gosimple", @@ -33,6 +36,7 @@ "gradientf", "gradientsf", "ineffassign", + "Infexion", "ipaddress", "ipmask", "ipnet", @@ -43,6 +47,7 @@ "nicksnyder", "nolint", "nolintlint", + "onsi", "paramset", "prealloc", "psname", @@ -51,6 +56,7 @@ "refl", "repotoken", "Selectf", + "snivilised", "staticcheck", "structcheck", "stylecheck", diff --git a/go.mod b/go.mod index def5b90..418f9ac 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/snivilised/cobrass -go 1.19 +go 1.21 require ( github.com/avfs/avfs v0.33.0 diff --git a/go.sum b/go.sum index 38110f0..b966c6f 100644 --- a/go.sum +++ b/go.sum @@ -54,6 +54,7 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -61,6 +62,7 @@ github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5y github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= +github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= @@ -96,6 +98,7 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -127,6 +130,7 @@ github.com/google/pprof v0.0.0-20230406165453-00490a63f317/go.mod h1:79YE0hCXdHa github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= +github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= @@ -144,15 +148,19 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 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= github.com/nicksnyder/go-i18n/v2 v2.2.1/go.mod h1:fF2++lPHlo+/kPaj3nB0uxtPwzlPm+BlgwGX7MkeGj0= github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= @@ -166,9 +174,11 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= +github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9cJvm4SvQ= github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U= @@ -179,6 +189,7 @@ github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXn github.com/snivilised/extendio v0.3.0 h1:lGy33940icaEd20gaDp6nm4ch3FfpMXJUrqpyvWax0E= github.com/snivilised/extendio v0.3.0/go.mod h1:GZI0mupBlnKu7qXvmvD/slpCa6CxQgQLdD5mvmtsbig= github.com/snivilised/lorax v0.4.1 h1:4WBPYFeAObXXloBGDnzQPMMS3nb+XuuhdCeaVF2aHLM= +github.com/snivilised/lorax v0.4.1/go.mod h1:dFRNvQzKSewjabVEfnigGETf0Xx1cD21+d01QNwIbWM= github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= @@ -218,6 +229,7 @@ go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -263,6 +275,7 @@ golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -517,9 +530,11 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= diff --git a/public-cobrass-clif-api.go b/public-cobrass-clif-api.go index 2e737db..db0bf3e 100644 --- a/public-cobrass-clif-api.go +++ b/public-cobrass-clif-api.go @@ -8,24 +8,31 @@ type ( // ThirdPartyFlagName raw name of a flag, ie without the leading --/- ThirdPartyFlagName = clif.ThirdPartyFlagName + // ThirdPartyPositional represents a positional argument + ThirdPartyPositional = clif.ThirdPartyPositional + + // ThirdPartyPositionalArgs represents a collection of third party + // positional arguments. + ThirdPartyPositionalArgs = clif.ThirdPartyPositionalArgs + // ThirdPartyOptionValue the string value of an option. Since this option // is being delegated to a third party command, it does not have to be // of a particular native go type and can be composed from a go type // using the value's String() method. ThirdPartyOptionValue = clif.ThirdPartyOptionValue - // PresentFlagsCollection represents the set of third party flags + // SpecifiedFlagsCollection represents the set of third party flags // presented by the user on the command line. // (NB: Cobra does not currently have a mechanism to collect third // party flags, by convention, anything that follows " -- "), therefore // we need to collect and handle these flags/options explicitly, // which is less than ideal. - // A difference between PresentFlagsCollection and ThirdPartyCommandLine - // is that switch flags have a true/false option value in PresentFlagsCollection + // A difference between SpecifiedFlagsCollection and ThirdPartyCommandLine + // is that switch flags have a true/false option value in SpecifiedFlagsCollection // but not in ThirdPartyCommandLine. - PresentFlagsCollection = clif.PresentFlagsCollection + SpecifiedFlagsCollection = clif.SpecifiedFlagsCollection - // ThirdPartyPresentFlags (see PresentFlagsCollection) + // ThirdPartyPresentFlags (see SpecifiedFlagsCollection) ThirdPartyPresentFlags = clif.ThirdPartyPresentFlags // KnownByCollection collection maps a full flag name to the @@ -35,7 +42,7 @@ type ( KnownByCollection = clif.KnownByCollection // ThirdPartyFlagKnownBy (see KnownByCollection). - ThirdPartyFlagKnownBy = clif.ThirdPartyFlagKnownBy + ThirdPartyFlagKnownBy = clif.ThirdPartyFlagsKnownBy // ThirdPartyCommandLine represents the collection of flags // used to invoke a third party command. This collection @@ -57,12 +64,21 @@ type ( ) var ( - // Evaluate merges the secondary command line with the present flags. - // The flags that occur in present take precedence over those in + // Evaluate merges the secondary command line with the specified flags. + // The flags that occur in specified take precedence over those in // secondary. There is a slight complication caused by the fact that - // a flag in the present set may be in the secondary set but in the opposite - // form; eg a flag may be in its short from in present but in long form - // in secondary. This is resolved by the knownBy set. The present set + // a flag in the specified set may be in the secondary set but in the opposite + // form; eg a flag may be in its short from in specified but in long form + // in secondary. This is resolved by the knownBy set. The specified set // contains flags in their bare long form (bare as in without dash prefix). Evaluate = clif.Evaluate + + // Expand returns a slice of strings representing the positional arguments and + // flags/options to be executed by the third party program. before and flags + // are represented as a ThirdPartyCommandLine. This means that they can be + // represented by any slice of strings. However, since before represents + // positional args, those args are not expected to include any flags. Those + // flags would be specified in the flags parameter. after is optional and + // again represents further positional arguments. + Expand = clif.Expand ) diff --git a/src/assistant/option-validator-container_test.go b/src/assistant/option-validator-container_test.go index 719467e..26edca5 100644 --- a/src/assistant/option-validator-container_test.go +++ b/src/assistant/option-validator-container_test.go @@ -18,7 +18,7 @@ var _ = Describe("ValidatorContainer", func() { var paramSet *assistant.ParamSet[WidgetParameterSet] Context("NewValidatorContainer", func() { - When("options not present", func() { + When("options not specified", func() { It("🧪 should: create ValidatorContainer with default options", func() { validators = assistant.NewValidatorContainer() Expect(validators).ToNot(BeNil()) diff --git a/src/assistant/param-set_test.go b/src/assistant/param-set_test.go index 6ec1137..96660d7 100644 --- a/src/assistant/param-set_test.go +++ b/src/assistant/param-set_test.go @@ -91,7 +91,7 @@ var _ = Describe("ParamSet (manual)", func() { // special scenario, not auto generated // Entry(nil, TcEntry{ - Message: "bool type flag is NOT present", + Message: "bool type flag is NOT specified", Binder: func() { paramSet.BindBool( assistant.NewFlagInfo("concise ensures that output is compressed", "c", false), diff --git a/src/clif/evaluate.go b/src/clif/evaluate.go index 2588772..a010e7d 100644 --- a/src/clif/evaluate.go +++ b/src/clif/evaluate.go @@ -17,7 +17,7 @@ type ( bare string optionValue string existingCL ThirdPartyCommandLine - presentFlags PresentFlagsCollection + presentFlags SpecifiedFlagsCollection knownBy KnownByCollection } @@ -92,14 +92,14 @@ func notInPresent(input *tokenInput) *handleTokenResult { return result } -// Evaluate merges the secondary command line with the present flags. -// The flags that occur in present take precedence over those in +// Evaluate merges the secondary command line with the specified flags. +// The flags that occur in specified take precedence over those in // secondary. There is a slight complication caused by the fact that -// a flag in the present set may be in the secondary set but in the opposite -// form; eg a flag may be in its short from in present but in long form -// in secondary. This is resolved by the knownBy set. The present set +// a flag in the specified set may be in the secondary set but in the opposite +// form; eg a flag may be in its short from in specified but in long form +// in secondary. This is resolved by the knownBy set. The specified set // contains flags in their bare long form. -func Evaluate(presentFlags PresentFlagsCollection, +func Evaluate(presentFlags SpecifiedFlagsCollection, knownBy KnownByCollection, secondaryCL ThirdPartyCommandLine, ) ThirdPartyCommandLine { @@ -168,7 +168,7 @@ func split(token string) (string, string) { //nolint:gocritic // pedant return lead, bare } -func spreadFlags(presentFlags PresentFlagsCollection) ThirdPartyCommandLine { +func spreadFlags(presentFlags SpecifiedFlagsCollection) ThirdPartyCommandLine { commandLine := ThirdPartyCommandLine{} for _, flag := range presentFlags.Keys() { diff --git a/src/clif/evaluate_test.go b/src/clif/evaluate_test.go index 407efc2..48c3471 100644 --- a/src/clif/evaluate_test.go +++ b/src/clif/evaluate_test.go @@ -11,7 +11,7 @@ import ( type evaluateTE struct { baseTE - present clif.PresentFlagsCollection + specified clif.SpecifiedFlagsCollection secondary clif.ThirdPartyCommandLine } @@ -31,7 +31,7 @@ var _ = Describe("Evaluate", Ordered, func() { DescribeTable("ThirdPartyCommandLine", func(entry *evaluateTE) { - actual := clif.Evaluate(entry.present, knownBy, entry.secondary) + actual := clif.Evaluate(entry.specified, knownBy, entry.secondary) Expect(actual).To(HaveExactElements(entry.expected)) }, func(entry *evaluateTE) string { @@ -44,11 +44,11 @@ var _ = Describe("Evaluate", Ordered, func() { // Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single switch; secondary is empty", - shouldReturn: "present", + given: "specified contains single switch; secondary is empty", + shouldReturn: "specified", expected: []string{"--dry-run"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", }, secondary: clif.ThirdPartyCommandLine{}, @@ -56,11 +56,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single flag; secondary is empty", - shouldReturn: "present", + given: "specified contains single flag; secondary is empty", + shouldReturn: "specified", expected: []string{"--sampling-factor", "4:2:0"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "sampling-factor": "4:2:0", }, secondary: clif.ThirdPartyCommandLine{}, @@ -68,11 +68,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single flag; secondary is empty", - shouldReturn: "present", + given: "specified contains single flag; secondary is empty", + shouldReturn: "specified", expected: []string{"--sampling-factor", "4:2:0"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "sampling-factor": "4:2:0", }, secondary: clif.ThirdPartyCommandLine{}, @@ -80,11 +80,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains flag and a switch; secondary is empty", - shouldReturn: "all present", + given: "specified contains flag and a switch; secondary is empty", + shouldReturn: "all specified", expected: []string{"--dry-run", "--sampling-factor", "4:2:0"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", "sampling-factor": "4:2:0", }, @@ -95,14 +95,14 @@ var _ = Describe("Evaluate", Ordered, func() { // single secondary token // - // ---> secondary switch in present + // ---> secondary switch in specified Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single switch; single long secondary switch in present", - shouldReturn: "present, ignore secondary", + given: "specified contains single switch; single long secondary switch in specified", + shouldReturn: "specified, ignore secondary", expected: []string{"--dry-run"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", }, secondary: clif.ThirdPartyCommandLine{"--dry-run"}, @@ -110,23 +110,23 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single switch; single short secondary switch in present", - shouldReturn: "present, ignore secondary", + given: "specified contains single switch; single short secondary switch in specified", + shouldReturn: "specified, ignore secondary", expected: []string{"--dry-run"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", }, secondary: clif.ThirdPartyCommandLine{"-D"}, }), - // ---> secondary switch NOT in present + // ---> secondary switch NOT in specified Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single switch; single long secondary switch NOT in present", - shouldReturn: "present with secondary", + given: "specified contains single switch; single long secondary switch NOT in specified", + shouldReturn: "specified with secondary", expected: []string{"--dry-run", "--strip"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", }, secondary: clif.ThirdPartyCommandLine{"--strip"}, @@ -134,11 +134,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single switch; single short secondary switch NOT in present", - shouldReturn: "present with secondary", + given: "specified contains single switch; single short secondary switch NOT in specified", + shouldReturn: "specified with secondary", expected: []string{"--dry-run", "-s"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", }, secondary: clif.ThirdPartyCommandLine{"-s"}, @@ -148,15 +148,15 @@ var _ = Describe("Evaluate", Ordered, func() { // single flag/option secondary tokens // - // ---> secondary flag in present + // ---> secondary flag in specified Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single flag; long secondary flag/option in present", - shouldReturn: "present, ignore secondary", + given: "specified contains single flag; long secondary flag/option in specified", + shouldReturn: "specified, ignore secondary", expected: []string{"--sampling-factor", "4:2:0"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "sampling-factor": "4:2:0", }, secondary: clif.ThirdPartyCommandLine{"--sampling-factor", "2x1"}, @@ -164,23 +164,23 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single flag; short secondary flag/option in present", - shouldReturn: "present, ignore secondary", + given: "specified contains single flag; short secondary flag/option in specified", + shouldReturn: "specified, ignore secondary", expected: []string{"--sampling-factor", "4:2:0"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "sampling-factor": "4:2:0", }, secondary: clif.ThirdPartyCommandLine{"-f", "2x1"}, }), - // ---> secondary flag NOT in present + // ---> secondary flag NOT in specified Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single flag; long secondary flag/option NOT in present", - shouldReturn: "present with secondary", + given: "specified contains single flag; long secondary flag/option NOT in specified", + shouldReturn: "specified with secondary", expected: []string{"--sampling-factor", "4:2:0", "--gaussian-blur", "0.05"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "sampling-factor": "4:2:0", }, secondary: clif.ThirdPartyCommandLine{"--gaussian-blur", "0.05"}, @@ -188,11 +188,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "present contains single flag; short secondary flag/option NOT in present", - shouldReturn: "present with secondary", + given: "specified contains single flag; short secondary flag/option NOT in specified", + shouldReturn: "specified with secondary", expected: []string{"--sampling-factor", "4:2:0", "-b", "0.05"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "sampling-factor": "4:2:0", }, secondary: clif.ThirdPartyCommandLine{"-b", "0.05"}, @@ -204,11 +204,11 @@ var _ = Describe("Evaluate", Ordered, func() { // Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "secondary switch followed by a flag; both in present", - shouldReturn: "present, ignore secondary", + given: "secondary switch followed by a flag; both in specified", + shouldReturn: "specified, ignore secondary", expected: []string{"--dry-run", "--sampling-factor", "4:2:0"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", "sampling-factor": "4:2:0", }, @@ -217,11 +217,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "secondary switch followed by a flag; switch in present", - shouldReturn: "present, with secondary flag", + given: "secondary switch followed by a flag; switch in specified", + shouldReturn: "specified, with secondary flag", expected: []string{"--dry-run", "--sampling-factor", "2x1"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "dry-run": "true", }, secondary: clif.ThirdPartyCommandLine{"--dry-run", "--sampling-factor", "2x1"}, @@ -229,11 +229,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "secondary switch followed by a flag; flag in present", - shouldReturn: "present, with secondary switch", + given: "secondary switch followed by a flag; flag in specified", + shouldReturn: "specified, with secondary switch", expected: []string{"--sampling-factor", "4:2:0", "--dry-run"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "sampling-factor": "4:2:0", }, secondary: clif.ThirdPartyCommandLine{"--dry-run", "--sampling-factor", "2x1"}, @@ -241,11 +241,11 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "secondary switch followed by a flag; neither in present", - shouldReturn: "present, secondary switch and flag", + given: "secondary switch followed by a flag; neither in specified", + shouldReturn: "specified, secondary switch and flag", expected: []string{"--gaussian-blur", "0.05", "--dry-run", "--sampling-factor", "2x1"}, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "gaussian-blur": "0.05", }, secondary: clif.ThirdPartyCommandLine{"--dry-run", "--sampling-factor", "2x1"}, @@ -255,8 +255,8 @@ var _ = Describe("Evaluate", Ordered, func() { Entry(nil, &evaluateTE{ baseTE: baseTE{ - given: "many in present; many in secondary", - shouldReturn: "present flags/options overriding secondary flags/options", + given: "many in specified; many in secondary", + shouldReturn: "specified flags/options overriding secondary flags/options", expected: []string{ "--gaussian-blur", "0.05", "-i", "plane", @@ -265,7 +265,7 @@ var _ = Describe("Evaluate", Ordered, func() { "--strip", }, }, - present: clif.PresentFlagsCollection{ + specified: clif.SpecifiedFlagsCollection{ "gaussian-blur": "0.05", "i": "plane", }, diff --git a/src/clif/expand.go b/src/clif/expand.go index 89aee9c..f50b9c3 100644 --- a/src/clif/expand.go +++ b/src/clif/expand.go @@ -1,6 +1,15 @@ package clif -func Expand(before, flags ThirdPartyCommandLine, after ...ThirdPartyFlagName) []string { +// Expand returns a slice of strings representing all the positional arguments +// and flags/options to be executed by the third party program. before and +// flags are represented as a ThirdPartyCommandLine. This means that they can +// be represented by any slice of strings. However, since before represents +// positional args, those args are not expected to include any flags. Those +// flags would be specified in the flags parameter. after is optional and +// again represents further positional arguments. +func Expand(before ThirdPartyPositionalArgs, flags ThirdPartyCommandLine, + after ...ThirdPartyPositional, +) ThirdPartyCommandLine { // approxTokensPerFlag: this is an approximate value because switches // do not need an option value const approxTokensPerFlag = 2 diff --git a/src/clif/expand_test.go b/src/clif/expand_test.go index 5894a42..e0f8a95 100644 --- a/src/clif/expand_test.go +++ b/src/clif/expand_test.go @@ -17,7 +17,7 @@ type baseTE struct { type expandTE struct { baseTE - before clif.ThirdPartyCommandLine + before clif.ThirdPartyPositionalArgs flags clif.ThirdPartyCommandLine after []clif.ThirdPartyFlagName } @@ -40,7 +40,7 @@ var _ = Describe("Expand", func() { shouldReturn: "the single before", expected: []string{"--dry-run"}, }, - before: clif.ThirdPartyCommandLine{}, + before: clif.ThirdPartyPositionalArgs{}, flags: clif.ThirdPartyCommandLine{"--dry-run"}, }), @@ -50,7 +50,7 @@ var _ = Describe("Expand", func() { shouldReturn: "the single before with flags", expected: []string{"file.jpg", "--dry-run"}, }, - before: clif.ThirdPartyCommandLine{"file.jpg"}, + before: clif.ThirdPartyPositionalArgs{"file.jpg"}, flags: clif.ThirdPartyCommandLine{"--dry-run"}, }), @@ -60,7 +60,7 @@ var _ = Describe("Expand", func() { shouldReturn: "after and single after", expected: []string{"--dry-run", "result.jpg"}, }, - before: clif.ThirdPartyCommandLine{}, + before: clif.ThirdPartyPositionalArgs{}, flags: clif.ThirdPartyCommandLine{"--dry-run"}, after: []clif.ThirdPartyFlagName{"result.jpg"}, }), @@ -74,7 +74,7 @@ var _ = Describe("Expand", func() { "result-1.jpg", "result-2.jpg", }, }, - before: clif.ThirdPartyCommandLine{"first.jpg", "second.jpg"}, + before: clif.ThirdPartyPositionalArgs{"first.jpg", "second.jpg"}, flags: clif.ThirdPartyCommandLine{"--dry-run", "--interlace", "plane"}, after: []clif.ThirdPartyFlagName{"result-1.jpg", "result-2.jpg"}, }), @@ -87,7 +87,7 @@ var _ = Describe("Expand", func() { "result-1.jpg", "result-2.jpg", }, }, - before: clif.ThirdPartyCommandLine{"first.jpg", "second.jpg"}, + before: clif.ThirdPartyPositionalArgs{"first.jpg", "second.jpg"}, flags: clif.ThirdPartyCommandLine{}, after: []clif.ThirdPartyFlagName{"result-1.jpg", "result-2.jpg"}, }), diff --git a/src/clif/public-clif-api.go b/src/clif/public-clif-api.go index a54bf1c..58f88e3 100644 --- a/src/clif/public-clif-api.go +++ b/src/clif/public-clif-api.go @@ -8,25 +8,32 @@ type ( // ThirdPartyFlagName raw name of a flag, ie without the leading --/- ThirdPartyFlagName = string + // ThirdPartyPositional represents a positional argument. + ThirdPartyPositional = string + + // ThirdPartyPositionalArgs represents a collection of third party + // positional arguments. + ThirdPartyPositionalArgs = []string + // ThirdPartyOptionValue the string value of an option. Since this option // is being delegated to a third party command, it does not have to be // of a particular native go type and can be composed from a go type // using the value's String() method. ThirdPartyOptionValue = string - // PresentFlagsCollection represents the set of third party flags + // SpecifiedFlagsCollection represents the set of third party flags // presented by the user on the command line. // (NB: Cobra does not currently have a mechanism to collect third // party flags, by convention, anything that follows " -- "), therefore // we need to collect and handle these flags/options explicitly, // which is less than ideal. - // A difference between PresentFlagsCollection and ThirdPartyCommandLine - // is that switch flags have a true/false option value in PresentFlagsCollection + // A difference between SpecifiedFlagsCollection and ThirdPartyCommandLine + // is that switch flags have a true/false option value in SpecifiedFlagsCollection // but not in ThirdPartyCommandLine. - PresentFlagsCollection = collections.OrderedKeysMap[ThirdPartyFlagName, ThirdPartyOptionValue] + SpecifiedFlagsCollection = collections.OrderedKeysMap[ThirdPartyFlagName, ThirdPartyOptionValue] - // ThirdPartyPresentFlags (see PresentFlagsCollection). - ThirdPartyPresentFlags PresentFlagsCollection + // ThirdPartyPresentFlags (see SpecifiedFlagsCollection). + ThirdPartyPresentFlags SpecifiedFlagsCollection // KnownByCollection collection maps a full flag name to the // short name it is also known by. If a flag does not @@ -34,8 +41,8 @@ type ( // string. KnownByCollection map[ThirdPartyFlagName]ThirdPartyFlagName - // ThirdPartyFlagKnownBy (see KnownByCollection). - ThirdPartyFlagKnownBy KnownByCollection + // ThirdPartyFlagsKnownBy (see KnownByCollection). + ThirdPartyFlagsKnownBy KnownByCollection // ThirdPartyCommandLine represents the collection of flags // used to invoke a third party command. This collection @@ -55,11 +62,12 @@ type ( // external third party command. ExternalThirdParty struct { // KnownBy represents the collection of all possible flags that - // can be specified in a particular invocation (see ThirdPartyFlagKnownBy) - KnownBy ThirdPartyFlagKnownBy + // can be specified in a particular invocation (see KnownByCollection) + KnownBy KnownByCollection - // Known represents a particular invocation of a third party - // command (see ThirdPartyFlagsInvocation). - Known ThirdPartyCommandLine + // ExecutionFlags represents a particular invocation of a third party + // command that contains flags explicitly specified and ones loaded + // from other sources, such as config. (see ThirdPartyCommandLine). + ExecutionFlags ThirdPartyCommandLine } ) diff --git a/src/store/families_test.go b/src/store/families_test.go index 62e9119..3e141b7 100644 --- a/src/store/families_test.go +++ b/src/store/families_test.go @@ -24,6 +24,17 @@ const ( shouldMessage = "🧪 should: bind all parameters without error" ) +// --files-gb(G) +// --files-rx(X) +// --folders-gb(Z) +// --folders-rx(y) + +type fileFamilyTE struct { + familyType string + persistent bool + commandLine []string +} + var _ = Describe("Families", Ordered, func() { var ( repo string @@ -71,92 +82,212 @@ var _ = Describe("Families", Ordered, func() { }) DescribeTable("filter family", - func(commandLine []string) { - ps := assistant.NewParamSet[store.FilterParameterSet](rootCommand) - ps.Native.BindAll(ps) + func(entry *fileFamilyTE) { + switch entry.familyType { + case "poly": + { + ps := assistant.NewParamSet[store.PolyFilterParameterSet](rootCommand) + if entry.persistent { + ps.Native.BindAll(ps, rootCommand.PersistentFlags()) + } else { + ps.Native.BindAll(ps) + } + } + + case "files": + { + ps := assistant.NewParamSet[store.FilesFilterParameterSet](rootCommand) + if entry.persistent { + ps.Native.BindAll(ps, rootCommand.PersistentFlags()) + } else { + ps.Native.BindAll(ps) + } + } + case "folders": + { + ps := assistant.NewParamSet[store.FoldersFilterParameterSet](rootCommand) + if entry.persistent { + ps.Native.BindAll(ps, rootCommand.PersistentFlags()) + } else { + ps.Native.BindAll(ps) + } + } + } - execute(commandLine) + execute(entry.commandLine) }, - func(args []string) string { + func(entry *fileFamilyTE) string { return shouldMessage }, Entry( - nil, []string{"--files-rx", "^foo", "--folders-gb", "bar*"}, + nil, + &fileFamilyTE{ + familyType: "files", + persistent: true, + commandLine: []string{"--files-rx", "^foo"}, + }, + ), + Entry( + nil, + &fileFamilyTE{ + familyType: "files", + commandLine: []string{"-X", "^foo"}, + }, + ), + // + Entry( + nil, + &fileFamilyTE{ + familyType: "folders", + commandLine: []string{"--folders-gb", "bar*"}, + }, + ), + Entry( + nil, + &fileFamilyTE{ + familyType: "folders", + persistent: true, + commandLine: []string{"-Z", "bar*"}, + }, + ), + // + Entry( + nil, + &fileFamilyTE{ + familyType: "poly", + commandLine: []string{"--files-rx", "^foo", "--folders-gb", "bar*"}, + }, ), Entry( - nil, []string{"-X", "^foo", "-z", "bar*"}, + nil, + &fileFamilyTE{ + familyType: "poly", + commandLine: []string{"-X", "^foo", "-Z", "bar*"}, + }, ), Entry( - nil, []string{"--files-gb", "foo*", "--folders-rx", "^bar"}, + nil, + &fileFamilyTE{ + familyType: "poly", + persistent: true, + commandLine: []string{"--files-gb", "foo*", "--folders-rx", "^bar"}, + }, ), Entry( - nil, []string{"-G", "foo*", "-y", "^bar"}, + nil, + &fileFamilyTE{ + familyType: "poly", + persistent: true, + commandLine: []string{"-G", "foo*", "-Y", "^bar"}, + }, ), + // + ) DescribeTable("worker pool family", - func(commandLine []string) { + func(entry *fileFamilyTE) { ps := assistant.NewParamSet[store.WorkerPoolParameterSet](rootCommand) - ps.Native.BindAll(ps) + if entry.persistent { + ps.Native.BindAll(ps, rootCommand.PersistentFlags()) + } else { + ps.Native.BindAll(ps) + } - execute(commandLine) + execute(entry.commandLine) }, - func(args []string) string { + func(entry *fileFamilyTE) string { return shouldMessage }, Entry( - nil, []string{"--cpu"}, + nil, + &fileFamilyTE{ + commandLine: []string{"--cpu"}, + persistent: true, + }, ), Entry( - nil, []string{"-C"}, + nil, + &fileFamilyTE{ + commandLine: []string{"-C"}, + }, ), Entry( - nil, []string{"--now", "4"}, + nil, + &fileFamilyTE{ + commandLine: []string{"--now", "4"}, + persistent: true, + }, ), Entry( - nil, []string{"-N", "4"}, + nil, + &fileFamilyTE{ + commandLine: []string{"-N", "4"}, + }, ), ) DescribeTable("profile family", - func(commandLine []string) { + func(entry *fileFamilyTE) { ps := assistant.NewParamSet[store.ProfileParameterSet](rootCommand) - ps.Native.BindAll(ps) + if entry.persistent { + ps.Native.BindAll(ps, rootCommand.PersistentFlags()) + } else { + ps.Native.BindAll(ps) + } - execute(commandLine) + execute(entry.commandLine) }, - func(args []string) string { + func(entry *fileFamilyTE) string { return shouldMessage }, Entry( - nil, []string{"--profile", "foo"}, + nil, + &fileFamilyTE{ + commandLine: []string{"--profile", "foo"}, + }, ), Entry( - nil, []string{"-P", "foo"}, + nil, + &fileFamilyTE{ + commandLine: []string{"-P", "foo"}, + persistent: true, + }, ), ) - DescribeTable("profile family", - func(commandLine []string) { + DescribeTable("preview family", + func(entry *fileFamilyTE) { ps := assistant.NewParamSet[store.PreviewParameterSet](rootCommand) - ps.Native.BindAll(ps) + if entry.persistent { + ps.Native.BindAll(ps, rootCommand.PersistentFlags()) + } else { + ps.Native.BindAll(ps) + } - execute(commandLine) + execute(entry.commandLine) }, - func(args []string) string { + func(entry *fileFamilyTE) string { return shouldMessage }, Entry( - nil, []string{"--dry-run"}, + nil, + &fileFamilyTE{ + commandLine: []string{"--dry-run"}, + persistent: true, + }, ), Entry( - nil, []string{"-D"}, + nil, + &fileFamilyTE{ + commandLine: []string{"-D"}, + }, ), ) When("usage requested", func() { It("should: 🧪 show help text", func() { - filtersPS := assistant.NewParamSet[store.FilterParameterSet](rootCommand) + filtersPS := assistant.NewParamSet[store.PolyFilterParameterSet](rootCommand) filtersPS.Native.BindAll(filtersPS) // poolPS := assistant.NewParamSet[store.WorkerPoolParameterSet](rootCommand) diff --git a/src/store/family-filter.go b/src/store/family-filter.go index 528cc7f..32539d3 100644 --- a/src/store/family-filter.go +++ b/src/store/family-filter.go @@ -13,61 +13,167 @@ const ( defaultFilterValue = "" ) -type FilterParameterSet struct { +// NB: We don't want to use up too many of the letters of the alphabet +// on short flag names, because it leaves less for the client to use. +// Therefore, for compound filters typically used when we want to filter +// file system nodes by file name and directory name, we forego the +// ability to specify compound file names (when using the navigator +// in extendio with the FoldersWithFiles subscription type) with a +// short code, as this is seen as a niche feature. The more common +// scenarios would be to either filter files, directories or both +// by using an 'any' scope. With this compromise, the user would +// always have to spell the compound file filter in it full form: +// --files-rx or --files-gb. When using extendio nav, the folders +// with files subscription would have to be used, ie there is no +// standalone file file, so --files-rx and --files-gb are both free +// to use without ambiguity. +// For a regular files scenario, we would need to use the files +// subscription type and in this case, --files-rx(x) and --files-gb(g) +// are still free to be used without ambiguity. + +type FilesFilterParameterSet struct { + FilesGlob string + FilesRexEx string +} + +func (f *FilesFilterParameterSet) BindAll( + parent *assistant.ParamSet[FilesFilterParameterSet], + flagSet ...*pflag.FlagSet, +) { + // --files-gb(G) + // + parent.BindString( + resolveNewFlagInfo( + xi18n.Text(i18n.FilesGlobParamUsageTemplData{}), + defaultFilterValue, + flagSet..., + ), + &parent.Native.FilesGlob, + ) + + // --files-rx(X) + // + parent.BindValidatedString( + resolveNewFlagInfo( + xi18n.Text(i18n.FilesRegExParamUsageTemplData{}), + defaultFilterValue, + flagSet..., + ), + &parent.Native.FilesRexEx, + func(value string, _ *pflag.Flag) error { + _, err := regexp.Compile(value) + return err + }, + ) + + parent.Command.MarkFlagsMutuallyExclusive("files-gb", "files-rx") +} + +type FoldersFilterParameterSet struct { + FoldersGlob string + FoldersRexEx string +} + +func (f *FoldersFilterParameterSet) BindAll( + parent *assistant.ParamSet[FoldersFilterParameterSet], + flagSet ...*pflag.FlagSet, +) { + // --folders-gb(Z) + // + parent.BindString( + resolveNewFlagInfo( + xi18n.Text(i18n.FolderGlobParamUsageTemplData{}), + defaultFilterValue, + flagSet..., + ), + &parent.Native.FoldersGlob, + ) + + // --folders-rx(y) + // + parent.BindValidatedString( + resolveNewFlagInfo( + xi18n.Text(i18n.FolderRexExParamUsageTemplData{}), + defaultFilterValue, + flagSet..., + ), + &parent.Native.FoldersRexEx, + func(value string, _ *pflag.Flag) error { + _, err := regexp.Compile(value) + return err + }, + ) + + parent.Command.MarkFlagsMutuallyExclusive("folders-gb", "folders-rx") +} + +type PolyFilterParameterSet struct { FilesGlob string FilesRexEx string FoldersGlob string FoldersRexEx string } -func (f *FilterParameterSet) BindAll(self *assistant.ParamSet[FilterParameterSet]) { +func (f *PolyFilterParameterSet) BindAll( + parent *assistant.ParamSet[PolyFilterParameterSet], + flagSet ...*pflag.FlagSet, +) { + // argh, code smell here, because we're duplicating the functionality + // in FileFilterParameterSet and FoldersFilterParameterSet, but that can't + // be helped because of the paramSet instance is type specific. + // // --files-gb(G) // - self.BindString( - newFlagInfo( + parent.BindString( + resolveNewFlagInfo( xi18n.Text(i18n.FilesGlobParamUsageTemplData{}), defaultFilterValue, + flagSet..., ), - &self.Native.FilesGlob, + &parent.Native.FilesGlob, ) // --files-rx(X) // - self.BindValidatedString( - newFlagInfo( + parent.BindValidatedString( + resolveNewFlagInfo( xi18n.Text(i18n.FilesRegExParamUsageTemplData{}), - defaultFilterValue), - &self.Native.FilesRexEx, + defaultFilterValue, + flagSet..., + ), + &parent.Native.FilesRexEx, func(value string, _ *pflag.Flag) error { _, err := regexp.Compile(value) return err }, ) - // --folders-gb(z) + // --folders-gb(Z) // - self.BindString( - newFlagInfo( + parent.BindString( + resolveNewFlagInfo( xi18n.Text(i18n.FolderGlobParamUsageTemplData{}), defaultFilterValue, + flagSet..., ), - &self.Native.FoldersGlob, + &parent.Native.FoldersGlob, ) // --folders-rx(y) // - self.BindValidatedString( - newFlagInfo( + parent.BindValidatedString( + resolveNewFlagInfo( xi18n.Text(i18n.FolderRexExParamUsageTemplData{}), defaultFilterValue, + flagSet..., ), - &self.Native.FoldersRexEx, + &parent.Native.FoldersRexEx, func(value string, _ *pflag.Flag) error { _, err := regexp.Compile(value) return err }, ) - self.Command.MarkFlagsMutuallyExclusive("files-gb", "files-rx") - self.Command.MarkFlagsMutuallyExclusive("folders-gb", "folders-rx") + parent.Command.MarkFlagsMutuallyExclusive("files-gb", "files-rx") + parent.Command.MarkFlagsMutuallyExclusive("folders-gb", "folders-rx") } diff --git a/src/store/family-preview.go b/src/store/family-preview.go index 9d719c9..6818b32 100644 --- a/src/store/family-preview.go +++ b/src/store/family-preview.go @@ -4,24 +4,29 @@ import ( "github.com/snivilised/cobrass/src/assistant" "github.com/snivilised/cobrass/src/assistant/i18n" xi18n "github.com/snivilised/extendio/i18n" + "github.com/spf13/pflag" ) type PreviewParameterSet struct { DryRun bool } -func (f *PreviewParameterSet) BindAll(self *assistant.ParamSet[PreviewParameterSet]) { +func (f *PreviewParameterSet) BindAll( + parent *assistant.ParamSet[PreviewParameterSet], + flagSet ...*pflag.FlagSet, +) { // --dry-run(D) // const ( defaultDryRun = false ) - self.BindBool( - newFlagInfo( + parent.BindBool( + resolveNewFlagInfo( xi18n.Text(i18n.DryRunParamUsageTemplData{}), defaultDryRun, + flagSet..., ), - &self.Native.DryRun, + &parent.Native.DryRun, ) } diff --git a/src/store/family-profile.go b/src/store/family-profile.go index 93fb73b..a62c6ea 100644 --- a/src/store/family-profile.go +++ b/src/store/family-profile.go @@ -4,23 +4,28 @@ import ( "github.com/snivilised/cobrass/src/assistant" "github.com/snivilised/cobrass/src/assistant/i18n" xi18n "github.com/snivilised/extendio/i18n" + "github.com/spf13/pflag" ) type ProfileParameterSet struct { Profile string } -func (f *ProfileParameterSet) BindAll(self *assistant.ParamSet[ProfileParameterSet]) { +func (f *ProfileParameterSet) BindAll( + parent *assistant.ParamSet[ProfileParameterSet], + flagSet ...*pflag.FlagSet, +) { const ( defaultProfile = "" ) - self.BindValidatedStringIsMatch( - newFlagInfo( + parent.BindValidatedStringIsMatch( + resolveNewFlagInfo( xi18n.Text(i18n.ProfileParamUsageTemplData{}), defaultProfile, + flagSet..., ), - &self.Native.Profile, + &parent.Native.Profile, `^[\w-]+$`, ) } diff --git a/src/store/family-worker-pool.go b/src/store/family-worker-pool.go index fc5193a..c5746d6 100644 --- a/src/store/family-worker-pool.go +++ b/src/store/family-worker-pool.go @@ -4,6 +4,7 @@ import ( "github.com/snivilised/cobrass/src/assistant" "github.com/snivilised/cobrass/src/assistant/i18n" xi18n "github.com/snivilised/extendio/i18n" + "github.com/spf13/pflag" ) type WorkerPoolParameterSet struct { @@ -11,19 +12,23 @@ type WorkerPoolParameterSet struct { NoWorkers int } -func (f *WorkerPoolParameterSet) BindAll(self *assistant.ParamSet[WorkerPoolParameterSet]) { +func (f *WorkerPoolParameterSet) BindAll( + parent *assistant.ParamSet[WorkerPoolParameterSet], + flagSet ...*pflag.FlagSet, +) { // --cpu(C) // const ( defaultCPU = false ) - self.BindBool( - newFlagInfo( + parent.BindBool( + resolveNewFlagInfo( xi18n.Text(i18n.WorkerPoolCPUParamUsageTemplData{}), defaultCPU, + flagSet..., ), - &self.Native.CPU, + &parent.Native.CPU, ) // --now(N) @@ -34,15 +39,16 @@ func (f *WorkerPoolParameterSet) BindAll(self *assistant.ParamSet[WorkerPoolPara maxNow = 100 ) - self.BindValidatedIntWithin( - newFlagInfo( + parent.BindValidatedIntWithin( + resolveNewFlagInfo( xi18n.Text(i18n.WorkerPoolNoWParamUsageTemplData{}), defaultNoW, + flagSet..., ), - &self.Native.NoWorkers, + &parent.Native.NoWorkers, minNow, maxNow, ) - self.Command.MarkFlagsMutuallyExclusive("cpu", "now") + parent.Command.MarkFlagsMutuallyExclusive("cpu", "now") } diff --git a/src/store/family.go b/src/store/family.go index 3b2f798..91751c8 100644 --- a/src/store/family.go +++ b/src/store/family.go @@ -3,7 +3,9 @@ package store import ( "strings" + "github.com/samber/lo" "github.com/snivilised/cobrass/src/assistant" + "github.com/spf13/pflag" ) type longFlagName = string @@ -25,8 +27,8 @@ var shortFlags = flagDefinitions{ // "files-gb": "G", "files-rx": "X", - "folders-gb": "z", - "folders-rx": "y", + "folders-gb": "Z", + "folders-rx": "Y", // parameter profile // @@ -39,3 +41,25 @@ func newFlagInfo[T any](usage string, defaultValue T) *assistant.FlagInfo { return assistant.NewFlagInfo(usage, short, defaultValue) } + +func newFlagInfoOnFlagSet[T any](usage string, defaultValue T, + alternativeFlagSet *pflag.FlagSet, +) *assistant.FlagInfo { + name := strings.Split(usage, " ")[0] + short := shortFlags[name] + + return assistant.NewFlagInfoOnFlagSet(usage, short, defaultValue, alternativeFlagSet) +} + +func resolveNewFlagInfo[T any](usage string, defaultValue T, + alternativeFlagSet ...*pflag.FlagSet, +) *assistant.FlagInfo { + return lo.TernaryF(len(alternativeFlagSet) == 0, + func() *assistant.FlagInfo { + return newFlagInfo(usage, defaultValue) + }, + func() *assistant.FlagInfo { + return newFlagInfoOnFlagSet(usage, defaultValue, alternativeFlagSet[0]) + }, + ) +}