Skip to content

Commit a3a4487

Browse files
committed
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.
1 parent ba48dcd commit a3a4487

File tree

2 files changed

+124
-0
lines changed

2 files changed

+124
-0
lines changed

cmd/download.go

+51
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,19 @@ func newDownload(flags *pflag.FlagSet, usrCfg *viper.Viper) (*download, error) {
162162
d.apibaseurl = usrCfg.GetString("apibaseurl")
163163
d.workspace = usrCfg.GetString("workspace")
164164

165+
if d.uuid == "" {
166+
if d.slug == "" {
167+
if _, slug, ok := trackAndSlugFromCwd(d.workspace); ok && slug != "" {
168+
d.slug = slug
169+
}
170+
}
171+
if d.track == "" && d.team == "" {
172+
if track, _, ok := trackAndSlugFromCwd(d.workspace); ok {
173+
d.track = track
174+
}
175+
}
176+
}
177+
165178
if err = d.needsSlugXorUUID(); err != nil {
166179
return nil, err
167180
}
@@ -349,6 +362,44 @@ func (sf solutionFile) relativePath() string {
349362
return filepath.FromSlash(file)
350363
}
351364

365+
func trackAndSlugFromCwd(workspace string) (string, string, bool) {
366+
cwd, err := os.Getwd()
367+
if err != nil {
368+
return "", "", false
369+
}
370+
371+
if !strings.HasPrefix(cwd, workspace) {
372+
return "", "", false
373+
}
374+
375+
remaining := strings.TrimPrefix(cwd, workspace)
376+
separator := string([]rune{os.PathSeparator})
377+
remaining = strings.TrimPrefix(remaining, separator)
378+
379+
components := strings.Split(remaining, separator)
380+
if len(components) == 0 {
381+
return "", "", false
382+
}
383+
384+
// We have at least one component, possibly more.
385+
// First is presumably the track, but check whether it actually is one.
386+
track := components[0]
387+
if !isTrack(track) {
388+
return "", "", false
389+
}
390+
391+
slug := ""
392+
if len(components) >= 2 {
393+
slug = components[1]
394+
}
395+
396+
return track, slug, true
397+
}
398+
399+
func isTrack(dir string) bool {
400+
return dir != "users" && dir != "teams"
401+
}
402+
352403
func setupDownloadFlags(flags *pflag.FlagSet) {
353404
flags.StringP("uuid", "u", "", "the solution UUID")
354405
flags.StringP("track", "t", "", "the track ID")

cmd/download_test.go

+73
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,79 @@ func TestDownloadWithoutFlags(t *testing.T) {
7777
}
7878
}
7979

80+
func TestDownloadInferringFlags(t *testing.T) {
81+
co := newCapturedOutput()
82+
co.override()
83+
defer co.reset()
84+
85+
originalCwd, err := os.Getwd()
86+
defer os.Chdir(originalCwd)
87+
assert.NoError(t, err)
88+
89+
testCases := []struct {
90+
cwd string
91+
flags map[string]string
92+
ok bool
93+
}{
94+
{
95+
cwd: "bogus-track",
96+
flags: map[string]string{"exercise": "bogus-exercise"},
97+
ok: true,
98+
},
99+
{
100+
cwd: "bogus-track/bogus-exercise",
101+
flags: nil,
102+
ok: true,
103+
},
104+
{
105+
cwd: "teams/bogus-team/bogus-track/bogus-exercise",
106+
flags: nil,
107+
ok: false,
108+
},
109+
{
110+
cwd: "users/bogus-user/bogus-track/bogus-exercise",
111+
flags: nil,
112+
ok: false,
113+
},
114+
}
115+
116+
for _, tc := range testCases {
117+
tmpDir, err := ioutil.TempDir("", "download-infer")
118+
defer os.RemoveAll(tmpDir)
119+
assert.NoError(t, err)
120+
121+
subdir := filepath.Join(tmpDir, tc.cwd)
122+
err = os.MkdirAll(subdir, 0755)
123+
assert.NoError(t, err)
124+
os.Chdir(subdir)
125+
126+
ts := fakeDownloadServer(strconv.FormatBool(true), "")
127+
defer ts.Close()
128+
129+
v := viper.New()
130+
v.Set("workspace", tmpDir)
131+
v.Set("apibaseurl", ts.URL)
132+
v.Set("token", "abc123")
133+
134+
cfg := config.Config{
135+
UserViperConfig: v,
136+
}
137+
flags := pflag.NewFlagSet("fake", pflag.PanicOnError)
138+
setupDownloadFlags(flags)
139+
for name, value := range tc.flags {
140+
flags.Set(name, value)
141+
}
142+
143+
err = runDownload(cfg, flags, []string{})
144+
if tc.ok {
145+
assert.NoError(t, err)
146+
assertDownloadedCorrectFiles(t, tmpDir)
147+
} else {
148+
assert.Error(t, err)
149+
}
150+
}
151+
}
152+
80153
func TestSolutionFile(t *testing.T) {
81154
testCases := []struct {
82155
name, file, expectedPath, expectedURL string

0 commit comments

Comments
 (0)