Repository: alecthomas/kong (go)

Issue:
Override place holder for mapper
I have a mapper that takes a json file path and unmarshals it into the arguments with `type:"jsonfile"`.  Right now the help text says

```
--myflag=KEY=VALUE;...
```

but ideally it would say `--myflag=PATH` or something.  It always takes a path so `KEY=VALUE` could be confusing.

Is there a way to have the mapper override the help text here, or could that functionality be added?

Pull request description:
Fixes #125: Support custom per-mapper placeholder text
Fixes #125 

This allows things like having the placeholder text be `--config=/path/to/file or "-"` or listing enumerations like `--level=trace|debug|info|warn`, dates like `--start-time="2000-11-01 23:00:00"` etc.  None of these are implemented out of the box - this is only for new custom mappers.

To use this implement the `PlaceHolderProvider` interface along with `MapperValue`.  Implementing that interface is optional, the default placeholder will be used otherwise.

Patch:
diff --git a/help.go b/help.go
index b0fc81d..2a9985f 100644
--- a/help.go
+++ b/help.go
@@ -65,6 +65,11 @@ type HelpProvider interface {
 	Help() string
 }
 
+// PlaceHolderProvider can be implemented by mappers to provide custom placeholder text.
+type PlaceHolderProvider interface {
+	PlaceHolder(flag *Flag) string
+}
+
 // HelpIndenter is used to indent new layers in the help tree.
 type HelpIndenter func(prefix string) string
 
diff --git a/mapper.go b/mapper.go
index b8447ec..f5e1a3f 100644
--- a/mapper.go
+++ b/mapper.go
@@ -43,6 +43,7 @@ func (r *DecodeContext) WithScanner(scan *Scanner) *DecodeContext {
 }
 
 // MapperValue may be implemented by fields in order to provide custom mapping.
+// Mappers may additionally implement PlaceHolderProvider to provide custom placeholder text.
 type MapperValue interface {
 	Decode(ctx *DecodeContext) error
 }
diff --git a/model.go b/model.go
index f21dece..da64356 100644
--- a/model.go
+++ b/model.go
@@ -397,6 +397,10 @@ func (f *Flag) String() string {
 
 // FormatPlaceHolder formats the placeholder string for a Flag.
 func (f *Flag) FormatPlaceHolder() string {
+	placeholderHelper, ok := f.Value.Mapper.(PlaceHolderProvider)
+	if ok {
+		return placeholderHelper.PlaceHolder(f)
+	}
 	tail := ""
 	if f.Value.IsSlice() && f.Value.Tag.Sep != -1 {
 		tail += string(f.Value.Tag.Sep) + "..."


Test patch:
diff --git a/mapper_test.go b/mapper_test.go
index 4f052f9..0a81f9c 100644
--- a/mapper_test.go
+++ b/mapper_test.go
@@ -420,3 +420,35 @@ func TestPathMapper(t *testing.T) {
 	require.NoError(t, err)
 	require.Equal(t, "-", cli.Path)
 }
+
+func TestMapperPlaceHolder(t *testing.T) {
+	var cli struct {
+		Flag string
+	}
+	b := bytes.NewBuffer(nil)
+	k := mustNew(
+		t,
+		&cli,
+		kong.Writers(b, b),
+		kong.ValueMapper(&cli.Flag, testMapperWithPlaceHolder{}),
+		kong.Exit(func(int) { panic("exit") }),
+	)
+	// Ensure that --help
+	require.Panics(t, func() {
+		_, err := k.Parse([]string{"--help"})
+		require.NoError(t, err)
+	})
+	require.Contains(t, b.String(), "--flag=/a/b/c")
+}
+
+type testMapperWithPlaceHolder struct {
+}
+
+func (t testMapperWithPlaceHolder) Decode(ctx *kong.DecodeContext, target reflect.Value) error {
+	target.SetString("hi")
+	return nil
+}
+
+func (t testMapperWithPlaceHolder) PlaceHolder(flag *kong.Flag) string {
+	return "/a/b/c"
+}