-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1693a03
commit 49d5493
Showing
11 changed files
with
765 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -26,3 +26,5 @@ coverage | |
ginkgo.report | ||
|
||
src/assistant/internal/l10n/out/translate.en-US.json | ||
|
||
.DS_Store |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
package cobrass | ||
|
||
import ( | ||
"github.com/snivilised/cobrass/src/clif" | ||
) | ||
|
||
type ( | ||
// ThirdPartyFlagName raw name of a flag, ie without the leading --/- | ||
ThirdPartyFlagName = clif.ThirdPartyFlagName | ||
|
||
// 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 | ||
// 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 | ||
// but not in ThirdPartyCommandLine. | ||
PresentFlagsCollection = clif.PresentFlagsCollection | ||
|
||
// ThirdPartyPresentFlags (see PresentFlagsCollection) | ||
ThirdPartyPresentFlags = clif.ThirdPartyPresentFlags | ||
|
||
// KnownByCollection collection maps a full flag name to the | ||
// short name it is also known by. If a flag does not | ||
// have a short name, it should be mapped to the empty | ||
// string. | ||
KnownByCollection = clif.KnownByCollection | ||
|
||
// ThirdPartyFlagKnownBy (see KnownByCollection). | ||
ThirdPartyFlagKnownBy = clif.ThirdPartyFlagKnownBy | ||
|
||
// ThirdPartyCommandLine represents the collection of flags | ||
// used to invoke a third party command. This collection | ||
// represents the raw flags used for the invocation in | ||
// the order required by the third party command. It also means | ||
// that this collection contains the leading --/- not just | ||
// the names of the flags and options. | ||
// For example, to invoke the magick command we may want to | ||
// compose this collection with: | ||
// magick --strip --interlace plane --gaussian-blur 0.05 | ||
// and in this case, the list would be defined as a string slice: | ||
// []string{"--strip", "--interlace", "plane", "--gaussian-blur", "0.05"} | ||
ThirdPartyCommandLine = clif.ThirdPartyCommandLine | ||
|
||
// ExternalThirdParty base struct for cli applications using the | ||
// entry paradigm that need to delegate an invocation to an | ||
// external third party command. | ||
ExternalThirdParty = clif.ExternalThirdParty | ||
) | ||
|
||
var ( | ||
// Evaluate merges the secondary command line with the present flags. | ||
// The flags that occur in present 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 | ||
// contains flags in their bare long form (bare as in without dash prefix). | ||
Evaluate = clif.Evaluate | ||
) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package clif_test | ||
|
||
import ( | ||
"testing" | ||
|
||
. "github.com/onsi/ginkgo/v2" | ||
. "github.com/onsi/gomega" | ||
) | ||
|
||
func TestClif(t *testing.T) { | ||
RegisterFailHandler(Fail) | ||
RunSpecs(t, "Clif Suite") | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
package clif | ||
|
||
import ( | ||
"strings" | ||
|
||
"github.com/samber/lo" | ||
"golang.org/x/exp/maps" | ||
"golang.org/x/exp/slices" | ||
) | ||
|
||
var booleans = []string{"true", "false"} | ||
|
||
type ( | ||
tokenInput struct { | ||
token string | ||
lead string | ||
bare string | ||
optionValue string | ||
existingCL ThirdPartyCommandLine | ||
presentFlags PresentFlagsCollection | ||
knownBy KnownByCollection | ||
} | ||
|
||
handleTokenResult struct { | ||
doConcatenate bool | ||
} | ||
|
||
concatIfFunc func(input *tokenInput) *handleTokenResult | ||
concatenateResult struct { | ||
commandLine ThirdPartyCommandLine | ||
handleResult handleTokenResult | ||
} | ||
) | ||
|
||
func (i *tokenInput) yoke(nextIndex int, secondaryCL ThirdPartyCommandLine) int { | ||
const ( | ||
unaryIncrement = 1 | ||
pairIncrement = 2 | ||
) | ||
|
||
handleAsPair := false | ||
|
||
if nextIndex < len(secondaryCL) { | ||
next := secondaryCL[nextIndex] | ||
nextLead, nextBare := split(next) | ||
|
||
if strings.HasPrefix(i.lead, "-") && !strings.HasPrefix(nextLead, "-") { | ||
i.optionValue = nextBare | ||
handleAsPair = true | ||
} | ||
} | ||
|
||
return lo.Ternary(handleAsPair, pairIncrement, unaryIncrement) | ||
} | ||
|
||
func (i *tokenInput) concatIf(concatFunc concatIfFunc) *concatenateResult { | ||
handleResult := concatFunc(i) | ||
|
||
if handleResult.doConcatenate { | ||
i.existingCL = append(i.existingCL, i.token) | ||
|
||
if i.optionValue != "" { | ||
i.existingCL = append(i.existingCL, i.optionValue) | ||
} | ||
} | ||
|
||
return &concatenateResult{ | ||
commandLine: i.existingCL, | ||
handleResult: *handleResult, | ||
} | ||
} | ||
|
||
func concatenate(input *tokenInput) *handleTokenResult { | ||
var ( | ||
result = &handleTokenResult{} | ||
) | ||
|
||
if input.lead == "" { | ||
result.doConcatenate = true | ||
|
||
return result | ||
} | ||
|
||
if _, found := input.presentFlags[input.bare]; found { | ||
return result | ||
} | ||
|
||
aka := input.knownBy[input.bare] | ||
_, found := input.presentFlags[aka] | ||
result.doConcatenate = !found | ||
|
||
return result | ||
} | ||
|
||
// Evaluate merges the secondary command line with the present flags. | ||
// The flags that occur in present 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 | ||
// contains flags in their bare long form. | ||
func Evaluate(presentFlags PresentFlagsCollection, | ||
knownBy KnownByCollection, | ||
secondaryCL ThirdPartyCommandLine, | ||
) ThirdPartyCommandLine { | ||
result := &concatenateResult{} | ||
bilateralKnownBy := composeBilateral(knownBy) | ||
|
||
result.commandLine = spreadFlags(presentFlags) | ||
|
||
if len(secondaryCL) == 0 { | ||
return result.commandLine | ||
} | ||
|
||
if len(secondaryCL) == 1 { | ||
token := secondaryCL[0] | ||
lead, bare := split(token) | ||
|
||
input := &tokenInput{ | ||
token: token, | ||
lead: lead, | ||
bare: bare, | ||
existingCL: result.commandLine, | ||
presentFlags: presentFlags, | ||
knownBy: bilateralKnownBy, | ||
} | ||
result = input.concatIf(concatenate) | ||
|
||
return result.commandLine | ||
} | ||
|
||
for t, n := 0, 1; t < len(secondaryCL); { | ||
token := secondaryCL[t] | ||
lead, bare := split(token) | ||
|
||
input := &tokenInput{ | ||
token: token, | ||
lead: lead, | ||
bare: bare, | ||
existingCL: result.commandLine, | ||
presentFlags: presentFlags, | ||
knownBy: bilateralKnownBy, | ||
} | ||
increment := input.yoke(n, secondaryCL) | ||
result = input.concatIf(concatenate) | ||
|
||
t += increment | ||
n += increment | ||
} | ||
|
||
return result.commandLine | ||
} | ||
|
||
func split(token string) (string, string) { //nolint:gocritic // pedant | ||
var ( | ||
lead string | ||
bare = token | ||
) | ||
|
||
if strings.HasPrefix(token, "--") { | ||
lead = "--" | ||
bare = token[2:] | ||
} else if strings.HasPrefix(token, "-") { | ||
lead = "-" | ||
bare = token[1:] | ||
} | ||
|
||
return lead, bare | ||
} | ||
|
||
func spreadFlags(presentFlags PresentFlagsCollection) ThirdPartyCommandLine { | ||
commandLine := ThirdPartyCommandLine{} | ||
|
||
for _, flag := range presentFlags.Keys() { | ||
option := presentFlags[flag] | ||
dash := lo.Ternary(len(flag) == 1, "-", "--") | ||
prefixed := dash + flag | ||
withOption := !slices.Contains(booleans, option) | ||
|
||
commandLine = append(commandLine, prefixed) | ||
|
||
if withOption { | ||
commandLine = append(commandLine, option) | ||
} | ||
} | ||
|
||
return commandLine | ||
} | ||
|
||
func composeBilateral(knownBy KnownByCollection) KnownByCollection { | ||
const twice = 2 | ||
bilateral := make(KnownByCollection, len(knownBy)*twice) | ||
|
||
maps.Copy(bilateral, knownBy) | ||
|
||
for long, short := range knownBy { | ||
bilateral[short] = long | ||
} | ||
|
||
return bilateral | ||
} |
Oops, something went wrong.