Skip to content

Commit

Permalink
feat(config): Add config struct
Browse files Browse the repository at this point in the history
  • Loading branch information
gabe565 committed Jul 15, 2024
1 parent 35c42bd commit e1c1abe
Show file tree
Hide file tree
Showing 6 changed files with 110 additions and 48 deletions.
49 changes: 16 additions & 33 deletions cmd/cmd.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package cmd

import (
"errors"
"strings"
"context"

tea "github.com/charmbracelet/bubbletea"
"github.com/gabe565/cli-of-life/internal/config"
Expand All @@ -22,60 +21,44 @@ func New() *cobra.Command {
DisableAutoGenTag: true,
}

cmd.Flags().StringP(config.FileFlag, "f", "", "Loads a pattern file on startup")
cmd.Flags().String(config.FileFormatFlag, "auto", "File format (one of: "+strings.Join(pattern.FormatStrings(), ", ")+")")
cmd.Flags().String(config.RuleStringFlag, pattern.GameOfLife().String(), "Rule string to use. This will be ignored if a pattern file is loaded.")
cmd.Flags().Bool(config.PlayFlag, false, "Play on startup")
cmd.Flags().String(config.CompletionFlag, "", "Output command-line completion code for the specified shell (one of: "+strings.Join(shells(), ", ")+")")

if err := errors.Join(
cmd.RegisterFlagCompletionFunc(config.FileFlag,
func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{pattern.ExtRLE, pattern.ExtPlaintext}, cobra.ShellCompDirectiveFilterFileExt
},
),
cmd.RegisterFlagCompletionFunc(config.FileFormatFlag,
func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return pattern.FormatStrings(), cobra.ShellCompDirectiveNoFileComp
},
),
cmd.RegisterFlagCompletionFunc(config.RuleStringFlag,
func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{pattern.GameOfLife().String(), pattern.HighLife().String()}, cobra.ShellCompDirectiveNoFileComp
},
),
); err != nil {
conf := config.New()
conf.RegisterFlags(cmd.Flags())
if err := config.RegisterCompletion(cmd); err != nil {
panic(err)
}

cmd.SetContext(config.NewContext(context.Background(), conf))
return cmd
}

func run(cmd *cobra.Command, _ []string) error {
if shell := cmd.Flag(config.CompletionFlag).Value.String(); shell != "" {
return completion(cmd, shell)
conf, ok := config.FromContext(cmd.Context())
if !ok {
panic("command missing config")
}

if conf.Completion != "" {
return completion(cmd, conf.Completion)
}

var rule pattern.Rule
if err := rule.UnmarshalText([]byte(cmd.Flag(config.RuleStringFlag).Value.String())); err != nil {
if err := rule.UnmarshalText([]byte(conf.RuleString)); err != nil {
return err
}

pat := pattern.Pattern{
Rule: rule,
}
if file := cmd.Flag(config.FileFlag).Value.String(); file != "" {
format := pattern.Format(cmd.Flag(config.FileFormatFlag).Value.String())
if conf.File != "" {
var err error
if pat, err = pattern.UnmarshalFile(file, format); err != nil {
if pat, err = pattern.UnmarshalFile(conf.File, pattern.Format(conf.FileFormat)); err != nil {
return err
}
}

g := game.New(
game.WithPattern(pat),
game.WithDimensions(400, 400),
game.WithPlay(cmd.Flag(config.PlayFlag).Value.String() == "true"),
game.WithPlay(conf.Play),
)

_, err := tea.NewProgram(g, tea.WithAltScreen(), tea.WithMouseAllMotion()).Run()
Expand Down
20 changes: 5 additions & 15 deletions cmd/completion.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,31 +4,21 @@ import (
"errors"
"fmt"

"github.com/gabe565/cli-of-life/internal/config"
"github.com/spf13/cobra"
)

const (
ShellBash = "bash"
ShellZsh = "zsh"
ShellFish = "fish"
ShellPowerShell = "powershell"
)

func shells() []string {
return []string{ShellBash, ShellZsh, ShellFish, ShellPowerShell}
}

var ErrInvalidShell = errors.New("invalid shell")

func completion(cmd *cobra.Command, shell string) error {
switch shell {
case ShellBash:
case config.ShellBash:
return cmd.Root().GenBashCompletion(cmd.OutOrStdout())
case ShellZsh:
case config.ShellZsh:
return cmd.Root().GenZshCompletion(cmd.OutOrStdout())
case ShellFish:
case config.ShellFish:
return cmd.Root().GenFishCompletion(cmd.OutOrStdout(), true)
case ShellPowerShell:
case config.ShellPowerShell:
return cmd.Root().GenPowerShellCompletionWithDesc(cmd.OutOrStdout())
default:
return fmt.Errorf("%w: %s", ErrInvalidShell, shell)
Expand Down
39 changes: 39 additions & 0 deletions internal/config/completion.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package config

import (
"errors"

"github.com/gabe565/cli-of-life/internal/pattern"
"github.com/spf13/cobra"
)

const (
ShellBash = "bash"
ShellZsh = "zsh"
ShellFish = "fish"
ShellPowerShell = "powershell"
)

func shells() []string {
return []string{ShellBash, ShellZsh, ShellFish, ShellPowerShell}
}

func RegisterCompletion(cmd *cobra.Command) error {
return errors.Join(
cmd.RegisterFlagCompletionFunc(FileFlag,
func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{pattern.ExtRLE, pattern.ExtPlaintext}, cobra.ShellCompDirectiveFilterFileExt
},
),
cmd.RegisterFlagCompletionFunc(FileFormatFlag,
func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return pattern.FormatStrings(), cobra.ShellCompDirectiveNoFileComp
},
),
cmd.RegisterFlagCompletionFunc(RuleStringFlag,
func(_ *cobra.Command, _ []string, _ string) ([]string, cobra.ShellCompDirective) {
return []string{pattern.GameOfLife().String(), pattern.HighLife().String()}, cobra.ShellCompDirectiveNoFileComp
},
),
)
}
19 changes: 19 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package config

import "github.com/gabe565/cli-of-life/internal/pattern"

type Config struct {
File string
FileFormat string
RuleString string
Play bool

Completion string
}

func New() *Config {
return &Config{
FileFormat: "auto",
RuleString: pattern.GameOfLife().String(),
}
}
16 changes: 16 additions & 0 deletions internal/config/context.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package config

import "context"

type ctxKey uint8

const configKey ctxKey = iota

func NewContext(ctx context.Context, conf *Config) context.Context {
return context.WithValue(ctx, configKey, conf)
}

func FromContext(ctx context.Context) (*Config, bool) {
conf, ok := ctx.Value(configKey).(*Config)
return conf, ok
}
15 changes: 15 additions & 0 deletions internal/config/flags.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
package config

import (
"strings"

"github.com/gabe565/cli-of-life/internal/pattern"
"github.com/spf13/pflag"
)

const (
FileFlag = "file"
FileFormatFlag = "file-format"
RuleStringFlag = "rule-string"
PlayFlag = "play"
CompletionFlag = "completion"
)

func (c *Config) RegisterFlags(fs *pflag.FlagSet) {
fs.StringVarP(&c.File, FileFlag, "f", c.File, "Loads a pattern file on startup")
fs.StringVar(&c.FileFormat, FileFormatFlag, c.FileFormat, "File format (one of: "+strings.Join(pattern.FormatStrings(), ", ")+")")
fs.StringVar(&c.RuleString, RuleStringFlag, c.RuleString, "Rule string to use. This will be ignored if a pattern file is loaded.")
fs.BoolVar(&c.Play, PlayFlag, c.Play, "Play on startup")
fs.StringVar(&c.Completion, CompletionFlag, c.Completion, "Output command-line completion code for the specified shell (one of: "+strings.Join(shells(), ", ")+")")
}

0 comments on commit e1c1abe

Please sign in to comment.