forked from franciscolourenco/fisher-index
-
Notifications
You must be signed in to change notification settings - Fork 5
/
Copy pathindexLine.go
150 lines (123 loc) · 3.07 KB
/
indexLine.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package main
import (
"fmt"
"log"
"reflect"
"regexp"
"strings"
)
type indexCode int
const (
indexName indexCode = iota
indexLink
indexDesc
indexTags
indexAuthor
)
var (
indexNames map[string]bool
indexUrls map[string]bool
lastName string
urlMatcher *regexp.Regexp
)
func init() {
log.SetFlags(0) // Set logging to have no prefixes
indexNames = make(map[string]bool)
indexUrls = make(map[string]bool)
urlMatcher = regexp.MustCompile(`^http(?:s)?://(?:github.com|gitlab.com|bitbucket.org|gist.github.com)/(?:\S+)/(?:\S+)$`)
}
type indexLineError struct {
what interface{}
lineText string
lineNumber int
}
func (e *indexLineError) Error() string {
what := ""
switch reflect.TypeOf(e.what).Kind() {
case reflect.Slice:
errs := reflect.ValueOf(e.what)
for i := 0; i < errs.Len(); i++ {
what += " " + errs.Index(i).String() + "\n"
}
default:
what, _ = e.what.(string)
what = " " + what + "\n"
}
return fmt.Sprintf("%d %s\n ---\n%s ...", e.lineNumber+1, e.lineText, what)
}
type indexLine struct {
no int
code indexCode
text string
}
func (l *indexLine) verify() error {
// Checks if fisher index is correctly formatted: 5 lines + 1 blank
if l.text == "" && l.no%6 != 5 {
log.Fatalf("line %d: index is incorrectly formatted\n", l.no)
}
switch l.code {
case indexName:
// Check if name is unique
if indexNames[l.text] {
return &indexLineError{"Name is not unique", l.text, l.no}
}
if lastName > l.text {
return &indexLineError{"Index is not sorted", l.text, l.no}
}
lastName = l.text
indexNames[l.text] = true
case indexLink:
// Check if URL is Unique
if indexUrls[l.text] {
return &indexLineError{"Url is not unique", l.text, l.no}
}
indexUrls[l.text] = true
if urlMatcher.MatchString(l.text) != true {
return &indexLineError{"Url in not valid", l.text, l.no}
}
case indexDesc:
// Check Description
// if _, ok := regexp.MatchString(`^[\w\s]+$`, l.text); ok != nil {
// return &indexLineError{"Description is not valid", l.text, l.no}
// }
case indexTags:
// Check duplicate and invalid tags
encountered := map[string]bool{}
duplicates := []string{}
invalids := []string{}
longs := []string{}
errors := []string{}
tags := strings.Fields(l.text)
for j := range tags {
if ok, _ := regexp.MatchString(`^[a-zA-z0-9_-]+$`, tags[j]); !ok {
invalids = append(invalids, tags[j])
}
if len(tags[j]) > 15 {
longs = append(longs, tags[j])
}
if encountered[tags[j]] {
duplicates = append(duplicates, tags[j])
} else {
encountered[tags[j]] = true
}
}
if len(tags) > 4 {
errors = append(errors, "Too many tags")
}
if len(longs) != 0 {
errors = append(errors, "Long tags: "+strings.Join(longs, ", "))
}
if len(invalids) != 0 {
errors = append(errors, "Invalid tags: "+strings.Join(invalids, ", "))
}
if len(duplicates) != 0 {
errors = append(errors, "Duplicate tags: "+strings.Join(duplicates, ", "))
}
if len(errors) != 0 {
return &indexLineError{errors, l.text, l.no}
}
case indexAuthor:
// Check Author
}
return nil
}