Skip to content

Commit 47d6fd4

Browse files
committed
Add very basic track endpoint
1 parent db0f990 commit 47d6fd4

5 files changed

Lines changed: 166 additions & 0 deletions

File tree

api/api.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,28 @@ func Unsubmit(url string) error {
125125
}
126126
return fmt.Errorf("failed to unsubmit - %s", pe.Error)
127127
}
128+
129+
// Tracks gets the current list of active and inactive language tracks.
130+
func Tracks(url string) ([]*Track, error) {
131+
req, err := http.NewRequest("GET", url, nil)
132+
if err != nil {
133+
return []*Track{}, err
134+
}
135+
req.Header.Set("User-Agent", UserAgent)
136+
137+
res, err := http.DefaultClient.Do(req)
138+
if err != nil {
139+
return []*Track{}, err
140+
}
141+
defer res.Body.Close()
142+
143+
var payload struct {
144+
Tracks []*Track
145+
}
146+
dec := json.NewDecoder(res.Body)
147+
err = dec.Decode(&payload)
148+
if err != nil {
149+
return []*Track{}, err
150+
}
151+
return payload.Tracks, nil
152+
}

api/track.go

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package api
2+
3+
import "fmt"
4+
5+
// Track is a collection of problems in a given language.
6+
type Track struct {
7+
ID string `json:"id"`
8+
Language string `json:"language"`
9+
Active bool `json:"active"`
10+
Problems []string `json:"problems"`
11+
}
12+
13+
// Len lists the number of problems a track has.
14+
func (t *Track) Len() int {
15+
return len(t.Problems)
16+
}
17+
18+
func (t *Track) String() string {
19+
return fmt.Sprintf("%s (%s)", t.Language, t.ID)
20+
}

cmd/tracks.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package cmd
2+
3+
import (
4+
"fmt"
5+
"log"
6+
7+
"github.com/codegangsta/cli"
8+
"github.com/exercism/cli/api"
9+
"github.com/exercism/cli/config"
10+
"github.com/exercism/cli/user"
11+
)
12+
13+
// Tracks lists available tracks.
14+
func Tracks(ctx *cli.Context) {
15+
c, err := config.Read(ctx.GlobalString("config"))
16+
if err != nil {
17+
log.Fatal(err)
18+
}
19+
20+
tracks, err := api.Tracks(fmt.Sprintf("%s/tracks", c.XAPI))
21+
if err != nil {
22+
log.Fatal(err)
23+
}
24+
25+
curr := user.NewCurriculum(tracks)
26+
fmt.Println("\nActive language tracks:")
27+
curr.Report(user.TrackActive)
28+
fmt.Println("\nInactive language tracks:")
29+
curr.Report(user.TrackInactive)
30+
31+
// TODO: implement `list` command to list problems in a track
32+
msg := `
33+
Related commands:
34+
exercism fetch (see 'exercism help fetch')
35+
`
36+
fmt.Println(msg)
37+
}

main.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const (
2525
descRestore = "Restores completed and current problems on from exercism.io, along with your most recent iteration for each."
2626
descSubmit = "Submits a new iteration to a problem on exercism.io."
2727
descUnsubmit = "Deletes the most recently submitted iteration."
28+
descTracks = "List the available language tracks"
2829
descLogin = "DEPRECATED: Interactively saves exercism.io api credentials."
2930
descLogout = "DEPRECATED: Clear exercism.io api credentials"
3031

@@ -112,6 +113,12 @@ func main() {
112113
Usage: descUnsubmit,
113114
Action: cmd.Unsubmit,
114115
},
116+
{
117+
Name: "tracks",
118+
ShortName: "t",
119+
Usage: descTracks,
120+
Action: cmd.Tracks,
121+
},
115122
}
116123
if err := app.Run(os.Args); err != nil {
117124

user/curriculum.go

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package user
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
7+
"github.com/exercism/cli/api"
8+
)
9+
10+
// Status is the status of a track (active/inactive).
11+
type Status bool
12+
13+
const (
14+
// TrackActive represents an active track.
15+
// Problems from active tracks will be delivered with the `fetch` command.
16+
TrackActive Status = true
17+
// TrackInactive represents an inactive track.
18+
// It is possible to fetch problems from an inactive track, and
19+
// submit them to the website, but these will not automatically be
20+
// delivered in the global `fetch` command.
21+
TrackInactive Status = false
22+
)
23+
24+
// Curriculum is a collection of language tracks.
25+
type Curriculum struct {
26+
Tracks []*api.Track
27+
wLang int
28+
wID int
29+
}
30+
31+
// NewCurriculum returns a collection of language tracks.
32+
func NewCurriculum(tracks []*api.Track) *Curriculum {
33+
return &Curriculum{Tracks: tracks}
34+
}
35+
36+
// Report creates a table of the tracks that have the requested status.
37+
func (cur *Curriculum) Report(status Status) {
38+
for _, track := range cur.Tracks {
39+
if Status(track.Active) == status {
40+
fmt.Println(
41+
" ",
42+
track.Language,
43+
strings.Repeat(" ", cur.lenLang()-len(track.Language)+1),
44+
track.ID,
45+
strings.Repeat(" ", cur.lenID()-len(track.ID)+1),
46+
track.Len(),
47+
"problems",
48+
)
49+
}
50+
}
51+
}
52+
53+
func (cur *Curriculum) lenLang() int {
54+
if cur.wLang > 0 {
55+
return cur.wLang
56+
}
57+
58+
for _, track := range cur.Tracks {
59+
if len(track.Language) > cur.wLang {
60+
cur.wLang = len(track.Language)
61+
}
62+
}
63+
return cur.wLang
64+
}
65+
66+
func (cur *Curriculum) lenID() int {
67+
if cur.wID > 0 {
68+
return cur.wID
69+
}
70+
71+
for _, track := range cur.Tracks {
72+
if len(track.ID) > cur.wID {
73+
cur.wID = len(track.ID)
74+
}
75+
}
76+
return cur.wID
77+
}

0 commit comments

Comments
 (0)