Skip to content

Commit 883a253

Browse files
committedOct 30, 2019
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 883a253

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed
 

‎cmd/download.go

+53
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,46 @@ func (sf solutionFile) relativePath() string {
349362
return filepath.FromSlash(file)
350363
}
351364

365+
// trackAndSlugFromCwd infers track and slug from current working directory,
366+
// by finding the two path components coming immediately after the workspace.
367+
func trackAndSlugFromCwd(workspace string) (string, string, bool) {
368+
cwd, err := os.Getwd()
369+
if err != nil {
370+
return "", "", false
371+
}
372+
373+
if !strings.HasPrefix(cwd, workspace) {
374+
return "", "", false
375+
}
376+
377+
afterWorkspace := strings.TrimPrefix(cwd, workspace)
378+
separator := string([]rune{os.PathSeparator})
379+
afterWorkspace = strings.TrimPrefix(afterWorkspace, separator)
380+
381+
components := strings.Split(afterWorkspace, separator)
382+
if len(components) == 0 {
383+
return "", "", false
384+
}
385+
386+
// We have at least one component, possibly more.
387+
// First is presumably the track, but check whether it actually is one.
388+
track := components[0]
389+
if !isTrack(track) {
390+
return "", "", false
391+
}
392+
393+
slug := ""
394+
if len(components) >= 2 {
395+
slug = components[1]
396+
}
397+
398+
return track, slug, true
399+
}
400+
401+
func isTrack(dir string) bool {
402+
return dir != "users" && dir != "teams"
403+
}
404+
352405
func setupDownloadFlags(flags *pflag.FlagSet) {
353406
flags.StringP("uuid", "u", "", "the solution UUID")
354407
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)