1/*
2 * MinIO Go Library for Amazon S3 Compatible Cloud Storage
3 * Copyright 2015-2017 MinIO, Inc.
4 *
5 * Licensed under the Apache License, Version 2.0 (the "License");
6 * you may not use this file except in compliance with the License.
7 * You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18package set
19
20import (
21	"encoding/json"
22	"fmt"
23	"sort"
24)
25
26// StringSet - uses map as set of strings.
27type StringSet map[string]struct{}
28
29// ToSlice - returns StringSet as string slice.
30func (set StringSet) ToSlice() []string {
31	keys := make([]string, 0, len(set))
32	for k := range set {
33		keys = append(keys, k)
34	}
35	sort.Strings(keys)
36	return keys
37}
38
39// IsEmpty - returns whether the set is empty or not.
40func (set StringSet) IsEmpty() bool {
41	return len(set) == 0
42}
43
44// Add - adds string to the set.
45func (set StringSet) Add(s string) {
46	set[s] = struct{}{}
47}
48
49// Remove - removes string in the set.  It does nothing if string does not exist in the set.
50func (set StringSet) Remove(s string) {
51	delete(set, s)
52}
53
54// Contains - checks if string is in the set.
55func (set StringSet) Contains(s string) bool {
56	_, ok := set[s]
57	return ok
58}
59
60// FuncMatch - returns new set containing each value who passes match function.
61// A 'matchFn' should accept element in a set as first argument and
62// 'matchString' as second argument.  The function can do any logic to
63// compare both the arguments and should return true to accept element in
64// a set to include in output set else the element is ignored.
65func (set StringSet) FuncMatch(matchFn func(string, string) bool, matchString string) StringSet {
66	nset := NewStringSet()
67	for k := range set {
68		if matchFn(k, matchString) {
69			nset.Add(k)
70		}
71	}
72	return nset
73}
74
75// ApplyFunc - returns new set containing each value processed by 'applyFn'.
76// A 'applyFn' should accept element in a set as a argument and return
77// a processed string.  The function can do any logic to return a processed
78// string.
79func (set StringSet) ApplyFunc(applyFn func(string) string) StringSet {
80	nset := NewStringSet()
81	for k := range set {
82		nset.Add(applyFn(k))
83	}
84	return nset
85}
86
87// Equals - checks whether given set is equal to current set or not.
88func (set StringSet) Equals(sset StringSet) bool {
89	// If length of set is not equal to length of given set, the
90	// set is not equal to given set.
91	if len(set) != len(sset) {
92		return false
93	}
94
95	// As both sets are equal in length, check each elements are equal.
96	for k := range set {
97		if _, ok := sset[k]; !ok {
98			return false
99		}
100	}
101
102	return true
103}
104
105// Intersection - returns the intersection with given set as new set.
106func (set StringSet) Intersection(sset StringSet) StringSet {
107	nset := NewStringSet()
108	for k := range set {
109		if _, ok := sset[k]; ok {
110			nset.Add(k)
111		}
112	}
113
114	return nset
115}
116
117// Difference - returns the difference with given set as new set.
118func (set StringSet) Difference(sset StringSet) StringSet {
119	nset := NewStringSet()
120	for k := range set {
121		if _, ok := sset[k]; !ok {
122			nset.Add(k)
123		}
124	}
125
126	return nset
127}
128
129// Union - returns the union with given set as new set.
130func (set StringSet) Union(sset StringSet) StringSet {
131	nset := NewStringSet()
132	for k := range set {
133		nset.Add(k)
134	}
135
136	for k := range sset {
137		nset.Add(k)
138	}
139
140	return nset
141}
142
143// MarshalJSON - converts to JSON data.
144func (set StringSet) MarshalJSON() ([]byte, error) {
145	return json.Marshal(set.ToSlice())
146}
147
148// UnmarshalJSON - parses JSON data and creates new set with it.
149// If 'data' contains JSON string array, the set contains each string.
150// If 'data' contains JSON string, the set contains the string as one element.
151// If 'data' contains Other JSON types, JSON parse error is returned.
152func (set *StringSet) UnmarshalJSON(data []byte) error {
153	sl := []string{}
154	var err error
155	if err = json.Unmarshal(data, &sl); err == nil {
156		*set = make(StringSet)
157		for _, s := range sl {
158			set.Add(s)
159		}
160	} else {
161		var s string
162		if err = json.Unmarshal(data, &s); err == nil {
163			*set = make(StringSet)
164			set.Add(s)
165		}
166	}
167
168	return err
169}
170
171// String - returns printable string of the set.
172func (set StringSet) String() string {
173	return fmt.Sprintf("%s", set.ToSlice())
174}
175
176// NewStringSet - creates new string set.
177func NewStringSet() StringSet {
178	return make(StringSet)
179}
180
181// CreateStringSet - creates new string set with given string values.
182func CreateStringSet(sl ...string) StringSet {
183	set := make(StringSet)
184	for _, k := range sl {
185		set.Add(k)
186	}
187	return set
188}
189
190// CopyStringSet - returns copy of given set.
191func CopyStringSet(set StringSet) StringSet {
192	nset := NewStringSet()
193	for k, v := range set {
194		nset[k] = v
195	}
196	return nset
197}
198