diff --git a/cmd/state/internal/cmdtree/cmdtree.go b/cmd/state/internal/cmdtree/cmdtree.go index 2d4dd05a0a..a72f479841 100644 --- a/cmd/state/internal/cmdtree/cmdtree.go +++ b/cmd/state/internal/cmdtree/cmdtree.go @@ -63,7 +63,10 @@ func New(prime *primer.Values, args ...string) *CmdTree { ) languagesCmd := newLanguagesCommand(prime) - languagesCmd.AddChildren(newLanguageInstallCommand(prime)) + languagesCmd.AddChildren( + newLanguageInstallCommand(prime), + newLanguageSearchCommand(prime), + ) cleanCmd := newCleanCommand(prime) cleanCmd.AddChildren( diff --git a/cmd/state/internal/cmdtree/languages.go b/cmd/state/internal/cmdtree/languages.go index 93480fa2e2..5adafc39b5 100644 --- a/cmd/state/internal/cmdtree/languages.go +++ b/cmd/state/internal/cmdtree/languages.go @@ -47,3 +47,19 @@ func newLanguageInstallCommand(prime *primer.Values) *captain.Command { }, ).SetSupportsStructuredOutput() } + +func newLanguageSearchCommand(prime *primer.Values) *captain.Command { + runner := languages.NewSearch(prime) + + return captain.NewCommand( + "search", + locale.Tl("languages_search_title", "Searching Languages"), + locale.Tl("languages_search_cmd_description", "Search for an available language to use in your project"), + prime, + []*captain.Flag{}, + []*captain.Argument{}, + func(ccmd *captain.Command, _ []string) error { + return runner.Run() + }, + ).SetSupportsStructuredOutput().SetUnstable(true) +} diff --git a/internal/language/language.go b/internal/language/language.go index b582be56f1..f42f5b2161 100644 --- a/internal/language/language.go +++ b/internal/language/language.go @@ -120,7 +120,7 @@ func MakeByName(name string) Language { } // MakeByNameAndVersion will retrieve a language by a given name and version. -func MakeByNameAndVersion(name, version string) (Language, error) { +func MakeByNameAndVersion(name, version string) Language { if strings.ToLower(name) == Python3.Requirement() { name = Python3.String() // Disambiguate python, preferring Python3. @@ -130,7 +130,7 @@ func MakeByNameAndVersion(name, version string) (Language, error) { name = Python2.String() } } - return MakeByName(name), nil + return MakeByName(name) } // MakeByText will retrieve a language by a given text diff --git a/internal/language/language_test.go b/internal/language/language_test.go index e6e48fda89..303cd0e38a 100644 --- a/internal/language/language_test.go +++ b/internal/language/language_test.go @@ -122,55 +122,44 @@ func TestMakeByNameAndVersion(t *testing.T) { version string } tests := []struct { - name string - args args - want Language - wantErr bool + name string + args args + want Language }{ { "Valid Python3 version", args{"python", "3.6.6"}, Python3, - false, }, { "Valid Python2 version", args{"python", "2.7.18"}, Python2, - false, }, { "Valid Python2 invalid patch", args{"python", "2.7.18.1"}, Python2, - false, }, { "Valid Python3 invalid patch", args{"python", "3.9"}, Python3, - false, }, { "Missing version", args{"python", ""}, Python3, - false, }, { "Valid Perl version", args{"perl", "5.28.1"}, Perl, - false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := MakeByNameAndVersion(tt.args.name, tt.args.version) - if (err != nil) != tt.wantErr { - t.Errorf("MakeByNameAndVersion() error = %v, wantErr %v", err, tt.wantErr) - return - } + got := MakeByNameAndVersion(tt.args.name, tt.args.version) if got != tt.want { t.Errorf("MakeByNameAndVersion() = %v, want %v", got, tt.want) } diff --git a/internal/locale/locales/en-us.yaml b/internal/locale/locales/en-us.yaml index 2582009493..c0f05d8556 100644 --- a/internal/locale/locales/en-us.yaml +++ b/internal/locale/locales/en-us.yaml @@ -1312,8 +1312,6 @@ arg_state_shell_namespace_description: other: The namespace of the project you wish to start a virtual environment shell/prompt for, or just the project name if previously used err_language_by_commit: other: Could not get language from commit ID {{.V0}} -err_make_language: - other: Could not create language from commit ID err_parse_project: other: Could not parse project file at {{.V0}} err_uninstall_privilege_mismatch: diff --git a/internal/runbits/checkout/checkout.go b/internal/runbits/checkout/checkout.go index 4d5d303ccb..ff2f439f9f 100644 --- a/internal/runbits/checkout/checkout.go +++ b/internal/runbits/checkout/checkout.go @@ -174,9 +174,5 @@ func getLanguage(commitID strfmt.UUID) (language.Language, error) { return language.Unset, locale.WrapError(err, "err_language_by_commit", "", string(commitID)) } - lang, err := language.MakeByNameAndVersion(modelLanguage.Name, modelLanguage.Version) - if err != nil { - return language.Unset, locale.WrapError(err, "err_make_language") - } - return lang, nil + return language.MakeByNameAndVersion(modelLanguage.Name, modelLanguage.Version), nil } diff --git a/internal/runners/initialize/init.go b/internal/runners/initialize/init.go index e04abe913b..62858c6660 100644 --- a/internal/runners/initialize/init.go +++ b/internal/runners/initialize/init.go @@ -151,15 +151,7 @@ func (r *Initialize) Run(params *RunParams) (rerr error) { return language.UnrecognizedLanguageError(languageName, language.RecognizedSupportedsNames()) } - lang, err := language.MakeByNameAndVersion(languageName, languageVersion) - if err != nil { - if inferred { - return locale.WrapError(err, "err_init_lang", "", languageName, languageVersion) - } else { - return locale.WrapInputError(err, "err_init_lang", "", languageName, languageVersion) - } - } - + lang := language.MakeByNameAndVersion(languageName, languageVersion) version, err := deriveVersion(lang, languageVersion) if err != nil { if inferred || !locale.IsInputError(err) { diff --git a/internal/runners/languages/search.go b/internal/runners/languages/search.go new file mode 100644 index 0000000000..b83c5b32d1 --- /dev/null +++ b/internal/runners/languages/search.go @@ -0,0 +1,42 @@ +package languages + +import ( + "github.com/ActiveState/cli/internal/errs" + "github.com/ActiveState/cli/internal/language" + "github.com/ActiveState/cli/internal/logging" + "github.com/ActiveState/cli/internal/output" + "github.com/ActiveState/cli/internal/primer" + "github.com/ActiveState/cli/pkg/platform/model" +) + +// Search manages the searching execution context. +type Search struct { + out output.Outputer +} + +// NewSearch prepares a search execution context for use. +func NewSearch(prime primer.Outputer) *Search { + return &Search{ + out: prime.Output(), + } +} + +// Run executes the search behavior. +func (s *Search) Run() error { + logging.Debug("Execute languages search") + + modelLanguages, err := model.FetchLanguages() + if err != nil { + return errs.Wrap(err, "Unable to fetch languages") + } + + supportedLanguages := []model.Language{} + for _, lang := range modelLanguages { + if language.MakeByNameAndVersion(lang.Name, lang.Version) == language.Unknown { + continue + } + supportedLanguages = append(supportedLanguages, lang) + } + s.out.Print(output.Prepare(supportedLanguages, supportedLanguages)) + return nil +} diff --git a/internal/runners/push/push.go b/internal/runners/push/push.go index 3f09da5723..08aae4592a 100644 --- a/internal/runners/push/push.go +++ b/internal/runners/push/push.go @@ -338,12 +338,7 @@ func fetchLanguage(commitID strfmt.UUID) (*language.Supported, string, error) { return nil, "", errs.Wrap(err, "Failed to retrieve language information for headless commit") } - l, err := language.MakeByNameAndVersion(lang.Name, lang.Version) - if err != nil { - return nil, "", errs.Wrap(err, "Failed to convert commit language to supported language") - } - - ls := language.Supported{Language: l} + ls := language.Supported{Language: language.MakeByNameAndVersion(lang.Name, lang.Version)} if !ls.Recognized() { return nil, "", locale.NewError("err_push_invalid_language", lang.Name) } diff --git a/test/integration/languages_int_test.go b/test/integration/languages_int_test.go index e97788bb26..8dae301141 100644 --- a/test/integration/languages_int_test.go +++ b/test/integration/languages_int_test.go @@ -97,6 +97,26 @@ func (suite *LanguagesIntegrationTestSuite) TestJSON() { cp.Expect(`[{"name":"Python","version":`) cp.ExpectExitCode(0) AssertValidJSON(suite.T(), cp) + + cp = ts.Spawn("languages", "search", "--output", "json") + cp.Expect(`[{"name":"perl","version":`) + cp.ExpectExitCode(0) + //AssertValidJSON(suite.T(), cp) // currently too big to fit in the terminal window for validation +} + +func (suite *LanguagesIntegrationTestSuite) TestSearch() { + suite.OnlyRunForTags(tagsuite.Languages) + ts := e2e.New(suite.T(), false) + defer ts.Close() + + cp := ts.Spawn("languages", "search") + cp.Expect("perl") + cp.Expect("5.32") + cp.Expect("python") + cp.Expect("3.11") + cp.Expect("ruby") + cp.Expect("3.2") + cp.ExpectExitCode(0) } func TestLanguagesIntegrationTestSuite(t *testing.T) {