Skip to content
This repository was archived by the owner on Aug 12, 2022. It is now read-only.

Commit 38c6d50

Browse files
authored
make ach create command use ach config to create a contest directory (#66)
1 parent 401eb81 commit 38c6d50

File tree

10 files changed

+149
-78
lines changed

10 files changed

+149
-78
lines changed

docs/cmd/ach.md

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cmd/ach_contest.md

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cmd/ach_contest_create.md

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cmd/ach_test.md

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/cmd/ach_version.md

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ require (
99
github.com/spf13/viper v1.7.0
1010
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
1111
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
12+
gopkg.in/yaml.v2 v2.2.8
1213
)

internal/cmd/ach/ach.go

Lines changed: 49 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@ package ach
33
import (
44
"fmt"
55
"log"
6-
"os"
6+
"os/user"
77
"path"
8+
"path/filepath"
9+
"strings"
810

911
"github.com/spf13/cobra"
1012
"github.com/spf13/viper"
@@ -27,8 +29,18 @@ func NewAchCmd() *cobra.Command {
2729
Long: `ach automates routine work you does when you participate AtCoder contests. `,
2830
}
2931

30-
cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ach/config.yaml)")
31-
cmd.PersistentFlags().StringVar(&taskCfgFile, "task-config", "", "task config file (default is ./achTaskConfig.yaml")
32+
defaultConfigFile := path.Join("$HOME", ".ach", "config.yaml")
33+
34+
cmd.PersistentFlags().StringVar(
35+
&cfgFile,
36+
"config",
37+
defaultConfigFile,
38+
"config file")
39+
cmd.PersistentFlags().StringVar(
40+
&taskCfgFile,
41+
"task-config",
42+
"./achTaskConfig.yaml",
43+
"task config file")
3244

3345
registerSubcommands(cmd)
3446

@@ -51,49 +63,52 @@ func initConfig() {
5163
}
5264

5365
func readAppConfig() {
54-
var configFileName string
55-
56-
if cfgFile != "" {
57-
viper.SetConfigName(cfgFile)
58-
configFileName = cfgFile
59-
} else {
60-
home, err := os.UserHomeDir()
61-
if err != nil {
62-
log.Fatal(err)
63-
}
64-
65-
viper.AddConfigPath(path.Join(home, ".ach"))
66-
viper.SetConfigName("config")
67-
configFileName = "config"
66+
v := viper.New()
67+
68+
user, err := user.Current()
69+
if err != nil {
70+
log.Fatal(fmt.Errorf("NewAchCmd: %w", err))
71+
}
72+
73+
home := user.HomeDir
74+
75+
homeReplacedCfgFile := strings.Replace(cfgFile, "$HOME", home, 1)
76+
77+
absCfgFile, err := filepath.Abs(homeReplacedCfgFile)
78+
if err != nil {
79+
log.Fatal(fmt.Errorf("failed to convert config to its absolute path: %w", err))
6880
}
6981

70-
if err := viper.ReadInConfig(); err != nil {
71-
log.Fatal(fmt.Errorf("failed to read app config %s: %w", configFileName, err))
82+
v.SetConfigName(strings.TrimSuffix(absCfgFile, path.Ext(absCfgFile)))
83+
v.AddConfigPath("/")
84+
85+
if err := v.ReadInConfig(); err != nil {
86+
log.Fatal(fmt.Errorf("failed to read app config %s: %w", absCfgFile, err))
7287
}
7388

74-
if err := viper.UnmarshalExact(&config.GlobalAppConfig); err != nil {
75-
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", configFileName, err))
89+
if err := v.UnmarshalExact(&config.GlobalAppConfig); err != nil {
90+
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", absCfgFile, err))
7691
}
92+
93+
config.GlobalAppConfig.ConfigDir = filepath.Dir(absCfgFile)
7794
}
7895

7996
func readTaskConfig() {
80-
var configFileName string
97+
v := viper.New()
8198

82-
if taskCfgFile != "" {
83-
viper.SetConfigFile(taskCfgFile)
84-
85-
configFileName = cfgFile
86-
} else {
87-
viper.AddConfigPath(".")
88-
viper.SetConfigName("achTaskConfig")
89-
configFileName = "achTaskConfig"
99+
absTaskCfgFile, err := filepath.Abs(taskCfgFile)
100+
if err != nil {
101+
log.Fatal(fmt.Errorf("failed to convert task config to its absolute path: %w", err))
90102
}
91103

92-
if err := viper.ReadInConfig(); err != nil {
93-
log.Print(fmt.Errorf("failed to read task config %s: %w", configFileName, err))
104+
v.SetConfigName(strings.TrimSuffix(absTaskCfgFile, path.Ext(absTaskCfgFile)))
105+
v.AddConfigPath("/")
106+
107+
if err := v.ReadInConfig(); err != nil {
108+
return
94109
}
95110

96-
if err := viper.UnmarshalExact(&config.GlobalTaskConfig); err != nil {
97-
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", configFileName, err))
111+
if err := v.UnmarshalExact(&config.GlobalTaskConfig); err != nil {
112+
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", absTaskCfgFile, err))
98113
}
99114
}

internal/cmd/ach/contest/create/create.go

Lines changed: 74 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ package create
33
import (
44
"fmt"
55
"log"
6+
"os"
67
"os/exec"
7-
"os/user"
88
"path"
99

1010
"github.com/spf13/cobra"
11+
"github.com/yuchiki/atcoderHelper/internal/config"
12+
yaml "gopkg.in/yaml.v2"
1113
)
1214

1315
// NewContestCreateCmd returns a new contest create command.
@@ -35,55 +37,101 @@ D is for directory.
3537
}
3638

3739
func runE(cmd *cobra.Command, args []string) error {
38-
user, _ := user.Current()
39-
templateDirName := path.Join(user.HomeDir, "projects", "private", "atcoder", "D")
40-
taskNames := []string{"A", "B", "C", "D", "E", "F"}
40+
template, err := config.GetDefaultTemplate()
41+
if err != nil {
42+
return err
43+
}
4144

45+
taskNames := []string{"A", "B", "C", "D", "E", "F"}
4246
contestName := args[0]
4347

44-
err := exec.Command("mkdir", contestName).Run()
48+
output, err := exec.Command("mkdir", contestName).CombinedOutput()
4549
if err != nil {
46-
return err
50+
return fmt.Errorf("%s: %w", output, err)
4751
}
4852

53+
absTemplateDir := getAbsTemplateDirectory(template.TemplateDirectory)
54+
4955
for _, taskName := range taskNames {
5056
taskDirName := path.Join(contestName, taskName)
51-
if output, err := exec.Command("cp", "-r", templateDirName, taskDirName).Output(); err != nil {
57+
if output, err := exec.Command("cp", "-r", absTemplateDir, taskDirName).CombinedOutput(); err != nil {
5258
fmt.Print(output)
5359

60+
return fmt.Errorf("%s: %w", output, err)
61+
}
62+
63+
taskConfig := config.TaskConfig{
64+
ContestID: contestName,
65+
TaskID: taskName,
66+
Template: config.GlobalAppConfig.DefaultTemplate,
67+
}
68+
69+
taskConfigYaml, err := yaml.Marshal(taskConfig)
70+
if err != nil {
71+
return err
72+
}
73+
74+
taskConfigName := path.Join(taskDirName, "achTaskConfig.yaml")
75+
76+
taskConfigFile, err := os.Create(taskConfigName)
77+
if err != nil {
78+
return err
79+
}
80+
defer taskConfigFile.Close()
81+
82+
_, err = taskConfigFile.Write(taskConfigYaml)
83+
if err != nil {
5484
return err
5585
}
5686

5787
sampleDirName := path.Join(taskDirName, "sampleCases")
58-
if output, err := exec.Command("mkdir", sampleDirName).Output(); err != nil {
59-
fmt.Print(output)
6088

89+
err = createSampleCases(sampleDirName, 5)
90+
if err != nil {
6191
return err
6292
}
93+
}
6394

64-
for i := 1; i <= 5; i++ {
65-
inputFileName := path.Join(sampleDirName, fmt.Sprintf("case%d.input", i))
95+
return nil
96+
}
6697

67-
output, err := exec.Command( //nolint:gosec // TODO: fix this and all the execs.
68-
"bash",
69-
"-c",
70-
fmt.Sprintf(`echo "[skip ach test]" > %s`, inputFileName)).
71-
Output()
72-
if err != nil {
73-
fmt.Printf("%s can not be initialized", inputFileName)
74-
fmt.Print(output)
98+
func createSampleCases(sampleDirName string, n int) error {
99+
if output, err := exec.Command("mkdir", sampleDirName).Output(); err != nil {
100+
fmt.Print(output)
75101

76-
return err
77-
}
102+
return err
103+
}
104+
105+
for i := 1; i <= n; i++ {
106+
inputFileName := path.Join(sampleDirName, fmt.Sprintf("case%d.input", i))
78107

79-
outputFileName := path.Join(sampleDirName, fmt.Sprintf("case%d.expected", i))
108+
output, err := exec.Command( //nolint:gosec // TODO: fix this and all the execs.
109+
"bash",
110+
"-c",
111+
fmt.Sprintf(`echo "[skip ach test]" > %s`, inputFileName)).
112+
CombinedOutput()
113+
if err != nil {
114+
fmt.Printf("%s can not be initialized", inputFileName)
115+
fmt.Print(output)
80116

81-
err = exec.Command("touch", outputFileName).Run()
82-
if err != nil {
83-
return err
84-
}
117+
return err
118+
}
119+
120+
outputFileName := path.Join(sampleDirName, fmt.Sprintf("case%d.expected", i))
121+
122+
err = exec.Command("touch", outputFileName).Run()
123+
if err != nil {
124+
return err
85125
}
86126
}
87127

88128
return nil
89129
}
130+
131+
func getAbsTemplateDirectory(templateDir string) string {
132+
if path.IsAbs(templateDir) {
133+
return templateDir
134+
}
135+
136+
return path.Join(config.GlobalAppConfig.ConfigDir, templateDir)
137+
}

internal/config/config.go

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,14 @@ type AppConfig struct {
2626
Username string
2727
Languages []Language
2828
Templates []Template
29-
DefaultTemplate string `mapstructure:"default-template"`
29+
ConfigDir string `mapstructure:"-" yaml:"-"`
30+
DefaultTemplate string `mapstructure:"default-template" yaml:"default-template"`
3031
}
3132

3233
// Language is an information of a programming language used when solving a task.
3334
type Language struct {
3435
Name string
35-
AtCoderName string `mapstructure:"atcoder-name"`
36+
AtCoderName string `mapstructure:"atcoder-name" yaml:"atcoder-name"`
3637
Build string
3738
Run string
3839
}
@@ -41,14 +42,14 @@ type Language struct {
4142
type Template struct {
4243
Name string
4344
Language string
44-
TemplateDirectory string `mapstructure:"template-directory"`
45-
SourceFile string `mapstructure:"source-file"`
45+
TemplateDirectory string `mapstructure:"template-directory" yaml:"template-directory"`
46+
SourceFile string `mapstructure:"source-file" yaml:"source-file"`
4647
}
4748

4849
// TaskConfig is a configuration for each task.
4950
type TaskConfig struct {
50-
ContestID string `mapstructure:"contest-id"`
51-
TaskID string `mapstructure:"task-id"`
51+
ContestID string `mapstructure:"contest-id" yaml:"contest-id"`
52+
TaskID string `mapstructure:"task-id" yaml:"task-id"`
5253
Template string
5354
}
5455

@@ -63,7 +64,7 @@ func (t *AppConfig) GetLanguage(name string) (Language, error) {
6364
return Language{}, fmt.Errorf("language %s not found: %w", name, ErrLanguageNotFound)
6465
}
6566

66-
// GetLanguage finds a language by name.
67+
// GetLanguage finds a language designated by task config.
6768
func GetLanguage() (Language, error) {
6869
template, err := GetTemplate()
6970
if err != nil {
@@ -84,7 +85,12 @@ func (t *AppConfig) GetTemplate(name string) (Template, error) {
8485
return Template{}, fmt.Errorf("template %s not found: %w", name, ErrTemplateNotFound)
8586
}
8687

87-
// GetTemplate finds a template by name.
88+
// GetTemplate finds a template designated by task config.
8889
func GetTemplate() (Template, error) {
8990
return GlobalAppConfig.GetTemplate(GlobalTaskConfig.Template)
9091
}
92+
93+
// GetDefaultTemplate finds a template.
94+
func GetDefaultTemplate() (Template, error) {
95+
return GlobalAppConfig.GetTemplate(GlobalAppConfig.DefaultTemplate)
96+
}

internal/config/config_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -206,6 +206,7 @@ func newSampleAppConfig() AppConfig {
206206
SourceFile: "Program.cs",
207207
},
208208
},
209+
ConfigDir: "sample",
209210
DefaultTemplate: "csharp",
210211
}
211212
}

0 commit comments

Comments
 (0)