Skip to content
This repository was archived by the owner on Aug 12, 2022. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/cmd/ach.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/cmd/ach_contest.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/cmd/ach_contest_create.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/cmd/ach_test.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions docs/cmd/ach_version.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ require (
github.com/spf13/viper v1.7.0
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect
gopkg.in/yaml.v2 v2.2.8
)
83 changes: 49 additions & 34 deletions internal/cmd/ach/ach.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@ package ach
import (
"fmt"
"log"
"os"
"os/user"
"path"
"path/filepath"
"strings"

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

cmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.ach/config.yaml)")
cmd.PersistentFlags().StringVar(&taskCfgFile, "task-config", "", "task config file (default is ./achTaskConfig.yaml")
defaultConfigFile := path.Join("$HOME", ".ach", "config.yaml")

cmd.PersistentFlags().StringVar(
&cfgFile,
"config",
defaultConfigFile,
"config file")
cmd.PersistentFlags().StringVar(
&taskCfgFile,
"task-config",
"./achTaskConfig.yaml",
"task config file")

registerSubcommands(cmd)

Expand All @@ -51,49 +63,52 @@ func initConfig() {
}

func readAppConfig() {
var configFileName string

if cfgFile != "" {
viper.SetConfigName(cfgFile)
configFileName = cfgFile
} else {
home, err := os.UserHomeDir()
if err != nil {
log.Fatal(err)
}

viper.AddConfigPath(path.Join(home, ".ach"))
viper.SetConfigName("config")
configFileName = "config"
v := viper.New()

user, err := user.Current()
if err != nil {
log.Fatal(fmt.Errorf("NewAchCmd: %w", err))
}

home := user.HomeDir

homeReplacedCfgFile := strings.Replace(cfgFile, "$HOME", home, 1)

absCfgFile, err := filepath.Abs(homeReplacedCfgFile)
if err != nil {
log.Fatal(fmt.Errorf("failed to convert config to its absolute path: %w", err))
}

if err := viper.ReadInConfig(); err != nil {
log.Fatal(fmt.Errorf("failed to read app config %s: %w", configFileName, err))
v.SetConfigName(strings.TrimSuffix(absCfgFile, path.Ext(absCfgFile)))
v.AddConfigPath("/")

if err := v.ReadInConfig(); err != nil {
log.Fatal(fmt.Errorf("failed to read app config %s: %w", absCfgFile, err))
}

if err := viper.UnmarshalExact(&config.GlobalAppConfig); err != nil {
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", configFileName, err))
if err := v.UnmarshalExact(&config.GlobalAppConfig); err != nil {
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", absCfgFile, err))
}

config.GlobalAppConfig.ConfigDir = filepath.Dir(absCfgFile)
}

func readTaskConfig() {
var configFileName string
v := viper.New()

if taskCfgFile != "" {
viper.SetConfigFile(taskCfgFile)

configFileName = cfgFile
} else {
viper.AddConfigPath(".")
viper.SetConfigName("achTaskConfig")
configFileName = "achTaskConfig"
absTaskCfgFile, err := filepath.Abs(taskCfgFile)
if err != nil {
log.Fatal(fmt.Errorf("failed to convert task config to its absolute path: %w", err))
}

if err := viper.ReadInConfig(); err != nil {
log.Print(fmt.Errorf("failed to read task config %s: %w", configFileName, err))
v.SetConfigName(strings.TrimSuffix(absTaskCfgFile, path.Ext(absTaskCfgFile)))
v.AddConfigPath("/")

if err := v.ReadInConfig(); err != nil {
return
}

if err := viper.UnmarshalExact(&config.GlobalTaskConfig); err != nil {
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", configFileName, err))
if err := v.UnmarshalExact(&config.GlobalTaskConfig); err != nil {
log.Fatal(fmt.Errorf("failed to parse app config %s: %w", absTaskCfgFile, err))
}
}
100 changes: 74 additions & 26 deletions internal/cmd/ach/contest/create/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,13 @@ package create
import (
"fmt"
"log"
"os"
"os/exec"
"os/user"
"path"

"github.com/spf13/cobra"
"github.com/yuchiki/atcoderHelper/internal/config"
yaml "gopkg.in/yaml.v2"
)

// NewContestCreateCmd returns a new contest create command.
Expand Down Expand Up @@ -35,55 +37,101 @@ D is for directory.
}

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

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

err := exec.Command("mkdir", contestName).Run()
output, err := exec.Command("mkdir", contestName).CombinedOutput()
if err != nil {
return err
return fmt.Errorf("%s: %w", output, err)
}

absTemplateDir := getAbsTemplateDirectory(template.TemplateDirectory)

for _, taskName := range taskNames {
taskDirName := path.Join(contestName, taskName)
if output, err := exec.Command("cp", "-r", templateDirName, taskDirName).Output(); err != nil {
if output, err := exec.Command("cp", "-r", absTemplateDir, taskDirName).CombinedOutput(); err != nil {
fmt.Print(output)

return fmt.Errorf("%s: %w", output, err)
}

taskConfig := config.TaskConfig{
ContestID: contestName,
TaskID: taskName,
Template: config.GlobalAppConfig.DefaultTemplate,
}

taskConfigYaml, err := yaml.Marshal(taskConfig)
if err != nil {
return err
}

taskConfigName := path.Join(taskDirName, "achTaskConfig.yaml")

taskConfigFile, err := os.Create(taskConfigName)
if err != nil {
return err
}
defer taskConfigFile.Close()

_, err = taskConfigFile.Write(taskConfigYaml)
if err != nil {
return err
}

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

err = createSampleCases(sampleDirName, 5)
if err != nil {
return err
}
}

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

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

return err
}
return err
}

for i := 1; i <= n; i++ {
inputFileName := path.Join(sampleDirName, fmt.Sprintf("case%d.input", i))

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

err = exec.Command("touch", outputFileName).Run()
if err != nil {
return err
}
return err
}

outputFileName := path.Join(sampleDirName, fmt.Sprintf("case%d.expected", i))

err = exec.Command("touch", outputFileName).Run()
if err != nil {
return err
}
}

return nil
}

func getAbsTemplateDirectory(templateDir string) string {
if path.IsAbs(templateDir) {
return templateDir
}

return path.Join(config.GlobalAppConfig.ConfigDir, templateDir)
}
22 changes: 14 additions & 8 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ type AppConfig struct {
Username string
Languages []Language
Templates []Template
DefaultTemplate string `mapstructure:"default-template"`
ConfigDir string `mapstructure:"-" yaml:"-"`
DefaultTemplate string `mapstructure:"default-template" yaml:"default-template"`
}

// Language is an information of a programming language used when solving a task.
type Language struct {
Name string
AtCoderName string `mapstructure:"atcoder-name"`
AtCoderName string `mapstructure:"atcoder-name" yaml:"atcoder-name"`
Build string
Run string
}
Expand All @@ -41,14 +42,14 @@ type Language struct {
type Template struct {
Name string
Language string
TemplateDirectory string `mapstructure:"template-directory"`
SourceFile string `mapstructure:"source-file"`
TemplateDirectory string `mapstructure:"template-directory" yaml:"template-directory"`
SourceFile string `mapstructure:"source-file" yaml:"source-file"`
}

// TaskConfig is a configuration for each task.
type TaskConfig struct {
ContestID string `mapstructure:"contest-id"`
TaskID string `mapstructure:"task-id"`
ContestID string `mapstructure:"contest-id" yaml:"contest-id"`
TaskID string `mapstructure:"task-id" yaml:"task-id"`
Template string
}

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

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

// GetTemplate finds a template by name.
// GetTemplate finds a template designated by task config.
func GetTemplate() (Template, error) {
return GlobalAppConfig.GetTemplate(GlobalTaskConfig.Template)
}

// GetDefaultTemplate finds a template.
func GetDefaultTemplate() (Template, error) {
return GlobalAppConfig.GetTemplate(GlobalAppConfig.DefaultTemplate)
}
1 change: 1 addition & 0 deletions internal/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,7 @@ func newSampleAppConfig() AppConfig {
SourceFile: "Program.cs",
},
},
ConfigDir: "sample",
DefaultTemplate: "csharp",
}
}
Expand Down