diff --git a/.gitignore b/.gitignore index 678af4d..bf4bd04 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,4 @@ dist/ coverage* .env -aoctl +/aoctl diff --git a/README.md b/README.md index 7606ec1..9747598 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,7 @@ aoc version Get The puzzle information with the option to save it locally in the repository. ```bash -aoc puzzle [OPTIONS] +aoc puzzles [OPTIONS] ``` #### Options diff --git a/cmd/aoctl/puzzle.go b/cmd/aoctl/puzzle.go index 59c4d42..bf5ef60 100644 --- a/cmd/aoctl/puzzle.go +++ b/cmd/aoctl/puzzle.go @@ -27,11 +27,10 @@ import ( "github.com/spf13/cobra" ) -var puzzleCmd = &cobra.Command{ - Use: "puzzle", - Aliases: []string{"p", "pzzl", "pz"}, - Short: "Get the puzzles for a given day", - Args: cobra.ExactArgs(0), +var puzzlesCmd = &cobra.Command{ + Use: "puzzles", + Aliases: []string{"puzzle", "p", "pzzl", "pz"}, + Short: "Interact with puzzles from adventofcode.com", Run: func(cmd *cobra.Command, args []string) { day := cmd.Flags().Lookup("day").Value.String() year := cmd.Flags().Lookup("year").Value.String() @@ -42,10 +41,10 @@ var puzzleCmd = &cobra.Command{ } func init() { - rootCmd.AddCommand(puzzleCmd) + rootCmd.AddCommand(puzzlesCmd) - puzzleCmd.Flags().StringP("day", "d", "1", "Day of the Advent of Code problems to fetch; By default it will be day 1") - puzzleCmd.Flags().StringP("year", "y", getCurrentYear(), "Year of the Advent of Code; by default uses the last Advent of Code Year") + puzzlesCmd.Flags().StringP("day", "d", "1", "Day of the Advent of Code problems to fetch; By default it will be day 1") + puzzlesCmd.Flags().StringP("year", "y", getCurrentYear(), "Year of the Advent of Code; by default uses the last Advent of Code Year") } func getCurrentYear() string { diff --git a/cmd/aoctl/synchronize.go b/cmd/aoctl/synchronize.go new file mode 100644 index 0000000..424a522 --- /dev/null +++ b/cmd/aoctl/synchronize.go @@ -0,0 +1,29 @@ +/* +Copyright © 2024 NAME HERE +*/ +package aoctl + +import ( + "github.com/dolfolife/aoctl/pkg/aoc" + "github.com/spf13/cobra" +) + +// syncronizeCmd represents the syncronize command +var ( + forced = false + synchronizeCmd = &cobra.Command{ + Use: "synchronize", + Short: "Synchronize current project", + Long: `Synchronize allows you to update the local repository with the tree of problems in +adventofcode.com.`, + Run: func(cmd *cobra.Command, args []string) { + aoc.SyncAoC(forced) + }, + } +) + +func init() { + puzzlesCmd.AddCommand(synchronizeCmd) + + synchronizeCmd.Flags().BoolVarP(&forced, "force", "f", false, "to force the files update") +} diff --git a/go.mod b/go.mod index 35f2e8d..6462b54 100644 --- a/go.mod +++ b/go.mod @@ -1,10 +1,10 @@ module github.com/dolfolife/aoctl -go 1.20 +go 1.22.0 require ( github.com/joho/godotenv v1.5.1 - github.com/spf13/cobra v1.7.0 + github.com/spf13/cobra v1.8.0 github.com/stretchr/testify v1.8.4 go.hein.dev/go-version v0.1.0 golang.org/x/net v0.13.0 diff --git a/go.sum b/go.sum index d4ab301..60a93f5 100644 --- a/go.sum +++ b/go.sum @@ -16,6 +16,7 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= @@ -86,11 +87,14 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU= github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/spf13/viper v1.4.0 h1:yXHLWeravcrgGyFSyCgdYpXQ9dR9c/WED3pg1RhxqEU= github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= diff --git a/pkg/aoc/aoc.go b/pkg/aoc/aoc.go index 3c9bb65..48f96f9 100644 --- a/pkg/aoc/aoc.go +++ b/pkg/aoc/aoc.go @@ -2,9 +2,12 @@ package aoc import ( "log" + "math" "net/url" "os" "path/filepath" + "strconv" + "time" "github.com/dolfolife/aoctl/pkg/puzzle" ) @@ -21,23 +24,72 @@ func InitializeProject(path string) error { return err } - if err := createFileFromTemplates(filepath.Join(path, ".env"), GetRootFile("env")); err != nil { - log.Fatalf("Error writing the env file: %s", err) - return err + rootFiles := ProjectTemplates() + + for filename, content := range rootFiles { + if err := createFileFromTemplates(filepath.Join(path, filename), content); err != nil { + log.Fatalf("Error writing the %s file: %s\n", filename, err) + return err + } } - if err = createFileFromTemplates(filepath.Join(path, "README.md"), GetRootFile("README.md")); err != nil { - log.Fatalf("Error creating README.md file: %s", err) - return err + log.Println("Project initialized!") + return nil +} + +func SyncAoC(force bool) { + firstYearOfAoC := 2015 + currentYear, CurrentMonth, currentDay := time.Now().Date() + + // if we are not in december it means that we do not have AoC for the current year + if CurrentMonth < 12 { + currentYear-- } + config := GetAoCConfig() + eventsPath := filepath.Join(config.ProjectPath, "events") - if err = createFileFromTemplates(filepath.Join(path, ".gitignore"), GetRootFile("gitignore")); err != nil { - log.Fatalf("Error creating gitignore file: %s", err) - return err + err := os.Mkdir(eventsPath, os.ModePerm) + + if err != nil && !os.IsExist(err) { + log.Fatal("events directory cannot be created") } - log.Println("Project initialized!") - return nil + for i := firstYearOfAoC; i <= currentYear; i++ { + yearDirPath := filepath.Join(eventsPath, strconv.Itoa(i)) + + err := os.Mkdir(yearDirPath, os.ModePerm) + + if err != nil && !os.IsExist(err) { + log.Fatalf("events directory for year %d cannot be created\n", i) + } + + // if we are in December we take the min day between Today and 25 + lastDay := 25 + if CurrentMonth == 12 { + lastDay = int(math.Min(float64(lastDay), float64(currentDay))) + } + + for j := 1; j <= lastDay; j++ { + dayDirPath := filepath.Join(yearDirPath, strconv.Itoa(j)) + err := os.Mkdir(dayDirPath, os.ModePerm) + + if err != nil && !os.IsExist(err) { + log.Fatalf("events directory for year %d cannot be created\n", i) + } + + if os.IsExist(err) && !force { + continue + } + // TODO: + // create all files + // - Create puzzleDay.yaml + // - Create main.go + // - Create solution.go (with two parts) + // - create input directory + // - create solution_test.go + // - Create README.md (use for cache the text in the adventofcode.com + } + } } func createFileFromTemplates(file string, content string) error { diff --git a/pkg/aoc/config.go b/pkg/aoc/config.go index 85f6101..8c44208 100644 --- a/pkg/aoc/config.go +++ b/pkg/aoc/config.go @@ -13,8 +13,17 @@ type AoCConfig struct { SessionId string } -func GetAoCConfig() AoCConfig { +type Templates map[string]string +func ProjectTemplates() Templates { + return Templates{ + ".env": GetRootFile("env"), + "README.md": GetRootFile("README.md"), + ".gitignore": GetRootFile("gitignore"), + } +} + +func GetAoCConfig() AoCConfig { err := godotenv.Load() if err != nil { diff --git a/pkg/aoc/templates/root/README.md b/pkg/aoc/templates/root/README.md index dbe47ae..4ead09c 100644 --- a/pkg/aoc/templates/root/README.md +++ b/pkg/aoc/templates/root/README.md @@ -1 +1,11 @@ # Advent of Code Solutions + +This project will help you organize your solutions for Advent of Code so you can learn, improve, and share it with others. + +To start building solutions you can sync your project with your account on Advent of Code with + +```bash +aoctl puzzles sync +``` + +to learn more about `sync` refer to the documentation at [sync](https://github.com/dolfolife/aoctl)