Skip to content

Commit

Permalink
Fixing NoArg
Browse files Browse the repository at this point in the history
  • Loading branch information
kenshaw committed Nov 24, 2024
1 parent c257018 commit d84cefc
Show file tree
Hide file tree
Showing 8 changed files with 242 additions and 175 deletions.
19 changes: 15 additions & 4 deletions cmd.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ func (fs *FlagSet) apply(val any) error {
switch v := val.(type) {
case *Command:
v.Flags = fs
case *RunContext:
case *Context:
default:
return fmt.Errorf("FlagSet: %w", ErrAppliedToInvalidType)
}
Expand Down Expand Up @@ -390,6 +390,13 @@ func NewFlag(name, usage string, opts ...Option) (*Flag, error) {
return nil, err
}
}
if opts, ok := typeOpts[g.Type]; ok {
for _, o := range opts {
if err := o.apply(g); err != nil {
return nil, err
}
}
}
if g.Name() == "" {
return nil, ErrInvalidFlagName
}
Expand Down Expand Up @@ -480,15 +487,19 @@ func Populate(cmd *Command, all, overwrite bool, vars Vars) error {
if _, ok := vars[name]; ok && overwrite {
delete(vars, name)
}
var value any
var value string
switch {
case g.Type == HookT, g.Def == nil && !all:
continue
case g.Def != nil:
value = g.Def
s, err := asString[string](g.Def, g.Type.Layout())
if err != nil {
return err
}
value = s
}
if err := vars.Set(g, value, false); err != nil {
return fmt.Errorf("cannot set %s to %q: %w", name, value, err)
return fmt.Errorf("cannot populate %s with %q: %w", name, value, err)
}
}
return nil
Expand Down
1 change: 1 addition & 0 deletions cmd_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ func TestParse(t *testing.T) {
root := testCommand(t)
for i, test := range parseTests() {
t.Run(strconv.Itoa(i), func(t *testing.T) {
t.Logf("invoked: %v", test.v)
vars := make(Vars)
cmd, args, err := Parse(root, test.v[1:], vars)
switch {
Expand Down
4 changes: 2 additions & 2 deletions conv.go
Original file line number Diff line number Diff line change
Expand Up @@ -472,9 +472,9 @@ func asNew(typ Type) (any, func([]byte) error, error) {
ok bool
asText bool
)
if f, ok = text[typ]; ok {
if f, ok = typeTextNews[typ]; ok {
asText = true
} else if f, ok = binary[typ]; ok {
} else if f, ok = typeBinaryNews[typ]; ok {
asText = false
}
if !ok {
Expand Down
14 changes: 7 additions & 7 deletions ctx.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,18 +95,18 @@ func VarsOK(ctx context.Context) (Vars, bool) {

// AnyOK returns a variable, its set status, and if it was defined from the
// context.
func AnyOK(ctx context.Context, name string) (Value, bool, bool) {
func AnyOK(ctx context.Context, name string) (Value, bool) {
if vars, ok := VarsOK(ctx); ok {
if vs, ok := vars[name]; ok {
return vs.Var, vs.WasSet, true
if val, ok := vars[name]; ok {
return val, true
}
}
return nil, false, false
return nil, false
}

// GetOK returns a variable.
func GetOK[T any](ctx context.Context, name string) (T, bool) {
if val, _, ok := AnyOK(ctx, name); ok {
if val, ok := AnyOK(ctx, name); ok {
if v, err := As[T](val); err == nil {
return v, true
}
Expand All @@ -123,7 +123,7 @@ func Get[T any](ctx context.Context, name string) T {

// Slice returns the slice variable from the context.
func Slice[T any](ctx context.Context, name string) []T {
if val, _, ok := AnyOK(ctx, name); ok {
if val, ok := AnyOK(ctx, name); ok {
if v, err := SliceAs[T](val); err == nil {
return v
}
Expand All @@ -133,7 +133,7 @@ func Slice[T any](ctx context.Context, name string) []T {

// Map returns the map variable from the context.
func Map[K cmp.Ordered, T any](ctx context.Context, name string) map[K]T {
if val, _, ok := AnyOK(ctx, name); ok {
if val, ok := AnyOK(ctx, name); ok {
if m, err := MapAs[K, T](val); err == nil {
return m
}
Expand Down
22 changes: 11 additions & 11 deletions opts.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import (
"unicode/utf8"
)

// RunArgs is a [Run] option to set the command-line arguments to use.
func RunArgs(args []string) RunOption {
// WithArgs is a [Context] option to set the command-line arguments to use.
func WithArgs(args []string) ContextOption {
return option{
name: "RunArgs",
ctx: func(opts *RunContext) error {
name: "WithArgs",
ctx: func(opts *Context) error {
opts.Args = args
return nil
},
Expand Down Expand Up @@ -364,13 +364,13 @@ func Relative(dir string) Option {
*/

// Option is the interface for options that can be passed when creating a
// [RunContext], [Command], or [Flag].
// [Context], [Command], or [Flag].
//
// The [Option] type is aliased as [RunOption], [CommandOption],
// The [Option] type is aliased as [ContextOption], [CommandOption],
// [CommandFlagOption], and [FlagOption] and provided for ease-of-use,
// readibility, and categorization within documentation.
//
// A [RunOption] can be applied to a [RunContext] and passed to [Run].
// A [ContextOption] can be applied to a [Context] and passed to [Run].
//
// A [CommandOption] can be applied to a [Command] and passed to [NewCommand].
//
Expand All @@ -386,8 +386,8 @@ type Option interface {
apply(any) error
}

// RunOption are [Option]'s that apply to a [RunContext].
type RunOption = Option
// ContextOption are [Option]'s that apply to a [Context].
type ContextOption = Option

// CommandOption are [Option]'s that apply to a [Command].
type CommandOption = Option
Expand All @@ -401,10 +401,10 @@ type FlagOption = Option
// option wraps an option.
type option struct {
name string
ctx func(*Context) error
cmd func(*Command) error
set func(*FlagSet) error
flag func(*Flag) error
ctx func(*RunContext) error
}

// apply satisfies the [Option] interface.
Expand All @@ -419,7 +419,7 @@ func (opt option) apply(val any) error {
if opt.flag != nil {
err = opt.flag(v)
}
case *RunContext:
case *Context:
if opt.ctx != nil {
err = opt.ctx(v)
}
Expand Down
10 changes: 5 additions & 5 deletions ox.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@ import (
)

// Run creates a [Command] for f using [os.Args] by default, unless arguments
// were specified using a [RunOption].
// were specified using a [ContextOption].
func Run[T ExecFunc](f T, opts ...Option) {
ctx := &RunContext{
ctx := &Context{
Stdin: os.Stdin,
Stdout: os.Stdout,
Stderr: os.Stderr,
Expand Down Expand Up @@ -47,8 +47,8 @@ func Run[T ExecFunc](f T, opts ...Option) {
}
}

// RunContext are run context options.
type RunContext struct {
// Context are run context options.
type Context struct {
Args []string
Stdin io.Writer
Stdout io.Writer
Expand All @@ -58,7 +58,7 @@ type RunContext struct {
}

// Handle handles an error.
func (ctx *RunContext) Handle(err error) {
func (ctx *Context) Handle(err error) {
var w io.Writer = os.Stderr
if ctx.Stderr != nil {
w = ctx.Stderr
Expand Down
56 changes: 32 additions & 24 deletions type.go
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,15 @@ func (typ Type) String() string {

// Layout returns the type's registered [time.Time.Format] [time.Layout].
func (typ Type) Layout() string {
return layouts[typ]
return typeLayouts[typ]
}

// New creates a new [Value] for the registered type.
func (typ Type) New() (Value, error) {
if typ == HookT {
return nil, nil
}
f, ok := types[typ]
f, ok := typeNews[typ]
if !ok {
return nil, fmt.Errorf("%w: type not registered", ErrCouldNotCreateValue)
}
Expand All @@ -105,23 +105,27 @@ func (typ Type) New() (Value, error) {
return v, nil
}

// types are registered type descriptions.
var types map[Type]func() (Value, error)
// typeNews are registered type creation funcs.
var typeNews map[Type]func() (Value, error)

// typeNames are type name lookups for registered types.
var typeNames map[string]Type
// typeOpts are registered type flag options.
var typeOpts map[Type][]Option

// layouts are [time.Time] parsing layouts.
var layouts map[Type]string
// typeLayouts are [time.Time] parsing typeLayouts.
var typeLayouts map[Type]string

// text holds new text funcs.
var text map[Type]func() (any, error)
// reflectTypes are reflect type name lookups for registered types.
var reflectTypes map[string]Type

// binary holds new binary funcs.
var binary map[Type]func() (any, error)
// typeTextNews are registered type creation funcs for text marshalable types.
var typeTextNews map[Type]func() (any, error)

// typeBinaryNews are registered type creation funcs for binary marshalable
// types.
var typeBinaryNews map[Type]func() (any, error)

func init() {
types = map[Type]func() (Value, error){
typeNews = map[Type]func() (Value, error){
BytesT: NewVal[[]byte](),
StringT: NewVal[string](),
RunesT: NewVal[[]rune](),
Expand Down Expand Up @@ -155,15 +159,19 @@ func init() {
MapT: NewMap(),
// HookT: NewTypeDesc(NewHook(), NoArg(true)),
}
layouts = map[Type]string{
typeLayouts = map[Type]string{
TimestampT: time.RFC3339Nano,
DateTimeT: time.DateTime,
DateT: time.DateOnly,
TimeT: time.TimeOnly,
}
typeNames = make(map[string]Type)
typeOpts = map[Type][]Option{
BoolT: {NoArg(true)},
CountT: {NoArg(true)},
}
reflectTypes = make(map[string]Type)
// text marshal types
text = make(map[Type]func() (any, error))
typeTextNews = make(map[Type]func() (any, error))
RegisterTextType(func() (*big.Int, error) {
return big.NewInt(0), nil
})
Expand All @@ -183,7 +191,7 @@ func init() {
return new(netip.Prefix), nil
})
// binary marshal types
binary = make(map[Type]func() (any, error))
typeBinaryNews = make(map[Type]func() (any, error))
RegisterBinaryType(func() (*url.URL, error) {
return new(url.URL), nil
})
Expand All @@ -192,35 +200,35 @@ func init() {
// RegisterTypeName registers a type name.
func RegisterTypeName(typ Type, names ...string) {
for _, name := range names {
typeNames[name] = typ
reflectTypes[name] = typ
}
}

// RegisterLayout registers a time layout for the type.
func RegisterLayout(typ Type, layout string) {
layouts[typ] = layout
typeLayouts[typ] = layout
}

// RegisterTextType registers a new text type.
func RegisterTextType[T TextMarshaler](f func() (T, error)) {
registerMarshaler[T](func() (any, error) { return f() }, text)
registerMarshaler[T](func() (any, error) { return f() }, typeTextNews)
}

// RegisterBinaryType registers a new binary type.
func RegisterBinaryType[T BinaryMarshaler](f func() (T, error)) {
registerMarshaler[T](func() (any, error) { return f() }, binary)
registerMarshaler[T](func() (any, error) { return f() }, typeBinaryNews)
}

// registerMarshaler registers a type marshaler.
func registerMarshaler[T any](f func() (any, error), descs map[Type]func() (any, error)) {
typ := typeType[T]()
if _, ok := types[typ]; ok {
if _, ok := typeNews[typ]; ok {
return
}
if _, ok := descs[typ]; ok {
return
}
types[typ], descs[typ] = NewVal[T](typ), f
typeNews[typ], descs[typ] = NewVal[T](typ), f
}

// TextMarshaler is the text marshal interface.
Expand Down Expand Up @@ -300,7 +308,7 @@ func typeRef(val any) Type {
return URLT
}
s := reflect.TypeOf(val).String()
if typ, ok := typeNames[s]; ok {
if typ, ok := reflectTypes[s]; ok {
return typ
}
return Type(s)
Expand Down
Loading

0 comments on commit d84cefc

Please sign in to comment.