Skip to content

Commit

Permalink
Refactor argument parsing to use custom structs
Browse files Browse the repository at this point in the history
  • Loading branch information
gnojus committed Feb 27, 2020
1 parent e0d3ef8 commit 14523b8
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 115 deletions.
4 changes: 2 additions & 2 deletions cf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"os"
"strings"

"github.com/fatih/color"
Expand Down Expand Up @@ -122,8 +123,7 @@ Options:
-h --help
--version`
usage = strings.Replace(usage, `$%version%$`, version, 1)

args, _ := docopt.Parse(usage, nil, true, fmt.Sprintf("Codeforces Tool (cf) %v", version), false)
args, _ := docopt.ParseArgs(usage, os.Args[1:], fmt.Sprintf("Codeforces Tool (cf) %v", version))
args[`{version}`] = version
color.Output = ansi.NewAnsiStdout()
configPath, _ = homedir.Expand(configPath)
Expand Down
18 changes: 9 additions & 9 deletions cmd/browser.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,32 +10,32 @@ import (
)

// Open command
func Open(args map[string]interface{}) error {
parsedArgs, err := parseArgs(args, map[string]bool{"<contest-id>": true, "<problem-id>": false})
func Open(args interface{}) error {
parsedArgs, err := parseArgs(args, ParseRequirement{ContestID: true})
if err != nil {
return err
}
contestID, problemID := parsedArgs["<contest-id>"], parsedArgs["<problem-id>"]
contestID, problemID := parsedArgs.ProblemID, parsedArgs.ProblemID
if problemID == "" {
return open.Run(client.ToGym(fmt.Sprintf(config.Instance.Host+"/contest/%v", contestID), contestID))
}
return open.Run(client.ToGym(fmt.Sprintf(config.Instance.Host+"/contest/%v/problem/%v", contestID, problemID), contestID))
}

// Stand command
func Stand(args map[string]interface{}) error {
parsedArgs, err := parseArgs(args, map[string]bool{"<contest-id>": true})
func Stand(args interface{}) error {
parsedArgs, err := parseArgs(args, ParseRequirement{ContestID: true})
if err != nil {
return err
}
contestID := parsedArgs["<contest-id>"]
contestID := parsedArgs.ContestID
return open.Run(client.ToGym(fmt.Sprintf(config.Instance.Host+"/contest/%v/standings", contestID), contestID))
}

// Sid command
func Sid(args map[string]interface{}) error {
parsedArgs, err := parseArgs(args, map[string]bool{"<contest-id>": false, "<submission-id>": false})
contestID, submissionID := parsedArgs["<contest-id>"], parsedArgs["<submission-id>"]
func Sid(args interface{}) error {
parsedArgs, err := parseArgs(args, ParseRequirement{})
contestID, submissionID := parsedArgs.ContestID, parsedArgs.SubmissionID
cfg := config.Instance
cln := client.Instance
if submissionID == "" {
Expand Down
7 changes: 4 additions & 3 deletions cmd/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,16 @@ import (
)

// Clone command
func Clone(args map[string]interface{}) error {
func Clone(args interface{}) error {
currentPath, err := os.Getwd()
if err != nil {
return err
}
cfg := config.Instance
cln := client.Instance
ac := args["ac"].(bool)
handle := args["<handle>"].(string)
parsedArgs, _ := parseArgs(args, ParseRequirement{})
ac := parsedArgs.Accepted
handle := parsedArgs.Handle

if err = cln.Clone(handle, currentPath, ac); err != nil {
if err = loginAgain(cfg, cln, err); err == nil {
Expand Down
149 changes: 91 additions & 58 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"errors"
"fmt"
"github.com/docopt/docopt-go"
"io/ioutil"
"os"
"path/filepath"
Expand All @@ -17,53 +18,91 @@ import (
)

// Eval args
func Eval(args map[string]interface{}) error {
if args["config"].(bool) {
return Config(args)
} else if args["submit"].(bool) {
func Eval(args docopt.Opts) error {
parsed := ParsedArgs{}
args.Bind(&parsed)
if parsed.Config {
return Config()
} else if parsed.Submit {
return Submit(args)
} else if args["list"].(bool) {
} else if parsed.List {
return List(args)
} else if args["parse"].(bool) {
} else if parsed.Parse {
return Parse(args)
} else if args["gen"].(bool) {
} else if parsed.Generate {
return Gen(args)
} else if args["test"].(bool) {
} else if parsed.Test {
return Test(args)
} else if args["watch"].(bool) {
} else if parsed.Watch {
return Watch(args)
} else if args["open"].(bool) {
} else if parsed.Open {
return Open(args)
} else if args["stand"].(bool) {
} else if parsed.Standings {
return Stand(args)
} else if args["sid"].(bool) {
} else if parsed.Sid {
return Sid(args)
} else if args["race"].(bool) {
} else if parsed.Race {
return Race(args)
} else if args["pull"].(bool) {
} else if parsed.Pull {
return Pull(args)
} else if args["clone"].(bool) {
} else if parsed.Clone {
return Clone(args)
} else if args["upgrade"].(bool) {
return Upgrade(args["{version}"].(string))
} else if parsed.Upgrade {
return Upgrade(parsed.Version)
}
return nil
}

func parseArgs(args map[string]interface{}, required map[string]bool) (map[string]string, error) {
result := make(map[string]string)
type ParseRequirement struct {
ContestID, ProblemID, SubmissionID, Filename, Alias, Username bool
}

type ParsedArgs struct {
ContestID string `docopt:"<url | contest-id>"`
ProblemID string `docopt:"<problem-id>"`
SubmissionID string `docopt:"<submission-id>"`
Filename string `docopt:"<filename>"`
Alias string `docopt:"<alias>"`
Accepted bool `docopt:"ac"`
All bool `docopt:"all"`
Handle string `docopt:"<handle>"`
Version string `docopt:"{version}"`
Config bool `docopt:"config"`
Submit bool `docopt:"submit"`
List bool `docopt:"list"`
Parse bool `docopt:"parse"`
Generate bool `docopt:"gen"`
Test bool `docopt:"test"`
Watch bool `docopt:"watch"`
Open bool `docopt:"open"`
Standings bool `docopt:"stand"`
Sid bool `docopt:"sid"`
Race bool `docopt:"race"`
Pull bool `docopt:"pull"`
Clone bool `docopt:"clone"`
Upgrade bool `docopt:"upgrade"`
ContestRootPath string
}

func parseArgs(args interface{}, required ParseRequirement) (ParsedArgs, error) {
opts, ok := args.(docopt.Opts)
result := ParsedArgs{}
if !ok {
return result, errors.New("args must be docopt.Opts type")
}
opts.Bind(&result)
contestID, problemID, lastDir := "", "", ""
path, err := os.Getwd()
if err != nil {
return nil, err
return result, err
}
result["contestRootPath"] = path
result.ContestRootPath = path
for {
c := filepath.Base(path)
if _, err := strconv.Atoi(c); err == nil {
contestID, problemID = c, strings.ToLower(lastDir)
if _, ok := args["<url | contest-id>"].(string); !ok {
result["contestRootPath"] = filepath.Dir(path)
if result.ContestID == "" {
result.ContestRootPath = filepath.Dir(path)
}
break
}
Expand All @@ -72,47 +111,41 @@ func parseArgs(args map[string]interface{}, required map[string]bool) (map[strin
}
path, lastDir = filepath.Dir(path), c
}
if p, ok := args["<problem-id>"].(string); ok {
problemID = strings.ToLower(p)
if result.ProblemID != "" {
problemID = strings.ToLower(result.ProblemID)
}
if c, ok := args["<url | contest-id>"].(string); ok {
if util.IsUrl(c) {
parsed, err := parseUrl(c)
if err != nil {
return nil, err
}
if value, ok := parsed["contestID"]; ok {
contestID = value
}
if value, ok := parsed["problemID"]; ok {
problemID = strings.ToLower(value)
}
} else if _, err := strconv.Atoi(c); err == nil {
contestID = c
if util.IsUrl(result.ContestID) {
parsed, err := parseUrl(result.ContestID)
if err != nil {
return result, err
}
}
if req, ok := required["<contest-id>"]; ok {
result["<contest-id>"] = contestID
if contestID == "" && req {
return nil, errors.New("Unable to find <contest-id>")
if value, ok := parsed["contestID"]; ok {
contestID = value
}
}
if req, ok := required["<problem-id>"]; ok {
result["<problem-id>"] = problemID
if problemID == "" && req {
return nil, errors.New("Unable to find <problem-id>")
if value, ok := parsed["problemID"]; ok {
problemID = strings.ToLower(value)
}
} else if _, err := strconv.Atoi(result.ContestID); err == nil {
contestID = result.ContestID
}
for key, req := range required {
if _, ok := result[key]; ok {
continue
}
value, ok := args[key].(string)
if req && !ok {
return nil, errors.New("Unable to find " + key)
}
result[key] = value
result.ContestID = contestID
result.ProblemID = problemID
if required.ContestID && contestID == "" {
return result, errors.New("Unable to find <contest-id>")
}
if required.ProblemID && problemID == "" {
return result, errors.New("Unable to find <problem-id>")
}
if required.SubmissionID && result.SubmissionID == "" {
return result, errors.New("Unable to find <submission-id>")
}
if required.Alias && result.Alias == "" {
return result, errors.New("Unable to find <alias>")
}
if required.Filename && result.Filename == "" {
return result, errors.New("Unable to find <filename>")
}

return result, nil
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// Config command
func Config(args map[string]interface{}) error {
func Config() error {
cfg := config.Instance
cln := client.Instance
color.Cyan("Configure the tool")
Expand Down
7 changes: 4 additions & 3 deletions cmd/gen.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,16 +56,17 @@ func gen(source, currentPath, ext string) error {
}

// Gen command
func Gen(args map[string]interface{}) error {
func Gen(args interface{}) error {
cfg := config.Instance
cln := client.Instance
if len(cfg.Template) == 0 {
return errors.New("You have to add at least one code template by `cf config`")
}

parsedArgs, _ := parseArgs(args, ParseRequirement{})
alias := parsedArgs.Alias
var path string

if alias, ok := args["<alias>"].(string); ok {
if alias != "" {
templates := cfg.TemplateByAlias(alias)
if len(templates) < 1 {
return fmt.Errorf("Cannot find any template with alias %v", alias)
Expand Down
6 changes: 3 additions & 3 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ import (
)

// List command
func List(args map[string]interface{}) error {
parsedArgs, err := parseArgs(args, map[string]bool{"<contest-id>": true})
func List(args interface{}) error {
parsedArgs, err := parseArgs(args, ParseRequirement{ContestID: true})
if err != nil {
return err
}
contestID := parsedArgs["<contest-id>"]
contestID := parsedArgs.ContestID
cfg := config.Instance
cln := client.Instance
problems, err := cln.StatisContest(contestID)
Expand Down
8 changes: 4 additions & 4 deletions cmd/parse.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (
"github.com/xalanq/cf-tool/config"
)

func _Parse(contestID string, problemID string, contestRootPath string) error {
func _parse(contestID string, problemID string, contestRootPath string) error {
cfg := config.Instance
cln := client.Instance
source := ""
Expand Down Expand Up @@ -68,10 +68,10 @@ func _Parse(contestID string, problemID string, contestRootPath string) error {
}

// Parse command
func Parse(args map[string]interface{}) error {
parsedArgs, err := parseArgs(args, map[string]bool{"<contest-id>": true, "<problem-id>": false})
func Parse(args interface{}) error {
parsedArgs, err := parseArgs(args, ParseRequirement{ContestID: true, ProblemID: false})
if err != nil {
return err
}
return _Parse(parsedArgs["<contest-id>"], parsedArgs["<problem-id>"], parsedArgs["contestRootPath"])
return _parse(parsedArgs.ContestID, parsedArgs.ProblemID, parsedArgs.Filename)
}
11 changes: 5 additions & 6 deletions cmd/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,18 @@ import (
)

// Pull command
func Pull(args map[string]interface{}) error {
func Pull(args interface{}) error {
cfg := config.Instance
cln := client.Instance
ac := args["ac"].(bool)
var err error
work := func() error {
parsedArgs, err := parseArgs(args, map[string]bool{"<contest-id>": true, "<problem-id>": false})
parsedArgs, err := parseArgs(args, ParseRequirement{ContestID: true})
if err != nil {
return err
}
contestID, problemID := parsedArgs["<contest-id>"], parsedArgs["<problem-id>"]
path := filepath.Join(parsedArgs["contestRootPath"], contestID, problemID)
return cln.PullContest(contestID, problemID, path, ac)
contestID, problemID := parsedArgs.ContestID, parsedArgs.ProblemID
path := filepath.Join(parsedArgs.ContestRootPath, contestID, problemID)
return cln.PullContest(contestID, problemID, path, parsedArgs.Accepted)
}
if err = work(); err != nil {
if err = loginAgain(cfg, cln, err); err == nil {
Expand Down
8 changes: 4 additions & 4 deletions cmd/race.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@ import (
)

// Race command
func Race(args map[string]interface{}) error {
parsedArgs, err := parseArgs(args, map[string]bool{"<contest-id>": true})
func Race(args interface{}) error {
parsedArgs, err := parseArgs(args, ParseRequirement{ContestID: true})
if err != nil {
return err
}
contestID := parsedArgs["<contest-id>"]
contestID := parsedArgs.ContestID
cfg := config.Instance
cln := client.Instance
if err = cln.RaceContest(contestID); err != nil {
Expand All @@ -29,5 +29,5 @@ func Race(args map[string]interface{}) error {
time.Sleep(1)
open.Run(client.ToGym(fmt.Sprintf(cfg.Host+"/contest/%v", contestID), contestID))
open.Run(client.ToGym(fmt.Sprintf(cfg.Host+"/contest/%v/problems", contestID), contestID))
return _Parse(contestID, "", parsedArgs["contestRootPath"])
return _parse(contestID, "", parsedArgs.ContestRootPath)
}
Loading

0 comments on commit 14523b8

Please sign in to comment.