1// Package gintersect provides methods to check whether the intersection of several globs matches a non-empty set of strings.
2package gintersect
3
4import (
5	"fmt"
6	"strings"
7)
8
9// Glob represents a glob.
10type Glob []Token
11
12// NewGlob constructs a Glob from the given string by tokenizing and then simplifying it, or reports errors if any.
13func NewGlob(input string) (Glob, error) {
14	tokens, err := Tokenize([]rune(input))
15	if err != nil {
16		return nil, err
17	}
18
19	tokens = Simplify(tokens)
20
21	return Glob(tokens), nil
22}
23
24// TokenType is the type of a Token.
25type TokenType uint
26
27const (
28	TTCharacter TokenType = iota
29	TTDot
30	TTSet
31)
32
33// Flag applies to a token.
34type Flag uint
35
36func (f Flag) String() (s string) {
37	for r, flag := range flagRunes {
38		if f == flag {
39			s = string(r)
40			break
41		}
42	}
43	return
44}
45
46const (
47	FlagNone = iota
48	FlagPlus
49	FlagStar
50)
51
52// Token is the element that makes up a Glob.
53type Token interface {
54	Type() TokenType
55	Flag() Flag
56	SetFlag(Flag)
57	// Equal describes whether the given Token is exactly equal to this one, barring differences in flags.
58	Equal(Token) bool
59	String() string
60}
61
62// token is the base for all structs implementing Token.
63type token struct {
64	ttype TokenType
65	flag  Flag
66}
67
68func (t token) Type() TokenType {
69	return t.ttype
70}
71
72func (t token) Flag() Flag {
73	return t.flag
74}
75
76func (t *token) SetFlag(f Flag) {
77	t.flag = f
78}
79
80// character is a specific rune. It implements Token.
81type character struct {
82	token
83	r rune
84}
85
86func NewCharacter(r rune) Token {
87	return &character{
88		token: token{ttype: TTCharacter},
89		r:     r,
90	}
91}
92
93func (c character) Equal(other Token) bool {
94	if c.Type() != other.Type() {
95		return false
96	}
97
98	o := other.(*character)
99	return c.Rune() == o.Rune()
100}
101
102func (c character) String() string {
103	return fmt.Sprintf("{character: %s flag: %s}", string(c.Rune()), c.Flag().String())
104}
105
106func (c character) Rune() rune {
107	return c.r
108}
109
110// dot is any character. It implements Token.
111type dot struct {
112	token
113}
114
115func NewDot() Token {
116	return &dot{
117		token: token{ttype: TTDot},
118	}
119}
120
121func (d dot) Equal(other Token) bool {
122	if d.Type() != other.Type() {
123		return false
124	}
125
126	return true
127}
128
129func (d dot) String() string {
130	return fmt.Sprintf("{dot flag: %s}", d.Flag().String())
131}
132
133// set is a set of characters (similar to regexp character class).
134// It implements Token.
135type set struct {
136	token
137	runes map[rune]bool
138}
139
140func NewSet(runes []rune) Token {
141	m := map[rune]bool{}
142	for _, r := range runes {
143		m[r] = true
144	}
145	return &set{
146		token: token{ttype: TTSet},
147		runes: m,
148	}
149}
150
151func (s set) Equal(other Token) bool {
152	if s.Type() != other.Type() {
153		return false
154	}
155
156	o := other.(*set)
157	r1, r2 := s.Runes(), o.Runes()
158
159	if len(r1) != len(r2) {
160		return false
161	}
162
163	for k, _ := range r1 {
164		if _, ok := r2[k]; !ok {
165			return false
166		}
167	}
168
169	return true
170}
171
172func (s set) String() string {
173	rs := make([]string, 0, 30)
174	for r, _ := range s.Runes() {
175		rs = append(rs, string(r))
176	}
177	return fmt.Sprintf("{set: %s flag: %s}", strings.Join(rs, ""), s.Flag().String())
178}
179
180func (s set) Runes() map[rune]bool {
181	return s.runes
182}
183