func RemoveDuplicatesStable(items []string, caseInsensitive bool) []string {
	itemsMap := make(map[string]bool, len(items))
	deduplicated := make([]string, 0, len(items))
	for _, item := range items {
		key := strings.TrimSpace(item)
		if caseInsensitive {
			key = strings.ToLower(key)
		}
		if key == "" || itemsMap[key] {
			continue
		}
		itemsMap[key] = true
		deduplicated = append(deduplicated, item)
	}
	return deduplicated
}
RemoveDuplicatesStable removes duplicate and empty elements from a slice of
strings, preserving order (and case) of the original slice.
In all cases, strings are compared after trimming whitespace
If caseInsensitive, strings will be compared after ToLower()
package strutil
import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"sort"
	"strings"
	"github.com/hashicorp/errwrap"
	glob "github.com/ryanuber/go-glob"
)
func StrListContainsGlob(haystack []string, needle string) bool {
	for _, item := range haystack {
		if glob.Glob(item, needle) {
			return true
		}
	}
	return false
}
func StrListContains(haystack []string, needle string) bool {
	for _, item := range haystack {
		if item == needle {
			return true
		}
	}
	return false
}
func StrListSubset(super, sub []string) bool {
	for _, item := range sub {
		if !StrListContains(super, item) {
			return false
		}
	}
	return true
}
func ParseDedupAndSortStrings(input string, sep string) []string {
	input = strings.TrimSpace(input)
	parsed := []string{}
	if input == "" {
		return parsed
	}
	return RemoveDuplicates(strings.Split(input, sep), false)
}
func ParseDedupLowercaseAndSortStrings(input string, sep string) []string {
	input = strings.TrimSpace(input)
	parsed := []string{}
	if input == "" {
		return parsed
	}
	return RemoveDuplicates(strings.Split(input, sep), true)
}
func ParseKeyValues(input string, out map[string]string, sep string) error {
	if out == nil {
		return fmt.Errorf("'out is nil")
	}
	keyValues := ParseDedupLowercaseAndSortStrings(input, sep)
	if len(keyValues) == 0 {
		return nil
	}
	for _, keyValue := range keyValues {
		shards := strings.Split(keyValue, "=")
		if len(shards) != 2 {
			return fmt.Errorf("invalid <key,value> format")
		}
		key := strings.TrimSpace(shards[0])
		value := strings.TrimSpace(shards[1])
		if key == "" || value == "" {
			return fmt.Errorf("invalid <key,value> pair: key: %q value: %q", key, value)
		}
		out[key] = value
	}
	return nil
}
func ParseArbitraryKeyValues(input string, out map[string]string, sep string) error {
	input = strings.TrimSpace(input)
	if input == "" {
		return nil
	}
	if out == nil {
		return fmt.Errorf("'out' is nil")
	}
	inputBytes, err := base64.StdEncoding.DecodeString(input)
	if err == nil {
		input = string(inputBytes)
	}
	err = json.Unmarshal([]byte(input), &out)
	if err != nil {
		if err = ParseKeyValues(input, out, sep); err != nil {
			return errwrap.Wrapf("failed to parse the input: {{err}}", err)
		}
	}
	for key, value := range out {
		if key != "" && value == "" {
			return fmt.Errorf("invalid value for key %q", key)
		}
	}
	return nil
}
func ParseStringSlice(input string, sep string) []string {
	input = strings.TrimSpace(input)
	if input == "" {
		return []string{}
	}
	splitStr := strings.Split(input, sep)
	ret := make([]string, len(splitStr))
	for i, val := range splitStr {
		ret[i] = strings.TrimSpace(val)
	}
	return ret
}
func ParseArbitraryStringSlice(input string, sep string) []string {
	input = strings.TrimSpace(input)
	if input == "" {
		return []string{}
	}
	inputBytes, err := base64.StdEncoding.DecodeString(input)
	if err == nil {
		input = string(inputBytes)
	}
	ret := []string{}
	err = json.Unmarshal([]byte(input), &ret)
	if err != nil {
		return ParseStringSlice(input, sep)
	}
	if ret == nil {
		return []string{}
	}
	return ret
}
func TrimStrings(items []string) []string {
	ret := make([]string, len(items))
	for i, item := range items {
		ret[i] = strings.TrimSpace(item)
	}
	return ret
}
func RemoveDuplicates(items []string, lowercase bool) []string {
	itemsMap := map[string]bool{}
	for _, item := range items {
		item = strings.TrimSpace(item)
		if lowercase {
			item = strings.ToLower(item)
		}
		if item == "" {
			continue
		}
		itemsMap[item] = true
	}
	items = make([]string, 0, len(itemsMap))
	for item := range itemsMap {
		items = append(items, item)
	}
	sort.Strings(items)
	return items
}