From 485a3bf96354d10ec4030fdb57834695933546c7 Mon Sep 17 00:00:00 2001 From: Peter Tseng Date: Sun, 11 Nov 2018 02:05:05 +0000 Subject: [PATCH] download: Infer track and exercise slug from CWD If I'm in the `exercism/c` directory and invoke: exercism download --exercise hello-world I would like `--track c` to be inferred. If I'm in the `exercism/c/hello-world` directory and invoke without arguments: exercism download I would like both `--track c` and `--exercise hello-world` to be inferred. --- cmd/download.go | 50 ++++++++++++++++++++++++++++++ cmd/download_test.go | 73 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/cmd/download.go b/cmd/download.go index e4c19377b..dde0cd80b 100644 --- a/cmd/download.go +++ b/cmd/download.go @@ -162,6 +162,19 @@ func newDownload(flags *pflag.FlagSet, usrCfg *viper.Viper) (*download, error) { d.apibaseurl = usrCfg.GetString("apibaseurl") d.workspace = usrCfg.GetString("workspace") + if d.uuid == "" { + if d.slug == "" { + if _, slug, ok := trackAndSlugFromCwd(d.workspace); ok && slug != "" { + d.slug = slug + } + } + if d.track == "" && d.team == "" { + if track, _, ok := trackAndSlugFromCwd(d.workspace); ok { + d.track = track + } + } + } + if err = d.needsSlugXorUUID(); err != nil { return nil, err } @@ -349,6 +362,43 @@ func (sf solutionFile) relativePath() string { return filepath.FromSlash(file) } +func trackAndSlugFromCwd(workspace string) (string, string, bool) { + cwd, err := os.Getwd() + if err != nil { + return "", "", false + } + + if !strings.HasPrefix(cwd, workspace) { + return "", "", false + } + + remaining := strings.TrimPrefix(cwd, workspace) + remaining = strings.TrimPrefix(remaining, os.PathSeparator) + + components := strings.Split(remaining, os.PathSeparator) + if len(components) == 0 { + return "", "", false + } + + // We have at least one component, possibly more. + // First is presumably the track, but check whether it actually is one. + track := components[0] + if !isTrack(track) { + return "", "", false + } + + slug := "" + if len(components) >= 2 { + slug = components[1] + } + + return track, slug, true +} + +func isTrack(dir string) bool { + return dir != "users" && dir != "teams" +} + func setupDownloadFlags(flags *pflag.FlagSet) { flags.StringP("uuid", "u", "", "the solution UUID") flags.StringP("track", "t", "", "the track ID") diff --git a/cmd/download_test.go b/cmd/download_test.go index 2eb0418df..c7d894451 100644 --- a/cmd/download_test.go +++ b/cmd/download_test.go @@ -77,6 +77,79 @@ func TestDownloadWithoutFlags(t *testing.T) { } } +func TestDownloadInferringFlags(t *testing.T) { + co := newCapturedOutput() + co.override() + defer co.reset() + + originalCwd, err := os.Getwd() + defer os.Chdir(originalCwd) + assert.NoError(t, err) + + testCases := []struct { + cwd string + flags map[string]string + ok bool + }{ + { + cwd: "bogus-track", + flags: map[string]string{"exercise": "bogus-exercise"}, + ok: true, + }, + { + cwd: "bogus-track/bogus-exercise", + flags: nil, + ok: true, + }, + { + cwd: "teams/bogus-team/bogus-track/bogus-exercise", + flags: nil, + ok: false, + }, + { + cwd: "users/bogus-user/bogus-track/bogus-exercise", + flags: nil, + ok: false, + }, + } + + for _, tc := range testCases { + tmpDir, err := ioutil.TempDir("", "download-infer") + defer os.RemoveAll(tmpDir) + assert.NoError(t, err) + + subdir := filepath.Join(tmpDir, tc.cwd) + err = os.MkdirAll(subdir, 0755) + assert.NoError(t, err) + os.Chdir(subdir) + + ts := fakeDownloadServer(strconv.FormatBool(true), "") + defer ts.Close() + + v := viper.New() + v.Set("workspace", tmpDir) + v.Set("apibaseurl", ts.URL) + v.Set("token", "abc123") + + cfg := config.Config{ + UserViperConfig: v, + } + flags := pflag.NewFlagSet("fake", pflag.PanicOnError) + setupDownloadFlags(flags) + for name, value := range tc.flags { + flags.Set(name, value) + } + + err = runDownload(cfg, flags, []string{}) + if tc.ok { + assert.NoError(t, err) + assertDownloadedCorrectFiles(t, tmpDir) + } else { + assert.Error(t, err) + } + } +} + func TestSolutionFile(t *testing.T) { testCases := []struct { name, file, expectedPath, expectedURL string