Skip to content

How To Share Same Slice Destination Among Different Flags #2073

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
kudla opened this issue Mar 10, 2025 · 5 comments
Open

How To Share Same Slice Destination Among Different Flags #2073

kudla opened this issue Mar 10, 2025 · 5 comments
Labels
area/v3 relates to / is being considered for v3 kind/question someone asking a question status/triage maintainers still need to look into this

Comments

@kudla
Copy link

kudla commented Mar 10, 2025

Hi!
Please guys. Actually in v3 how to make several slice flags to share the same destination .
What I'm trying is something like this

package main

import (
	"context"
	"fmt"

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

func main() {
	ints := []int64{}
	app := &cli.Command{
		Flags: []cli.Flag{
			&cli.IntSliceFlag{
				Name:        "a",
				Destination: &ints,
			},
			&cli.IntSliceFlag{
				Name:        "b",
				Destination: &ints,
			},
			&cli.IntSliceFlag{
				Name:        "c",
				Destination: &ints,
			},
		},
		Action: func(ctx context.Context, cmd *cli.Command) error {
			fmt.Printf("%v\n", ints)

			return nil
		},
	}

	err := app.Run(context.Background(), []string{"app", "-a", "1", "-a", "2", "-b", "3", "-c", "4", "-a", "5", "-b", "6"})

	if err != nil {
		panic(err)
	}
}

So it almost works aggregating all the values into a single slice, except it looks like one whatever the flag is being provided at first (initialized) it resets all the values had being populated with previous other flag values.

And actually the more deeper question (story) behind this.

In v3 how actually to make the set of flags to add values into a same slice of some custom type but being customized by the fine grained params for the each flag individually.

In v2 it was pretty simple by implementing Generic.Set(value string) error
in v3 it seams it really something like ValueCreators and FlagConfig should be involved for. Still I can't grasp the whole idea to achieve this.

As a simple example I can see it something like following

type BytesFlagConfig struct {
	Scale int64
}

type Amount struct {
    Value int64
    Scale int64
}

type BytesValue struct {
	destination * Amount
}


type (
	BytesSlice     = cli.SliceBase[Amount, BytesFlagConfig, BytesValue]
	BytesSliceFlag = cli.FlagBase[[]Amount, BytesFlagConfig, BytesSlice]
)

var NewBytesSlice = cli.NewSliceBase[Amount, BytesFlagConfig, BytesValue]

/*

Some lack of magic here

*/

func main() {
      data := []Amount{}

      app := &cli.Command{
          Flags: []cli.Flag{
               &BytesSliceFlag {
                      Name: "bytes",
                      Aliases: []string{"b"},
                      Config: {
                          Scale: 1,
                      },
                      Destination: &data,
               },
               &BytesSliceFlag {
                      Name: "k-bytes",
                      Aliases: []string{"k"},
                      Config: {
                          Scale: 1024,
                      },
                      Destination: &data,
               },
               &BytesSliceFlag {
                      Name: "m-bytes",
                      Aliases: []string{"M"},
                      Config: {
                          Scale: 1024 * 1024,
                      },
                      Destination: &data,
               },
          },
      }

      app.Run(context.Context. os.Args())
}

So that to be called as

my-app -b 120 -k 12 -M 100

An to have in the data destination respectively something related to [120, 12 * 1024, 100 * 1024 * 1024]
It actually matters to keep the values initial order (so that's why we need the same destination here)

Another case would be something to accumulate a set of inputs but from different encoding sources

my-app --data-hex 45af9210eca  --data-base64 c2Rmcmd0ZWZ2ZXI=

So besides the initially described problem with multi int slice flags. The question is

  • how actually to achieve such a logic.
  • Should the custom cli.Value implementation be introduced for that or could it be achieved just on the generic params manipulation level
  • Do I see it at in the right direction at all or should it be implemented in some other way
  • Maybe some very common examples on this could be shared to see as well

Thank you.

@kudla kudla added area/v2 relates to / is being considered for v2 kind/question someone asking a question status/triage maintainers still need to look into this labels Mar 10, 2025
@dearchap dearchap added area/v3 relates to / is being considered for v3 and removed area/v2 relates to / is being considered for v2 labels Mar 10, 2025
@dearchap
Copy link
Contributor

@kudla The approach I would take is to have a single flag for all our values with a custom Value. You would then do something like this

-b 120 -b 12K -b 100M

and so on that way you parse the value into your slice/destination after the conversion. Defining multiple flags for this is an overkill IMO. As of now there is no way to share destinations among various flags

@kudla
Copy link
Author

kudla commented Mar 12, 2025

@dearchap Thanks for your reply
Actually a case I really have is more complex and there're a lot of quirks appointed to a value parsing it self. So there's no more room for that. Even more the different flags type actually determines the way values are parsed as well.

Anyway regardless the fact I can agree my personal example is too complex I still believe approach of sharing the same destination across different flags would be really helpful anyway in a lot of other cases.

E.g. the second example above

my-app --data-hex 45af9210eca  --data-base64 c2Rmcmd0ZWZ2ZXI=

In v2 it was pretty easy with just implementing Generic.Set(value string) error

So would it be ok as a feature request for this approach for the future?

@dearchap
Copy link
Contributor

Sure will do. Did you look at the GenericValue in v3 ?

@kudla
Copy link
Author

kudla commented Mar 14, 2025

Oh, great! Looks like it really might help a lot.

Yet genericValue it self seems to be non exported
Should I use just GenericFlag and provide whatever custom cli.Value implementation for a flag.Value holder?

@dearchap
Copy link
Contributor

dearchap commented Mar 15, 2025

Yes !!! GenericFlag will accept a cli.Value . https://github.com/urfave/cli/blob/main/flag_generic.go#L3

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/v3 relates to / is being considered for v3 kind/question someone asking a question status/triage maintainers still need to look into this
Projects
None yet
Development

No branches or pull requests

2 participants