modernize: Prefer strings.SplitSeq instead of Split

Avoids extra allocations. Added in Go 1.24.

Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
This commit is contained in:
Paweł Gronowski
2025-12-15 17:53:59 +01:00
parent c9b0a21bb1
commit a25907b485
29 changed files with 50 additions and 50 deletions

View File

@@ -545,7 +545,7 @@ func validateEndpointSettings(nw *libnetwork.Network, nwName string, epConfig *n
} }
if sysctls, ok := epConfig.DriverOpts[netlabel.EndpointSysctls]; ok { if sysctls, ok := epConfig.DriverOpts[netlabel.EndpointSysctls]; ok {
for _, sysctl := range strings.Split(sysctls, ",") { for sysctl := range strings.SplitSeq(sysctls, ",") {
scname := strings.SplitN(sysctl, ".", 5) scname := strings.SplitN(sysctl, ".", 5)
// Allow "ifname" as well as "IFNAME", because the CLI converts to lower case. // Allow "ifname" as well as "IFNAME", because the CLI converts to lower case.
if len(scname) != 5 || if len(scname) != 5 ||

View File

@@ -404,7 +404,7 @@ func extractDistributionSources(labels map[string]string) []distributionSource {
// if yes, read it as source // if yes, read it as source
for k, v := range labels { for k, v := range labels {
if reg := strings.TrimPrefix(k, containerdlabels.LabelDistributionSource); reg != k { if reg := strings.TrimPrefix(k, containerdlabels.LabelDistributionSource); reg != k {
for _, repo := range strings.Split(v, ",") { for repo := range strings.SplitSeq(v, ",") {
ref, err := reference.ParseNamed(reg + "/" + repo) ref, err := reference.ParseNamed(reg + "/" + repo)
if err != nil { if err != nil {
continue continue

View File

@@ -56,7 +56,7 @@ func Scan(text string) (*events.Message, error) {
} }
attrs := make(map[string]string) attrs := make(map[string]string)
for _, a := range strings.Split(md["attributes"], ", ") { for a := range strings.SplitSeq(md["attributes"], ", ") {
k, v, _ := strings.Cut(a, "=") k, v, _ := strings.Cut(a, "=")
attrs[k] = v attrs[k] = v
} }

View File

@@ -264,7 +264,7 @@ func (d *Driver) getLowerDirs(id string) ([]string, error) {
var lowersArray []string var lowersArray []string
lowers, err := os.ReadFile(path.Join(d.dir(id), lowerFile)) lowers, err := os.ReadFile(path.Join(d.dir(id), lowerFile))
if err == nil { if err == nil {
for _, s := range strings.Split(string(lowers), ":") { for s := range strings.SplitSeq(string(lowers), ":") {
lp, err := os.Readlink(path.Join(d.home, s)) lp, err := os.Readlink(path.Join(d.home, s))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -469,7 +469,7 @@ func (d *Driver) getLowerDirs(id string) ([]string, error) {
var lowersArray []string var lowersArray []string
lowers, err := os.ReadFile(path.Join(d.dir(id), lowerFile)) lowers, err := os.ReadFile(path.Join(d.dir(id), lowerFile))
if err == nil { if err == nil {
for _, s := range strings.Split(string(lowers), ":") { for s := range strings.SplitSeq(string(lowers), ":") {
lp, err := os.Readlink(path.Join(d.home, s)) lp, err := os.Readlink(path.Join(d.home, s))
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -361,8 +361,8 @@ func parseInitVersion(v string) (version string, commit string, _ error) {
// commit: 69663f0bd4b60df09991c08812a60108003fa340 // commit: 69663f0bd4b60df09991c08812a60108003fa340
// spec: 1.0.0 // spec: 1.0.0
func parseRuntimeVersion(v string) (runtime, version, commit string, _ error) { func parseRuntimeVersion(v string) (runtime, version, commit string, _ error) {
lines := strings.Split(strings.TrimSpace(v), "\n") lines := strings.SplitSeq(strings.TrimSpace(v), "\n")
for _, line := range lines { for line := range lines {
if strings.Contains(line, "version") { if strings.Contains(line, "version") {
s := strings.Split(line, "version") s := strings.Split(line, "version")
runtime = strings.TrimSpace(s[0]) runtime = strings.TrimSpace(s[0])

View File

@@ -60,7 +60,7 @@ func (e *imageExporter) Resolve(ctx context.Context, id int, attrs map[string]st
for k, v := range attrs { for k, v := range attrs {
switch exptypes.ImageExporterOptKey(k) { switch exptypes.ImageExporterOptKey(k) {
case exptypes.OptKeyName: case exptypes.OptKeyName:
for _, v := range strings.Split(v, ",") { for v := range strings.SplitSeq(v, ",") {
ref, err := reference.ParseNormalizedNamed(v) ref, err := reference.ParseNormalizedNamed(v)
if err != nil { if err != nil {
return nil, err return nil, err

View File

@@ -100,7 +100,7 @@ func (i *imageExporterInstanceWrapper) processNamedCallback(ctx context.Context,
return return
} }
for _, name := range strings.Split(imageName, ",") { for name := range strings.SplitSeq(imageName, ",") {
ref, err := reference.ParseNormalizedNamed(name) ref, err := reference.ParseNormalizedNamed(name)
if err != nil { if err != nil {
// Shouldn't happen, but log if it does and continue. // Shouldn't happen, but log if it does and continue.

View File

@@ -86,8 +86,8 @@ func appendDistributionSourceLabel(originLabel, repo string) string {
} }
func hasDistributionSource(label, repo string) bool { func hasDistributionSource(label, repo string) bool {
sources := strings.Split(label, ",") sources := strings.SplitSeq(label, ",")
for _, s := range sources { for s := range sources {
if s == repo { if s == repo {
return true return true
} }

View File

@@ -31,7 +31,7 @@ func (info *Info) ExtraAttributes(keyMod func(string) string) (map[string]string
extra := make(map[string]string) extra := make(map[string]string)
if labels, ok := info.Config["labels"]; ok && labels != "" { if labels, ok := info.Config["labels"]; ok && labels != "" {
for _, l := range strings.Split(labels, ",") { for l := range strings.SplitSeq(labels, ",") {
if v, ok := info.ContainerLabels[l]; ok { if v, ok := info.ContainerLabels[l]; ok {
if keyMod != nil { if keyMod != nil {
l = keyMod(l) l = keyMod(l)
@@ -69,7 +69,7 @@ func (info *Info) ExtraAttributes(keyMod func(string) string) (map[string]string
} }
if env, ok := info.Config["env"]; ok && env != "" { if env, ok := info.Config["env"]; ok && env != "" {
for _, l := range strings.Split(env, ",") { for l := range strings.SplitSeq(env, ",") {
if v, ok := envMapping[l]; ok { if v, ok := envMapping[l]; ok {
if keyMod != nil { if keyMod != nil {
l = keyMod(l) l = keyMod(l)

View File

@@ -408,7 +408,7 @@ const (
// hasMountInfoOption checks if any of the passed any of the given option values // hasMountInfoOption checks if any of the passed any of the given option values
// are set in the passed in option string. // are set in the passed in option string.
func hasMountInfoOption(opts string, vals ...string) bool { func hasMountInfoOption(opts string, vals ...string) bool {
for _, opt := range strings.Split(opts, " ") { for opt := range strings.SplitSeq(opts, " ") {
for _, val := range vals { for _, val := range vals {
if strings.HasPrefix(opt, val) { if strings.HasPrefix(opt, val) {
return true return true

View File

@@ -419,7 +419,7 @@ func (v *localVolume) LiveRestoreVolume(ctx context.Context, _ string) error {
// getAddress finds out address/hostname from options // getAddress finds out address/hostname from options
func getAddress(opts string) string { func getAddress(opts string) string {
for _, opt := range strings.Split(opts, ",") { for opt := range strings.SplitSeq(opts, ",") {
if strings.HasPrefix(opt, "addr=") { if strings.HasPrefix(opt, "addr=") {
return strings.TrimPrefix(opt, "addr=") return strings.TrimPrefix(opt, "addr=")
} }
@@ -429,7 +429,7 @@ func getAddress(opts string) string {
// getPassword finds out a password from options // getPassword finds out a password from options
func getPassword(opts string) string { func getPassword(opts string) string {
for _, opt := range strings.Split(opts, ",") { for opt := range strings.SplitSeq(opts, ",") {
if strings.HasPrefix(opt, "password=") { if strings.HasPrefix(opt, "password=") {
return strings.TrimPrefix(opt, "password=") return strings.TrimPrefix(opt, "password=")
} }

View File

@@ -174,7 +174,7 @@ var linuxPropagationModes = map[mount.Propagation]bool{
const linuxDefaultPropagationMode = mount.PropagationRPrivate const linuxDefaultPropagationMode = mount.PropagationRPrivate
func linuxGetPropagation(mode string) mount.Propagation { func linuxGetPropagation(mode string) mount.Propagation {
for _, o := range strings.Split(mode, ",") { for o := range strings.SplitSeq(mode, ",") {
prop := mount.Propagation(o) prop := mount.Propagation(o)
if linuxPropagationModes[prop] { if linuxPropagationModes[prop] {
return prop return prop
@@ -184,7 +184,7 @@ func linuxGetPropagation(mode string) mount.Propagation {
} }
func linuxHasPropagation(mode string) bool { func linuxHasPropagation(mode string) bool {
for _, o := range strings.Split(mode, ",") { for o := range strings.SplitSeq(mode, ",") {
if linuxPropagationModes[mount.Propagation(o)] { if linuxPropagationModes[mount.Propagation(o)] {
return true return true
} }
@@ -203,7 +203,7 @@ func linuxValidMountMode(mode string) bool {
copyModeCount := 0 copyModeCount := 0
consistencyModeCount := 0 consistencyModeCount := 0
for _, o := range strings.Split(mode, ",") { for o := range strings.SplitSeq(mode, ",") {
switch { switch {
case rwModes[o]: case rwModes[o]:
rwModeCount++ rwModeCount++
@@ -256,7 +256,7 @@ func (p *linuxParser) ReadWrite(mode string) bool {
return false return false
} }
for _, o := range strings.Split(mode, ",") { for o := range strings.SplitSeq(mode, ",") {
if o == "ro" { if o == "ro" {
return false return false
} }

View File

@@ -14,7 +14,7 @@ func copyModeExists(mode string) bool {
// GetCopyMode gets the copy mode from the mode string for mounts // GetCopyMode gets the copy mode from the mode string for mounts
func getCopyMode(mode string, def bool) (bool, bool) { func getCopyMode(mode string, def bool) (bool, bool) {
for _, o := range strings.Split(mode, ",") { for o := range strings.SplitSeq(mode, ",") {
if isEnabled, exists := copyModes[o]; exists { if isEnabled, exists := copyModes[o]; exists {
return isEnabled, true return isEnabled, true
} }

View File

@@ -569,7 +569,7 @@ type buildLine struct {
func getImageIDsFromBuild(t *testing.T, output []byte) []string { func getImageIDsFromBuild(t *testing.T, output []byte) []string {
var ids []string var ids []string
for _, line := range bytes.Split(output, []byte("\n")) { for line := range bytes.SplitSeq(output, []byte("\n")) {
if len(line) == 0 { if len(line) == 0 {
continue continue
} }

View File

@@ -199,8 +199,8 @@ func (s *DockerCLIImagesSuite) TestImagesFilterSinceAndBefore(c *testing.T) {
func getImageIDs(out string) []string { func getImageIDs(out string) []string {
var actual []string var actual []string
imgs := strings.Split(out, "\n") imgs := strings.SplitSeq(out, "\n")
for _, l := range imgs { for l := range imgs {
imgTag, imgDigest, _ := strings.Cut(l, "\t") imgTag, imgDigest, _ := strings.Cut(l, "\t")
if strings.HasPrefix(imgTag, "test_") { if strings.HasPrefix(imgTag, "test_") {
actual = append(actual, imgDigest) actual = append(actual, imgDigest)

View File

@@ -217,8 +217,8 @@ func (s *DockerCLILogsSuite) TestLogsSinceFutureFollow(c *testing.T) {
since := t.Unix() + 2 since := t.Unix() + 2
out := cli.DockerCmd(c, "logs", "-t", "-f", fmt.Sprintf("--since=%v", since), name).Combined() out := cli.DockerCmd(c, "logs", "-t", "-f", fmt.Sprintf("--since=%v", since), name).Combined()
assert.Assert(c, out != "", "cannot read from empty log") assert.Assert(c, out != "", "cannot read from empty log")
lines := strings.Split(strings.TrimSpace(out), "\n") lines := strings.SplitSeq(strings.TrimSpace(out), "\n")
for _, v := range lines { for v := range lines {
ts, err := time.Parse(time.RFC3339Nano, strings.Split(v, " ")[0]) ts, err := time.Parse(time.RFC3339Nano, strings.Split(v, " ")[0])
assert.NilError(c, err, "cannot parse timestamp output from log: '%v'", v) assert.NilError(c, err, "cannot parse timestamp output from log: '%v'", v)
assert.Assert(c, ts.Unix() >= since, "earlier log found. since=%v logdate=%v", since, ts) assert.Assert(c, ts.Unix() >= since, "earlier log found. since=%v logdate=%v", since, ts)

View File

@@ -531,7 +531,7 @@ func (s *DockerCLIPsSuite) TestPsListContainersFilterCreated(c *testing.T) {
out = cli.DockerCmd(c, "ps", "-a").Stdout() out = cli.DockerCmd(c, "ps", "-a").Stdout()
hits := 0 hits := 0
for _, line := range strings.Split(out, "\n") { for line := range strings.SplitSeq(out, "\n") {
if !strings.Contains(line, shortCID) { if !strings.Contains(line, shortCID) {
continue continue
} }

View File

@@ -157,7 +157,7 @@ func (s *DockerHubPullSuite) TestPullAllTagsFromCentralRegistry(c *testing.T) {
// Verify that the line for 'dockercore/engine-pull-all-test-fixture:latest' is left unchanged. // Verify that the line for 'dockercore/engine-pull-all-test-fixture:latest' is left unchanged.
var latestLine string var latestLine string
for _, line := range strings.Split(outImageAllTagCmd, "\n") { for line := range strings.SplitSeq(outImageAllTagCmd, "\n") {
if strings.HasPrefix(line, "dockercore/engine-pull-all-test-fixture") && strings.Contains(line, "latest") { if strings.HasPrefix(line, "dockercore/engine-pull-all-test-fixture") && strings.Contains(line, "latest") {
latestLine = line latestLine = line
break break

View File

@@ -79,7 +79,7 @@ func (s *DockerRegistrySuite) TestPushMultipleTags(c *testing.T) {
// Ensure layer list is equivalent for repoTag1 and repoTag2 // Ensure layer list is equivalent for repoTag1 and repoTag2
out1 := cli.DockerCmd(c, "push", repoTag1).Combined() out1 := cli.DockerCmd(c, "push", repoTag1).Combined()
var out1Lines []string var out1Lines []string
for _, outputLine := range strings.Split(out1, "\n") { for outputLine := range strings.SplitSeq(out1, "\n") {
if strings.Contains(outputLine, imageAlreadyExists) { if strings.Contains(outputLine, imageAlreadyExists) {
out1Lines = append(out1Lines, outputLine) out1Lines = append(out1Lines, outputLine)
} }
@@ -87,7 +87,7 @@ func (s *DockerRegistrySuite) TestPushMultipleTags(c *testing.T) {
out2 := cli.DockerCmd(c, "push", repoTag2).Combined() out2 := cli.DockerCmd(c, "push", repoTag2).Combined()
var out2Lines []string var out2Lines []string
for _, outputLine := range strings.Split(out2, "\n") { for outputLine := range strings.SplitSeq(out2, "\n") {
if strings.Contains(outputLine, imageAlreadyExists) { if strings.Contains(outputLine, imageAlreadyExists) {
out2Lines = append(out2Lines, outputLine) out2Lines = append(out2Lines, outputLine)
} }

View File

@@ -55,8 +55,8 @@ func getContainerCount(t *testing.T) int {
result := icmd.RunCommand(dockerBinary, "info") result := icmd.RunCommand(dockerBinary, "info")
result.Assert(t, icmd.Success) result.Assert(t, icmd.Success)
lines := strings.Split(result.Combined(), "\n") lines := strings.SplitSeq(result.Combined(), "\n")
for _, line := range lines { for line := range lines {
if strings.Contains(line, containers) { if strings.Contains(line, containers) {
output := strings.TrimSpace(line) output := strings.TrimSpace(line)
output = strings.TrimPrefix(output, containers) output = strings.TrimPrefix(output, containers)
@@ -435,7 +435,7 @@ func loadSpecialImage(t *testing.T, imageFunc specialimage.SpecialImageFunc) str
out := cli.DockerCmd(t, "load", "-i", imgTar).Stdout() out := cli.DockerCmd(t, "load", "-i", imgTar).Stdout()
for _, line := range strings.Split(out, "\n") { for line := range strings.SplitSeq(out, "\n") {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if _, imageID, hasID := strings.Cut(line, "Loaded image ID: "); hasID { if _, imageID, hasID := strings.Cut(line, "Loaded image ID: "); hasID {

View File

@@ -102,8 +102,8 @@ func (e *eventObserver) CheckEventError(t *testing.T, id, event string, match ev
if e.disconnectionError != nil { if e.disconnectionError != nil {
until := daemonUnixTime(t) until := daemonUnixTime(t)
out := cli.DockerCmd(t, "events", "--since", e.startTime, "--until", until).Stdout() out := cli.DockerCmd(t, "events", "--since", e.startTime, "--until", until).Stdout()
events := strings.Split(strings.TrimSpace(out), "\n") events := strings.SplitSeq(strings.TrimSpace(out), "\n")
for _, e := range events { for e := range events {
if _, ok := match(e); ok { if _, ok := match(e); ok {
foundEvent = true foundEvent = true
break break
@@ -176,7 +176,7 @@ func matchEventID(matches map[string]string, id string) bool {
if !matchID && matches["attributes"] != "" { if !matchID && matches["attributes"] != "" {
// try matching a name in the attributes // try matching a name in the attributes
attributes := map[string]string{} attributes := map[string]string{}
for _, a := range strings.Split(matches["attributes"], ", ") { for a := range strings.SplitSeq(matches["attributes"], ", ") {
kv := strings.Split(a, "=") kv := strings.Split(a, "=")
attributes[kv[0]] = kv[1] attributes[kv[0]] = kv[1]
} }
@@ -186,8 +186,8 @@ func matchEventID(matches map[string]string, id string) bool {
} }
func parseEvents(t *testing.T, out, match string) { func parseEvents(t *testing.T, out, match string) {
events := strings.Split(strings.TrimSpace(out), "\n") events := strings.SplitSeq(strings.TrimSpace(out), "\n")
for _, event := range events { for event := range events {
matches := eventstestutils.ScanMap(event) matches := eventstestutils.ScanMap(event)
matched, err := regexp.MatchString(match, matches["action"]) matched, err := regexp.MatchString(match, matches["action"])
assert.NilError(t, err) assert.NilError(t, err)

View File

@@ -24,7 +24,7 @@ func getPrefixAndSlashFromDaemonPlatform() (prefix, slash string) {
// a map which cgroup name as key and path as value. // a map which cgroup name as key and path as value.
func ParseCgroupPaths(procCgroupData string) map[string]string { func ParseCgroupPaths(procCgroupData string) map[string]string {
cgroupPaths := map[string]string{} cgroupPaths := map[string]string{}
for _, line := range strings.Split(procCgroupData, "\n") { for line := range strings.SplitSeq(procCgroupData, "\n") {
parts := strings.Split(line, ":") parts := strings.Split(line, ":")
if len(parts) != 3 { if len(parts) != 3 {
continue continue
@@ -120,7 +120,7 @@ func existingElements(t *testing.T, opts elementListOptions) []string {
} }
out := cli.DockerCmd(t, args...).Combined() out := cli.DockerCmd(t, args...).Combined()
var lines []string var lines []string
for _, l := range strings.Split(out, "\n") { for l := range strings.SplitSeq(out, "\n") {
if l != "" { if l != "" {
lines = append(lines, l) lines = append(lines, l)
} }

View File

@@ -861,7 +861,7 @@ type buildLine struct {
func getImageIDsFromBuild(output []byte) ([]string, error) { func getImageIDsFromBuild(output []byte) ([]string, error) {
var ids []string var ids []string
for _, line := range bytes.Split(output, []byte("\n")) { for line := range bytes.SplitSeq(output, []byte("\n")) {
if len(line) == 0 { if len(line) == 0 {
continue continue
} }

View File

@@ -891,7 +891,7 @@ func TestFirewallBackendSwitch(t *testing.T) {
}) })
// TODO: (When Go 1.24 is min version) Replace with `strings.Lines(dump)`. // TODO: (When Go 1.24 is min version) Replace with `strings.Lines(dump)`.
for _, line := range strings.Split(dump, "\n") { for line := range strings.SplitSeq(dump, "\n") {
line = strings.TrimSpace(line) line = strings.TrimSpace(line)
if line == "" { if line == "" {
continue continue

View File

@@ -1591,7 +1591,7 @@ func checkProxies(ctx context.Context, t *testing.T, c *client.Client, daemonPid
t.Error(res) t.Error(res)
return return
} }
for _, line := range strings.Split(res.Stdout(), "\n") { for line := range strings.SplitSeq(res.Stdout(), "\n") {
_, args, ok := strings.Cut(line, "docker-proxy") _, args, ok := strings.Cut(line, "docker-proxy")
if !ok { if !ok {
continue continue
@@ -1785,7 +1785,7 @@ func TestAdvertiseAddresses(t *testing.T) {
// the associated MAC address. // the associated MAC address.
findNeighMAC := func(neighOut, ip string) string { findNeighMAC := func(neighOut, ip string) string {
t.Helper() t.Helper()
for _, line := range strings.Split(neighOut, "\n") { for line := range strings.SplitSeq(neighOut, "\n") {
// Lines look like ... // Lines look like ...
// 172.22.22.22 dev eth0 lladdr 36:bc:ce:67:f3:e4 ref 1 used 0/7/0 probes 1 DELAY // 172.22.22.22 dev eth0 lladdr 36:bc:ce:67:f3:e4 ref 1 used 0/7/0 probes 1 DELAY
fields := strings.Fields(line) fields := strings.Fields(line)
@@ -2015,7 +2015,7 @@ func TestLegacyLinksEnvVars(t *testing.T) {
// Check the list of environment variables set in the linking container. // Check the list of environment variables set in the linking container.
var found bool var found bool
for _, l := range strings.Split(exportRes.Stdout.String(), "\n") { for l := range strings.SplitSeq(exportRes.Stdout.String(), "\n") {
if strings.HasPrefix(l, "export CTR1_") { if strings.HasPrefix(l, "export CTR1_") {
// Legacy links env var found, but not expected. // Legacy links env var found, but not expected.
if !tc.expectEnvVars { if !tc.expectEnvVars {

View File

@@ -281,8 +281,8 @@ func enableIPv6OnAll(t *testing.T) func() {
ifaces := map[string]string{} ifaces := map[string]string{}
var allVal string var allVal string
sysctls := strings.Split(string(out), "\n") sysctls := strings.SplitSeq(string(out), "\n")
for _, sysctl := range sysctls { for sysctl := range sysctls {
if sysctl == "" { if sysctl == "" {
continue continue
} }

View File

@@ -71,7 +71,7 @@ func IsContainerized() (bool, error) {
if err != nil { if err != nil {
return false, err return false, err
} }
for _, line := range bytes.Split(b, []byte{'\n'}) { for line := range bytes.SplitSeq(b, []byte{'\n'}) {
if len(line) > 0 && !bytes.HasSuffix(line, []byte(":/")) && !bytes.HasSuffix(line, []byte(":/init.scope")) { if len(line) > 0 && !bytes.HasSuffix(line, []byte(":/")) && !bytes.HasSuffix(line, []byte(":/init.scope")) {
return true, nil return true, nil
} }

View File

@@ -55,7 +55,7 @@ func findCgroupV1Mountpoints() (map[string]string, error) {
mps := make(map[string]string) mps := make(map[string]string)
for _, mi := range mounts { for _, mi := range mounts {
for _, opt := range strings.Split(mi.VFSOptions, ",") { for opt := range strings.SplitSeq(mi.VFSOptions, ",") {
seen, known := allMap[opt] seen, known := allMap[opt]
if known && !seen { if known && !seen {
allMap[opt] = true allMap[opt] = true
@@ -375,7 +375,7 @@ func parseUintList(val string, maximum int) (map[int]struct{}, error) {
availableInts := make(map[int]struct{}) availableInts := make(map[int]struct{})
errInvalidFormat := fmt.Errorf("invalid format: %s", val) errInvalidFormat := fmt.Errorf("invalid format: %s", val)
for _, r := range strings.Split(val, ",") { for r := range strings.SplitSeq(val, ",") {
if !strings.Contains(r, "-") { if !strings.Contains(r, "-") {
v, err := strconv.Atoi(r) v, err := strconv.Atoi(r)
if err != nil { if err != nil {