Blob Blame History Raw
package util

import (
	"fmt"
	"strings"
)

// The input mapping is experessed on the command line as `key1:value1,key2:value2,...`
// We parse it here, but need to keep in mind that keys or values may contain
// commas and colons. We will allow escaping those using double quotes, so
// when passing in "key1":"value1", we will not look inside the quoted sections.
func ParseCommandlineMap(src string) (map[string]string, error) {
	result := make(map[string]string)
	tuples := splitString(src, ',')
	for _, t := range tuples {
		kv := splitString(t, ':')
		if len(kv) != 2 {
			return nil, fmt.Errorf("expected key:value, got :%s", t)
		}
		key := strings.TrimLeft(kv[0], `"`)
		key = strings.TrimRight(key, `"`)

		value := strings.TrimLeft(kv[1], `"`)
		value = strings.TrimRight(value, `"`)

		result[key] = value
	}
	return result, nil
}

// This function splits a string along the specifed separator, but it
// ignores anything between double quotes for splitting. We do simple
// inside/outside quote counting. Quotes are not stripped from output.
func splitString(s string, sep rune) ([]string) {
	const escapeChar rune = '"'

	var parts []string
	var part string
	inQuotes := false

	for _, c := range s {
		if c == escapeChar{
			if inQuotes {
				inQuotes = false
			} else {
				inQuotes = true
			}
		}

		// If we've gotten the separator rune, consider the previous part
		// complete, but only if we're outside of quoted sections
		if c == sep && !inQuotes {
			parts = append(parts, part)
			part = ""
			continue
		}
		part = part + string(c)
	}
	return append(parts, part)
}