func (c *cfger) alter(args []string) {
	c.parseEntity(args)
	c.parseKVs()
	if c.incremental {
		c.alterIncremental()
	} else {
		c.alterOld()
	}
}
alter actually issues an alter config command, where args can contain
either nothing or a single topic or broker name.
package configs
import (
	"context"
	"fmt"
	"sort"
	"strconv"
	"strings"
	"github.com/spf13/cobra"
	"github.com/twmb/franz-go/pkg/kmsg"
	"github.com/twmb/kcl/client"
	"github.com/twmb/kcl/out"
)
func Command(cl *client.Client) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "configs",
		Short: "Alter or describe topic, broker, or broker logger configs.",
	}
	cmd.AddCommand(alterCommand(cl))
	cmd.AddCommand(describeCommand(cl))
	return cmd
}
type entity int8
const (
	entityUnknown      = 0
	entityTopic        = 2
	entityBroker       = 4
	entityBrokerLogger = 8
)
type querier struct {
	cl *client.Client
	rawEntity    string
	entity       entity
	resourceName string
	requestor client.Requestor
}
func (q *querier) parseEntity(args []string) {
	switch q.rawEntity {
	case "t", "topic":
		q.entity = entityTopic
	case "b", "broker":
		q.entity = entityBroker
	case "bl", "broker logger":
		q.entity = entityBrokerLogger
	default:
		out.Die("unrecognized entity type %q (allowed: t, topic, b, broker, bl, broker logger)", q.rawEntity)
	}
	if q.entity == entityTopic && len(args) == 0 {
		out.Die("missing entity name")
	}
	if len(args) > 0 {
		q.resourceName = args[0]
	}
	q.requestor = q.cl.Client()
	if q.entity == entityBroker && len(args) > 0 {
		bid, err := strconv.Atoi(args[0])
		out.MaybeDie(err, "unable to parse broker ID: %v", err)
		q.requestor = q.cl.Client().Broker(bid)
	}
}
func alterCommand(cl *client.Client) *cobra.Command {
	cfger := &cfger{
		querier: querier{
			cl: cl,
		},
	}
	cmd := &cobra.Command{
		Use:   "alter [ENTITY]",
		Short: "Alter a topic, broker, or broker logger's configs.",
		Long: `Alter configurations (0.11.0+).
Kafka has two modes of altering configurations: wholesale altering, and
incremental altering. The original altering method requires specifying all
configuration options; anything not specified will be lost. The incremental
method, introduced in Kafka 2.3.0, allows modifying individual values.
To opt into the new method, use the --inc flag. If using the old method, this
command by default checks for existing config key/value pairs that may be lost
in the alter and prompts if it is OK to lose those values. To skip this check,
use the --no-confirm flag. As well, with incremental altering, keys require
a prefix of either set, del, +, or - to indicate whether the value is to be
set, deleted, added to an array, or removed from an array.
Altering requires specifying the "entity type" being altered. This is either
"topic" ("t"), "broker" ("b"), or "broker logger" ("bl"). Broker logger support
only exists for incrementally altering configs.
Altering topics always requires the topic name being altered. Altering brokers
allows for leaving off the broker being altered; this will update the dynamic
configuration on all brokers. Updating an individual broker causes the broker
to reload its password files and allows for setting password fields.
`,
		Example: `alter foo -itt -ks:cleanup.policy=compact -kd:preallocate
alter foo --dry --inc --type topic --kv set:preallocate=true --kv del:cleanup.policy
alter foo --no-confirm --type topic --kv preallocate=true  
		Run: func(_ *cobra.Command, args []string) {
			cfger.alter(args)
		},
	}
	cmd.Flags().StringVarP(&cfger.rawEntity, "type", "t", "topic", "entity type (t or topic, b or broker, bl or broker logger)")
	cmd.Flags().BoolVarP(&cfger.incremental, "inc", "i", false, "perform an incremental alter (Kafka 2.3.0+)")
	cmd.Flags().StringArrayVarP(&cfger.rawKVs, "kv", "k", nil, "key value config parameters; repeatable; if incremental, keys require prefix in [set:, del:, +:, -:]")
	cmd.Flags().BoolVarP(&cfger.dryRun, "dry", "d", false, "dry run: validate the config alter request, but do not apply")
	cmd.Flags().BoolVar(&cfger.noConfirm, "no-confirm", false, "skip confirmation of to-be-lost unspecified existing dynamic config keys")
	return cmd
}
type kv struct {
	k  string
	v  *string
	op int8  
}
type cfger struct {
	querier
	rawKVs    []string
	parsedKVs []kv
	incremental bool
	noConfirm   bool
	dryRun      bool
}
func (c *cfger) parseKVs() {
	for _, rawKV := range c.rawKVs {
		split := strings.SplitN(rawKV, "=", 2)
		if c.incremental {
			colon := strings.IndexByte(split[0], ':')
			if colon == -1 {
				out.Die("missing op: prefix on key %q", split[0])
			}
			rawOp := split[0][:colon]
			split[0] = split[0][colon+1:]
			var op int8
			switch rawOp {
			case "s", "set":
				op = 0
			case "d", "del":
				op = 1
			case "+":
				op = 2
			case "-":
				op = 3
			default:
				out.Die("unrecognized incremental op %q; not in set [s, set, d, del, +, -]", rawOp)
			}
			var v *string
			if op == 0 || op == 2 {
				if len(split) != 2 {
					out.Die("set or append key %q missing value", split[0])
				}
				v = &split[1]
			}
			c.parsedKVs = append(c.parsedKVs, kv{k: split[0], v: v, op: op})
		} else {
			if len(split) != 2 {
				out.Die("key %q missing value", split[0])
			}
			if strings.Contains(split[0], ":") {
				out.Die("invalid incremental syntax on key %q", split[0])
			}
			c.parsedKVs = append(c.parsedKVs, kv{k: split[0], v: &split[1]})
		}
	}
}