// Tracks lists available tracks. func Tracks(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } client := api.NewClient(c) tracks, err := client.Tracks() if err != nil { log.Fatal(err) } curr := user.NewCurriculum(tracks) fmt.Println("\nActive language tracks:") curr.Report(user.TrackActive) fmt.Println("\nInactive language tracks:") curr.Report(user.TrackInactive) msg := ` Related commands: exercism fetch (see 'exercism help fetch') exercism list (see 'exercism help list') ` fmt.Println(msg) }
// Status is a command that allows a user to view their progress in a given // language track. func Status(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } args := ctx.Args() if len(args) != 1 { fmt.Fprintf(os.Stderr, "Usage: exercism status TRACK_ID") os.Exit(1) } client := api.NewClient(c) trackID := args[0] status, err := client.Status(trackID) if err != nil { if err == api.ErrUnknownTrack { log.Fatalf("There is no track with ID '%s'.", trackID) } else { log.Fatal(err) } } fmt.Println(status) }
// List returns the full list of assignments for a given language func List(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } args := ctx.Args() if len(args) != 1 { msg := "Usage: exercism list LANGUAGE" log.Fatal(msg) } language := args[0] client := api.NewClient(c) problems, err := client.List(language) if err != nil { if err == api.ErrUnknownLanguage { log.Fatalf("The requested language '%s' is unknown", language) } log.Fatal(err) } for _, p := range problems { fmt.Printf("%s\n", p) } fmt.Printf("\n%s\n\n", msgExplainFetch) }
// Demo returns one problem for each active track. func Demo(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } client := api.NewClient(c) problems, err := client.Demo() if err != nil { log.Fatal(err) } if dirOpt := ctx.String("dir"); dirOpt != "" { c.SetDir(dirOpt) } fmt.Printf("Your exercises will be saved at: %s\n", c.Dir) hw := user.NewHomework(problems, c) if err := hw.Save(); err != nil { log.Fatal(err) } hw.Report(user.HWAll) fmt.Println("Next step: choose a language, read the README, and make the test suite pass.") }
// List returns the full list of assignments for a given track. func List(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } args := ctx.Args() if len(args) != 1 { msg := "Usage: exercism list TRACK_ID" log.Fatal(msg) } trackID := args[0] client := api.NewClient(c) problems, err := client.List(trackID) if err != nil { if err == api.ErrUnknownTrack { log.Fatalf("There is no track with ID '%s'.", trackID) } log.Fatal(err) } for _, p := range problems { fmt.Printf("%s\n", p) } fmt.Printf("\n%s\n\n", msgExplainFetch) }
// Fetch downloads exercism problems and writes them to disk. func Fetch(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } client := api.NewClient(c) problems, err := client.Fetch(ctx.Args()) if err != nil { log.Fatal(err) } submissionInfo, err := client.Submissions() if err != nil { log.Fatal(err) } if err := setSubmissionState(problems, submissionInfo); err != nil { log.Fatal(err) } hw := user.NewHomework(problems, c) if err := hw.Save(); err != nil { log.Fatal(err) } hw.Summarize(user.HWAll) }
// Unsubmit deletes an iteration from the api. // If no iteration is specified, the most recent iteration // is deleted. func Unsubmit(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } if !c.IsAuthenticated() { log.Fatal(msgPleaseAuthenticate) } client := api.NewClient(c) if err := client.Unsubmit(); err != nil { log.Fatal(err) } fmt.Println("Your most recent submission was successfully deleted.") }
// Open uses the given track and problem and opens it in the browser. func Open(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } client := api.NewClient(c) args := ctx.Args() if len(args) != 2 { fmt.Fprintf(os.Stderr, "Usage: exercism open TRACK_ID PROBLEM") os.Exit(1) } trackID := args[0] slug := args[1] submission, err := client.SubmissionURL(trackID, slug) if err != nil { log.Fatal(err) } url := submission.URL // Escape characters are not allowed by cmd/bash. switch runtime.GOOS { case "windows": url = strings.Replace(url, "&", `^&`, -1) default: url = strings.Replace(url, "&", `\&`, -1) } // The command to open the browser is OS-dependent. var cmd *exec.Cmd switch runtime.GOOS { case "darwin": cmd = exec.Command("open", url) case "freebsd", "linux", "netbsd", "openbsd": cmd = exec.Command("xdg-open", url) case "windows": cmd = exec.Command("cmd", "/c", "start", url) } if err := cmd.Run(); err != nil { log.Fatal(err) } }
// Fetch downloads exercism problems and writes them to disk. func Fetch(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } client := api.NewClient(c) problems, err := client.Fetch(ctx.Args()) if err != nil { log.Fatal(err) } submissionInfo, err := client.Submissions() if err != nil { log.Fatal(err) } if err := setSubmissionState(problems, submissionInfo); err != nil { log.Fatal(err) } dirs, err := filepath.Glob(filepath.Join(c.Dir, "*")) if err != nil { log.Fatal(err) } dirMap := make(map[string]bool) for _, dir := range dirs { dirMap[dir] = true } hw := user.NewHomework(problems, c) if len(ctx.Args()) == 0 { if err := hw.RejectMissingTracks(dirMap); err != nil { log.Fatal(err) } } if err := hw.Save(); err != nil { log.Fatal(err) } hw.Summarize(user.HWAll) }
// Restore returns a user's solved problems. func Restore(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } client := api.NewClient(c) problems, err := client.Restore() if err != nil { log.Fatal(err) } hw := user.NewHomework(problems, c) if err := hw.Save(); err != nil { log.Fatal(err) } hw.Summarize(user.HWNotSubmitted) }
// Status is a command that allows a user to view their progress in a given // language track. func Status(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } args := ctx.Args() if len(args) != 1 { log.Fatal("Usage: exercism status TRACK_ID") } client := api.NewClient(c) status, err := client.Status(args[0]) if err != nil { log.Fatal(err) } fmt.Println(status) }
// Unsubmit deletes the most recent submission from the API. func Unsubmit(ctx *cli.Context) { if len(ctx.Args()) > 0 { log.Fatal("\nThe unsubmit command does not take any arguments, it deletes the most recent submission.\n\nTo delete a different submission, you'll need to do it from the website.") } c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } if !c.IsAuthenticated() { log.Fatal(msgPleaseAuthenticate) } client := api.NewClient(c) if err := client.Unsubmit(); err != nil { log.Fatal(err) } fmt.Println("Your most recent submission was successfully deleted.") }
// Download returns specified iteration with its related problem. func Download(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } client := api.NewClient(c) args := ctx.Args() if len(args) != 1 { msg := "Usage: exercism download SUBMISSION_ID" log.Fatal(msg) } submission, err := client.Download(args[0]) if err != nil { log.Fatal(err) } path := filepath.Join(c.Dir, "solutions", submission.Username, submission.TrackID, submission.Slug, args[0]) if err := os.MkdirAll(path, 0755); err != nil { log.Fatal(err) } for name, contents := range submission.ProblemFiles { if err := ioutil.WriteFile(fmt.Sprintf("%s/%s", path, name), []byte(contents), 0755); err != nil { log.Fatalf("Unable to write file %s: %s", name, err) } } for name, contents := range submission.SolutionFiles { filename := strings.TrimPrefix(name, strings.ToLower("/"+submission.TrackID+"/"+submission.Slug+"/")) if err := ioutil.WriteFile(fmt.Sprintf("%s/%s", path, filename), []byte(contents), 0755); err != nil { log.Fatalf("Unable to write file %s: %s", name, err) } } fmt.Printf("Successfully downloaded submission.\n\nThe submission can be viewed at:\n %s\n\n", path) }
// Skip command allows a user to skip a specific problem func Skip(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } args := ctx.Args() if len(args) != 2 { msg := "Usage: exercism skip LANGUAGE SLUG" log.Fatal(msg) } var ( language = args[0] slug = args[1] ) client := api.NewClient(c) if err := client.Skip(language, slug); err != nil { log.Fatal(err) } fmt.Printf("Exercise %q in %q has been skipped.\n", slug, language) }
// Skip allows a user to skip a specific problem. func Skip(ctx *cli.Context) { c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } args := ctx.Args() if len(args) != 2 { msg := "Usage: exercism skip TRACK_ID PROBLEM" log.Fatal(msg) } var ( trackID = args[0] slug = args[1] ) client := api.NewClient(c) if err := client.Skip(trackID, slug); err != nil { log.Fatal(err) } fmt.Printf("Exercise %q in %q has been skipped.\n", slug, trackID) }
// Submit posts an iteration to the API. func Submit(ctx *cli.Context) { if len(ctx.Args()) == 0 { log.Fatal("Please enter a file name") } c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("Exercises dir: %s", c.Dir) dir, err := os.Getwd() if err != nil { log.Printf("Unable to get current working directory - %s", err) } else { log.Printf("Current dir: %s", dir) } } if !c.IsAuthenticated() { log.Fatal(msgPleaseAuthenticate) } dir, err := filepath.EvalSymlinks(c.Dir) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("eval symlinks (dir): %s", dir) } files := []string{} for _, filename := range ctx.Args() { if ctx.GlobalBool("verbose") { log.Printf("file name: %s", filename) } if isTest(filename) && !ctx.Bool("test") { log.Fatal("You're trying to submit a test file. If this is really what " + "you want, please pass the --test flag to exercism submit.") } if isREADME(filename) { log.Fatal("You cannot submit the README as a solution.") } if paths.IsDir(filename) { log.Fatal("Please specify each file that should be submitted, e.g. `exercism submit file1 file2 file3`.") } file, err := filepath.Abs(filename) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("absolute path: %s", file) } file, err = filepath.EvalSymlinks(file) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("eval symlinks (file): %s", file) } files = append(files, file) } iteration, err := api.NewIteration(dir, files) if err != nil { log.Fatalf("Unable to submit - %s", err) } iteration.Key = c.APIKey iteration.Comment = ctx.String("comment") client := api.NewClient(c) submission, err := client.Submit(iteration) if err != nil { log.Fatal(err) } fmt.Printf("%s - %s\n%s\n\n", submission.Language, submission.Name, submission.URL) }
// Submit posts an iteration to the API. func Submit(ctx *cli.Context) { if len(ctx.Args()) == 0 { log.Fatal("Please enter a file name") } c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("Exercises dir: %s", c.Dir) dir, err := os.Getwd() if err != nil { log.Printf("Unable to get current working directory - %s", err) } else { log.Printf("Current dir: %s", dir) } } if !c.IsAuthenticated() { log.Fatal(msgPleaseAuthenticate) } dir, err := filepath.EvalSymlinks(c.Dir) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("eval symlinks (dir): %s", dir) } files := []string{} for _, filename := range ctx.Args() { if ctx.GlobalBool("verbose") { log.Printf("file name: %s", filename) } if isTest(filename) && !ctx.Bool("test") { log.Fatal("You're trying to submit a test file. If this is really what " + "you want, please pass the --test flag to exercism submit.") } file, err := filepath.Abs(filename) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("absolute path: %s", file) } file, err = filepath.EvalSymlinks(file) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("eval symlinks (file): %s", file) } files = append(files, file) } iteration, err := api.NewIteration(dir, files) if err != nil { log.Fatalf("Unable to submit - %s", err) } iteration.Key = c.APIKey client := api.NewClient(c) submission, err := client.Submit(iteration) if err != nil { log.Fatal(err) } msg := ` Submitted %s in %s. Your submission can be found online at %s To get the next exercise, run "exercism fetch" again. ` fmt.Printf(msg, submission.Name, submission.Language, submission.URL) }
// Submit posts an iteration to the API. func Submit(ctx *cli.Context) { if len(ctx.Args()) == 0 { log.Fatal("Please enter a file name") } c, err := config.New(ctx.GlobalString("config")) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("Exercises dir: %s", c.Dir) dir, err := os.Getwd() if err != nil { log.Printf("Unable to get current working directory - %s", err) } else { log.Printf("Current dir: %s", dir) } } if !c.IsAuthenticated() { log.Fatal(msgPleaseAuthenticate) } dir, err := filepath.EvalSymlinks(c.Dir) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("eval symlinks (dir): %s", dir) } files := []string{} for _, filename := range ctx.Args() { if ctx.GlobalBool("verbose") { log.Printf("file name: %s", filename) } if isTest(filename) && !ctx.Bool("test") { log.Fatal("You're trying to submit a test file. If this is really what " + "you want, please pass the --test flag to exercism submit.") } if isREADME(filename) { log.Fatal("You cannot submit the README as a solution.") } file, err := filepath.Abs(filename) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("absolute path: %s", file) } file, err = filepath.EvalSymlinks(file) if err != nil { log.Fatal(err) } if ctx.GlobalBool("verbose") { log.Printf("eval symlinks (file): %s", file) } files = append(files, file) } iteration, err := api.NewIteration(dir, files) if err != nil { log.Fatalf("Unable to submit - %s", err) } iteration.Key = c.APIKey iteration.Comment = ctx.String("comment") client := api.NewClient(c) submission, err := client.Submit(iteration) if err != nil { log.Fatal(err) } msg := ` Submitted %s in %s. Your submission can be found online at %s ` if submission.Iteration == 1 { msg += ` To get the next exercise, run "exercism fetch" again. ` rand.Seed(time.Now().UTC().UnixNano()) phrases := []string{ "For bonus points", "Don't stop now: The fun's just begun", "Some tips to continue", } msg += fmt.Sprintf("\n## %s\n", phrases[rand.Intn(len(phrases))]) msg += tips } fmt.Printf(msg, submission.Name, submission.Language, submission.URL) }