Skip to content

Text flags #2051

Closed
Closed

Description

@somebadcode

Checklist

  • Are you running the latest v3 release? The list of releases is here.
  • Did you check the manual for your release? The v3 manual is here.
  • Did you perform a search about this feature? Here's the GitHub guide about searching.

What problem does this solve?

The standard library's flag package has had flag.TextVar since 1.19 which allows any type that satisfies encoding.TextMarshaller and encoding.TextUnmarshaller to be set using flags. A good use for this is to set log level using strings such as slog.LevelVar.

Other logging packages such as go.uber.org/zap satisfies these interfaces for its atomic level type. You can have a logger before the log level has been set. It'll be ready before arguments have been parsed and output structured logs in case of a parse error.

Solution description

Implement TextValue and TextFlag.

Describe alternatives you've considered

Here's what I had to implement to get the functionality. It would be nice to see this being supported by this package.

package clix

import (
	"encoding"
	"strings"

	"github.com/urfave/cli/v3"
)

type TextMarshalUnMarshaller interface {
	encoding.TextMarshaler
	encoding.TextUnmarshaler
}

type TextFlag = cli.FlagBase[TextMarshalUnMarshaller, cli.StringConfig, TextValue]

type TextValue struct {
	Value  TextMarshalUnMarshaller
	Config cli.StringConfig
}

func (v TextValue) String() string {
	text, err := v.Value.MarshalText()
	if err != nil {
		return ""
	}

	return string(text)
}

func (v TextValue) Set(s string) error {
	if v.Config.TrimSpace {
		return v.Value.UnmarshalText([]byte(strings.TrimSpace(s)))
	}

	return v.Value.UnmarshalText([]byte(s))
}

func (v TextValue) Get() any {
	return v.Value
}

func (v TextValue) Create(t TextMarshalUnMarshaller, _ *TextMarshalUnMarshaller, c cli.StringConfig) cli.Value {
	return &TextValue{
		Value:  t,
		Config: c,
	}
}

func (v TextValue) ToString(t TextMarshalUnMarshaller) string {
	text, err := t.MarshalText()
	if err != nil {
		return ""
	}

	return string(text)
}

I can contribute a solution based on this if so desired.

Activity

  1. added
    area/v3relates to / is being considered for v3
    status/triagemaintainers still need to look into this
    on Feb 5, 2025
  2. dearchap commented on Feb 5, 2025

    @dearchap
    Contributor

    @somebadcode Yes please !!!!

  3. added a commit that references this issue on Feb 10, 2025
    fbcbd0f
  4. added a commit that references this issue on Apr 18, 2025
    3d26679
  5. dearchap commented on Apr 19, 2025

    @dearchap
    Contributor

    @somebadcode This is what I have and it works

    package main
    
    import (
    	"context"
    	"log"
    	"log/slog"
    	"os"
    
    	"github.com/urfave/cli/v3"
    )
    
    type textValue struct {
    	lv slog.LevelVar
    }
    
    func (tv *textValue) String() string {
    	return tv.lv.String()
    }
    
    func (tv *textValue) Set(value string) error {
    	if err := tv.lv.UnmarshalText([]byte(value)); err != nil {
    		return err
    	}
    	return nil
    }
    
    func (tv *textValue) Get() any {
    	return tv.lv.Level()
    }
    
    func newTextValue() *textValue {
    	p := &textValue{
    		lv: slog.LevelVar{},
    	}
    	return p
    }
    
    func ptr(v cli.Value) *cli.Value {
    	return &v
    }
    
    func main() {
    	nv := newTextValue()
    
    	cmd := &cli.Command{
    		Name: "foo",
    		Flags: []cli.Flag{
    			&cli.GenericFlag{
    				Name:        "level",
    				Usage:       "log-level",
    				Value:       nv,
    				Destination: ptr(nv),
    			},
    		},
    		Action: func(_ context.Context, c *cli.Command) error {
    			log.Printf("%v", c.Value("level").(slog.Level))
    			return nil
    		},
    	}
    
    	cmd.Run(context.TODO(), os.Args)
    }
    
    

    Only difference is that it uses the GenericFlag instead of a new TextFlag. While I like the TextFlag approach I think the current plumbing of cli allows you to achieve what you want without a new flag in the mix.

    $ go run . --level INFO
    2025/04/18 20:45:03 INFO
    
  6. added a commit that references this issue on Apr 19, 2025
    88c33c4
  7. added
    kind/featuredescribes a code enhancement / feature request
    and removed
    status/triagemaintainers still need to look into this
    on Jun 14, 2025
  8. added a commit that references this issue on Sep 4, 2026
    7ee9d8e
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area/v3relates to / is being considered for v3kind/featuredescribes a code enhancement / feature request

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions