浏览代码

Compare prefix against dashboards' slugs instead of their names

父节点
当前提交
fc150cfbb1
签署人:: Brendan Abolivier <contact@brendanabolivier.com> GPG 密钥 ID: 8EF1500759F70623

+ 4
- 2
src/config/config.go 查看文件

@@ -2,10 +2,10 @@ package config
2 2
 
3 3
 import (
4 4
 	"io/ioutil"
5
-	"strings"
6 5
 
7 6
 	"gopkg.in/yaml.v2"
8 7
 
8
+	"github.com/gosimple/slug"
9 9
 	"github.com/sirupsen/logrus"
10 10
 )
11 11
 
@@ -65,6 +65,8 @@ func Load(filename string) (cfg *Config, err error) {
65 65
 
66 66
 	cfg = new(Config)
67 67
 	err = yaml.Unmarshal(rawCfg, cfg)
68
-	cfg.Grafana.IgnorePrefix = strings.ToLower(cfg.Grafana.IgnorePrefix)
68
+	// Since we always compare the prefix against a slug, we need to make sure
69
+	// the prefix is a slug itself.
70
+	cfg.Grafana.IgnorePrefix = slug.Make(cfg.Grafana.IgnorePrefix)
69 71
 	return
70 72
 }

+ 22
- 0
src/grafana/helpers/helpers.go 查看文件

@@ -0,0 +1,22 @@
1
+package helpers
2
+
3
+import (
4
+	"encoding/json"
5
+
6
+	"github.com/gosimple/slug"
7
+)
8
+
9
+// GetDashboardSlug reads the JSON description of a dashboard and computes a
10
+// slug from the dashboard's title.
11
+// Returns an error if there was an issue parsing the dashboard JSON description.
12
+func GetDashboardSlug(dbJSONDescription []byte) (dbSlug string, err error) {
13
+	// Parse the file's content to find the dashboard's title
14
+	var dashboardTitle struct {
15
+		Title string `json:"title"`
16
+	}
17
+
18
+	err = json.Unmarshal(dbJSONDescription, &dashboardTitle)
19
+	// Compute the slug
20
+	dbSlug = slug.Make(dashboardTitle.Title)
21
+	return
22
+}

+ 1
- 2
src/puller/puller.go 查看文件

@@ -66,8 +66,7 @@ func PullGrafanaAndCommit(client *grafana.Client, cfg *config.Config) error {
66 66
 		}
67 67
 
68 68
 		if len(cfg.Grafana.IgnorePrefix) > 0 {
69
-			lowerCasedName := strings.ToLower(dashboard.Name)
70
-			if strings.HasPrefix(lowerCasedName, cfg.Grafana.IgnorePrefix) {
69
+			if strings.HasPrefix(dashboard.Slug, cfg.Grafana.IgnorePrefix) {
71 70
 				logrus.WithFields(logrus.Fields{
72 71
 					"uri":    uri,
73 72
 					"name":   dashboard.Name,

+ 6
- 10
src/pusher/webhook.go 查看文件

@@ -1,12 +1,12 @@
1 1
 package main
2 2
 
3 3
 import (
4
-	"encoding/json"
5 4
 	"io/ioutil"
6 5
 	"strings"
7 6
 
8 7
 	"config"
9 8
 	"git"
9
+	"grafana/helpers"
10 10
 	puller "puller"
11 11
 
12 12
 	"github.com/sirupsen/logrus"
@@ -183,18 +183,14 @@ func isIgnored(filename string) (bool, error) {
183 183
 		return false, err
184 184
 	}
185 185
 
186
-	// Parse the file's content to find the dashboard's name
187
-	var dashboardName struct {
188
-		Name string `json:"title"`
189
-	}
190
-	if err = json.Unmarshal(fileContent, &dashboardName); err != nil {
186
+	// Parse the file's content to extract its slug
187
+	slug, err := helpers.GetDashboardSlug(fileContent)
188
+	if err != nil {
191 189
 		return false, err
192 190
 	}
193 191
 
194
-	// Compare the lower case dashboar name to the prefix (which has already
195
-	// been lower cased when loading the configuration file)
196
-	lowerCaseName := strings.ToLower(dashboardName.Name)
197
-	if strings.HasPrefix(lowerCaseName, cfg.Grafana.IgnorePrefix) {
192
+	// Compare the slug against the prefix
193
+	if strings.HasPrefix(slug, cfg.Grafana.IgnorePrefix) {
198 194
 		return true, nil
199 195
 	}
200 196
 

+ 12
- 0
vendor/manifest 查看文件

@@ -8,6 +8,12 @@
8 8
 			"branch": "master"
9 9
 		},
10 10
 		{
11
+			"importpath": "github.com/gosimple/slug",
12
+			"repository": "https://github.com/gosimple/slug",
13
+			"revision": "e9f42fa127660e552d0ad2b589868d403a9be7c6",
14
+			"branch": "master"
15
+		},
16
+		{
11 17
 			"importpath": "github.com/jbenet/go-context/io",
12 18
 			"repository": "https://github.com/jbenet/go-context",
13 19
 			"revision": "d14ea06fba99483203c19d92cfcd13ebe73135f4",
@@ -27,6 +33,12 @@
27 33
 			"branch": "master"
28 34
 		},
29 35
 		{
36
+			"importpath": "github.com/rainycape/unidecode",
37
+			"repository": "https://github.com/rainycape/unidecode",
38
+			"revision": "cb7f23ec59bec0d61b19c56cd88cee3d0cc1870c",
39
+			"branch": "master"
40
+		},
41
+		{
30 42
 			"importpath": "github.com/sergi/go-diff/diffmatchpatch",
31 43
 			"repository": "https://github.com/sergi/go-diff",
32 44
 			"revision": "1744e2970ca51c86172c8190fadad617561ed6e7",

+ 373
- 0
vendor/src/github.com/gosimple/slug/LICENSE 查看文件

@@ -0,0 +1,373 @@
1
+Mozilla Public License Version 2.0
2
+==================================
3
+
4
+1. Definitions
5
+--------------
6
+
7
+1.1. "Contributor"
8
+    means each individual or legal entity that creates, contributes to
9
+    the creation of, or owns Covered Software.
10
+
11
+1.2. "Contributor Version"
12
+    means the combination of the Contributions of others (if any) used
13
+    by a Contributor and that particular Contributor's Contribution.
14
+
15
+1.3. "Contribution"
16
+    means Covered Software of a particular Contributor.
17
+
18
+1.4. "Covered Software"
19
+    means Source Code Form to which the initial Contributor has attached
20
+    the notice in Exhibit A, the Executable Form of such Source Code
21
+    Form, and Modifications of such Source Code Form, in each case
22
+    including portions thereof.
23
+
24
+1.5. "Incompatible With Secondary Licenses"
25
+    means
26
+
27
+    (a) that the initial Contributor has attached the notice described
28
+        in Exhibit B to the Covered Software; or
29
+
30
+    (b) that the Covered Software was made available under the terms of
31
+        version 1.1 or earlier of the License, but not also under the
32
+        terms of a Secondary License.
33
+
34
+1.6. "Executable Form"
35
+    means any form of the work other than Source Code Form.
36
+
37
+1.7. "Larger Work"
38
+    means a work that combines Covered Software with other material, in
39
+    a separate file or files, that is not Covered Software.
40
+
41
+1.8. "License"
42
+    means this document.
43
+
44
+1.9. "Licensable"
45
+    means having the right to grant, to the maximum extent possible,
46
+    whether at the time of the initial grant or subsequently, any and
47
+    all of the rights conveyed by this License.
48
+
49
+1.10. "Modifications"
50
+    means any of the following:
51
+
52
+    (a) any file in Source Code Form that results from an addition to,
53
+        deletion from, or modification of the contents of Covered
54
+        Software; or
55
+
56
+    (b) any new file in Source Code Form that contains any Covered
57
+        Software.
58
+
59
+1.11. "Patent Claims" of a Contributor
60
+    means any patent claim(s), including without limitation, method,
61
+    process, and apparatus claims, in any patent Licensable by such
62
+    Contributor that would be infringed, but for the grant of the
63
+    License, by the making, using, selling, offering for sale, having
64
+    made, import, or transfer of either its Contributions or its
65
+    Contributor Version.
66
+
67
+1.12. "Secondary License"
68
+    means either the GNU General Public License, Version 2.0, the GNU
69
+    Lesser General Public License, Version 2.1, the GNU Affero General
70
+    Public License, Version 3.0, or any later versions of those
71
+    licenses.
72
+
73
+1.13. "Source Code Form"
74
+    means the form of the work preferred for making modifications.
75
+
76
+1.14. "You" (or "Your")
77
+    means an individual or a legal entity exercising rights under this
78
+    License. For legal entities, "You" includes any entity that
79
+    controls, is controlled by, or is under common control with You. For
80
+    purposes of this definition, "control" means (a) the power, direct
81
+    or indirect, to cause the direction or management of such entity,
82
+    whether by contract or otherwise, or (b) ownership of more than
83
+    fifty percent (50%) of the outstanding shares or beneficial
84
+    ownership of such entity.
85
+
86
+2. License Grants and Conditions
87
+--------------------------------
88
+
89
+2.1. Grants
90
+
91
+Each Contributor hereby grants You a world-wide, royalty-free,
92
+non-exclusive license:
93
+
94
+(a) under intellectual property rights (other than patent or trademark)
95
+    Licensable by such Contributor to use, reproduce, make available,
96
+    modify, display, perform, distribute, and otherwise exploit its
97
+    Contributions, either on an unmodified basis, with Modifications, or
98
+    as part of a Larger Work; and
99
+
100
+(b) under Patent Claims of such Contributor to make, use, sell, offer
101
+    for sale, have made, import, and otherwise transfer either its
102
+    Contributions or its Contributor Version.
103
+
104
+2.2. Effective Date
105
+
106
+The licenses granted in Section 2.1 with respect to any Contribution
107
+become effective for each Contribution on the date the Contributor first
108
+distributes such Contribution.
109
+
110
+2.3. Limitations on Grant Scope
111
+
112
+The licenses granted in this Section 2 are the only rights granted under
113
+this License. No additional rights or licenses will be implied from the
114
+distribution or licensing of Covered Software under this License.
115
+Notwithstanding Section 2.1(b) above, no patent license is granted by a
116
+Contributor:
117
+
118
+(a) for any code that a Contributor has removed from Covered Software;
119
+    or
120
+
121
+(b) for infringements caused by: (i) Your and any other third party's
122
+    modifications of Covered Software, or (ii) the combination of its
123
+    Contributions with other software (except as part of its Contributor
124
+    Version); or
125
+
126
+(c) under Patent Claims infringed by Covered Software in the absence of
127
+    its Contributions.
128
+
129
+This License does not grant any rights in the trademarks, service marks,
130
+or logos of any Contributor (except as may be necessary to comply with
131
+the notice requirements in Section 3.4).
132
+
133
+2.4. Subsequent Licenses
134
+
135
+No Contributor makes additional grants as a result of Your choice to
136
+distribute the Covered Software under a subsequent version of this
137
+License (see Section 10.2) or under the terms of a Secondary License (if
138
+permitted under the terms of Section 3.3).
139
+
140
+2.5. Representation
141
+
142
+Each Contributor represents that the Contributor believes its
143
+Contributions are its original creation(s) or it has sufficient rights
144
+to grant the rights to its Contributions conveyed by this License.
145
+
146
+2.6. Fair Use
147
+
148
+This License is not intended to limit any rights You have under
149
+applicable copyright doctrines of fair use, fair dealing, or other
150
+equivalents.
151
+
152
+2.7. Conditions
153
+
154
+Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted
155
+in Section 2.1.
156
+
157
+3. Responsibilities
158
+-------------------
159
+
160
+3.1. Distribution of Source Form
161
+
162
+All distribution of Covered Software in Source Code Form, including any
163
+Modifications that You create or to which You contribute, must be under
164
+the terms of this License. You must inform recipients that the Source
165
+Code Form of the Covered Software is governed by the terms of this
166
+License, and how they can obtain a copy of this License. You may not
167
+attempt to alter or restrict the recipients' rights in the Source Code
168
+Form.
169
+
170
+3.2. Distribution of Executable Form
171
+
172
+If You distribute Covered Software in Executable Form then:
173
+
174
+(a) such Covered Software must also be made available in Source Code
175
+    Form, as described in Section 3.1, and You must inform recipients of
176
+    the Executable Form how they can obtain a copy of such Source Code
177
+    Form by reasonable means in a timely manner, at a charge no more
178
+    than the cost of distribution to the recipient; and
179
+
180
+(b) You may distribute such Executable Form under the terms of this
181
+    License, or sublicense it under different terms, provided that the
182
+    license for the Executable Form does not attempt to limit or alter
183
+    the recipients' rights in the Source Code Form under this License.
184
+
185
+3.3. Distribution of a Larger Work
186
+
187
+You may create and distribute a Larger Work under terms of Your choice,
188
+provided that You also comply with the requirements of this License for
189
+the Covered Software. If the Larger Work is a combination of Covered
190
+Software with a work governed by one or more Secondary Licenses, and the
191
+Covered Software is not Incompatible With Secondary Licenses, this
192
+License permits You to additionally distribute such Covered Software
193
+under the terms of such Secondary License(s), so that the recipient of
194
+the Larger Work may, at their option, further distribute the Covered
195
+Software under the terms of either this License or such Secondary
196
+License(s).
197
+
198
+3.4. Notices
199
+
200
+You may not remove or alter the substance of any license notices
201
+(including copyright notices, patent notices, disclaimers of warranty,
202
+or limitations of liability) contained within the Source Code Form of
203
+the Covered Software, except that You may alter any license notices to
204
+the extent required to remedy known factual inaccuracies.
205
+
206
+3.5. Application of Additional Terms
207
+
208
+You may choose to offer, and to charge a fee for, warranty, support,
209
+indemnity or liability obligations to one or more recipients of Covered
210
+Software. However, You may do so only on Your own behalf, and not on
211
+behalf of any Contributor. You must make it absolutely clear that any
212
+such warranty, support, indemnity, or liability obligation is offered by
213
+You alone, and You hereby agree to indemnify every Contributor for any
214
+liability incurred by such Contributor as a result of warranty, support,
215
+indemnity or liability terms You offer. You may include additional
216
+disclaimers of warranty and limitations of liability specific to any
217
+jurisdiction.
218
+
219
+4. Inability to Comply Due to Statute or Regulation
220
+---------------------------------------------------
221
+
222
+If it is impossible for You to comply with any of the terms of this
223
+License with respect to some or all of the Covered Software due to
224
+statute, judicial order, or regulation then You must: (a) comply with
225
+the terms of this License to the maximum extent possible; and (b)
226
+describe the limitations and the code they affect. Such description must
227
+be placed in a text file included with all distributions of the Covered
228
+Software under this License. Except to the extent prohibited by statute
229
+or regulation, such description must be sufficiently detailed for a
230
+recipient of ordinary skill to be able to understand it.
231
+
232
+5. Termination
233
+--------------
234
+
235
+5.1. The rights granted under this License will terminate automatically
236
+if You fail to comply with any of its terms. However, if You become
237
+compliant, then the rights granted under this License from a particular
238
+Contributor are reinstated (a) provisionally, unless and until such
239
+Contributor explicitly and finally terminates Your grants, and (b) on an
240
+ongoing basis, if such Contributor fails to notify You of the
241
+non-compliance by some reasonable means prior to 60 days after You have
242
+come back into compliance. Moreover, Your grants from a particular
243
+Contributor are reinstated on an ongoing basis if such Contributor
244
+notifies You of the non-compliance by some reasonable means, this is the
245
+first time You have received notice of non-compliance with this License
246
+from such Contributor, and You become compliant prior to 30 days after
247
+Your receipt of the notice.
248
+
249
+5.2. If You initiate litigation against any entity by asserting a patent
250
+infringement claim (excluding declaratory judgment actions,
251
+counter-claims, and cross-claims) alleging that a Contributor Version
252
+directly or indirectly infringes any patent, then the rights granted to
253
+You by any and all Contributors for the Covered Software under Section
254
+2.1 of this License shall terminate.
255
+
256
+5.3. In the event of termination under Sections 5.1 or 5.2 above, all
257
+end user license agreements (excluding distributors and resellers) which
258
+have been validly granted by You or Your distributors under this License
259
+prior to termination shall survive termination.
260
+
261
+************************************************************************
262
+*                                                                      *
263
+*  6. Disclaimer of Warranty                                           *
264
+*  -------------------------                                           *
265
+*                                                                      *
266
+*  Covered Software is provided under this License on an "as is"       *
267
+*  basis, without warranty of any kind, either expressed, implied, or  *
268
+*  statutory, including, without limitation, warranties that the       *
269
+*  Covered Software is free of defects, merchantable, fit for a        *
270
+*  particular purpose or non-infringing. The entire risk as to the     *
271
+*  quality and performance of the Covered Software is with You.        *
272
+*  Should any Covered Software prove defective in any respect, You     *
273
+*  (not any Contributor) assume the cost of any necessary servicing,   *
274
+*  repair, or correction. This disclaimer of warranty constitutes an   *
275
+*  essential part of this License. No use of any Covered Software is   *
276
+*  authorized under this License except under this disclaimer.         *
277
+*                                                                      *
278
+************************************************************************
279
+
280
+************************************************************************
281
+*                                                                      *
282
+*  7. Limitation of Liability                                          *
283
+*  --------------------------                                          *
284
+*                                                                      *
285
+*  Under no circumstances and under no legal theory, whether tort      *
286
+*  (including negligence), contract, or otherwise, shall any           *
287
+*  Contributor, or anyone who distributes Covered Software as          *
288
+*  permitted above, be liable to You for any direct, indirect,         *
289
+*  special, incidental, or consequential damages of any character      *
290
+*  including, without limitation, damages for lost profits, loss of    *
291
+*  goodwill, work stoppage, computer failure or malfunction, or any    *
292
+*  and all other commercial damages or losses, even if such party      *
293
+*  shall have been informed of the possibility of such damages. This   *
294
+*  limitation of liability shall not apply to liability for death or   *
295
+*  personal injury resulting from such party's negligence to the       *
296
+*  extent applicable law prohibits such limitation. Some               *
297
+*  jurisdictions do not allow the exclusion or limitation of           *
298
+*  incidental or consequential damages, so this exclusion and          *
299
+*  limitation may not apply to You.                                    *
300
+*                                                                      *
301
+************************************************************************
302
+
303
+8. Litigation
304
+-------------
305
+
306
+Any litigation relating to this License may be brought only in the
307
+courts of a jurisdiction where the defendant maintains its principal
308
+place of business and such litigation shall be governed by laws of that
309
+jurisdiction, without reference to its conflict-of-law provisions.
310
+Nothing in this Section shall prevent a party's ability to bring
311
+cross-claims or counter-claims.
312
+
313
+9. Miscellaneous
314
+----------------
315
+
316
+This License represents the complete agreement concerning the subject
317
+matter hereof. If any provision of this License is held to be
318
+unenforceable, such provision shall be reformed only to the extent
319
+necessary to make it enforceable. Any law or regulation which provides
320
+that the language of a contract shall be construed against the drafter
321
+shall not be used to construe this License against a Contributor.
322
+
323
+10. Versions of the License
324
+---------------------------
325
+
326
+10.1. New Versions
327
+
328
+Mozilla Foundation is the license steward. Except as provided in Section
329
+10.3, no one other than the license steward has the right to modify or
330
+publish new versions of this License. Each version will be given a
331
+distinguishing version number.
332
+
333
+10.2. Effect of New Versions
334
+
335
+You may distribute the Covered Software under the terms of the version
336
+of the License under which You originally received the Covered Software,
337
+or under the terms of any subsequent version published by the license
338
+steward.
339
+
340
+10.3. Modified Versions
341
+
342
+If you create software not governed by this License, and you want to
343
+create a new license for such software, you may create and use a
344
+modified version of this License if you rename the license and remove
345
+any references to the name of the license steward (except to note that
346
+such modified license differs from this License).
347
+
348
+10.4. Distributing Source Code Form that is Incompatible With Secondary
349
+Licenses
350
+
351
+If You choose to distribute Source Code Form that is Incompatible With
352
+Secondary Licenses under the terms of this version of the License, the
353
+notice described in Exhibit B of this License must be attached.
354
+
355
+Exhibit A - Source Code Form License Notice
356
+-------------------------------------------
357
+
358
+  This Source Code Form is subject to the terms of the Mozilla Public
359
+  License, v. 2.0. If a copy of the MPL was not distributed with this
360
+  file, You can obtain one at http://mozilla.org/MPL/2.0/.
361
+
362
+If it is not possible or desirable to put the notice in a particular
363
+file, then You may include the notice in a location (such as a LICENSE
364
+file in a relevant directory) where a recipient would be likely to look
365
+for such a notice.
366
+
367
+You may add additional accurate notices of copyright ownership.
368
+
369
+Exhibit B - "Incompatible With Secondary Licenses" Notice
370
+---------------------------------------------------------
371
+
372
+  This Source Code Form is "Incompatible With Secondary Licenses", as
373
+  defined by the Mozilla Public License, v. 2.0.

+ 54
- 0
vendor/src/github.com/gosimple/slug/README.md 查看文件

@@ -0,0 +1,54 @@
1
+slug
2
+====
3
+
4
+Package `slug` generate slug from unicode string, URL-friendly slugify with
5
+multiple languages support.
6
+
7
+[![GoDoc](https://godoc.org/github.com/gosimple/slug?status.png)](https://godoc.org/github.com/gosimple/slug)
8
+[![Build Status](https://drone.io/github.com/gosimple/slug/status.png)](https://drone.io/github.com/gosimple/slug/latest)
9
+
10
+[Documentation online](http://godoc.org/github.com/gosimple/slug)
11
+
12
+## Example
13
+
14
+	package main
15
+
16
+	import(
17
+		"github.com/gosimple/slug"
18
+	    "fmt"
19
+	)
20
+
21
+	func main () {
22
+		text := slug.Make("Hellö Wörld хелло ворлд")
23
+		fmt.Println(text) // Will print: "hello-world-khello-vorld"
24
+
25
+		someText := slug.Make("影師")
26
+		fmt.Println(someText) // Will print: "ying-shi"
27
+
28
+		enText := slug.MakeLang("This & that", "en")
29
+		fmt.Println(enText) // Will print: "this-and-that"
30
+
31
+		deText := slug.MakeLang("Diese & Dass", "de")
32
+		fmt.Println(deText) // Will print: "diese-und-dass"
33
+
34
+		slug.CustomSub = map[string]string{
35
+			"water": "sand",
36
+		}
37
+		textSub := slug.Make("water is hot")
38
+		fmt.Println(textSub) // Will print: "sand-is-hot"
39
+	}
40
+
41
+### Requests or bugs?
42
+<https://github.com/gosimple/slug/issues>
43
+
44
+## Installation
45
+
46
+	go get -u github.com/gosimple/slug
47
+
48
+## License
49
+
50
+The source files are distributed under the
51
+[Mozilla Public License, version 2.0](http://mozilla.org/MPL/2.0/),
52
+unless otherwise noted.
53
+Please read the [FAQ](http://www.mozilla.org/MPL/2.0/FAQ.html)
54
+if you have further questions regarding the license.

+ 43
- 0
vendor/src/github.com/gosimple/slug/doc.go 查看文件

@@ -0,0 +1,43 @@
1
+// Copyright 2013 by Dobrosław Żybort. All rights reserved.
2
+// This Source Code Form is subject to the terms of the Mozilla Public
3
+// License, v. 2.0. If a copy of the MPL was not distributed with this
4
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+
6
+/*
7
+Package slug generate slug from unicode string, URL-friendly slugify with
8
+multiple languages support.
9
+
10
+Example:
11
+
12
+	package main
13
+
14
+	import(
15
+		"github.com/gosimple/slug"
16
+		"fmt"
17
+	)
18
+
19
+	func main () {
20
+		text := slug.Make("Hellö Wörld хелло ворлд")
21
+		fmt.Println(text) // Will print hello-world-khello-vorld
22
+
23
+		someText := slug.Make("影師")
24
+		fmt.Println(someText) // Will print: ying-shi
25
+
26
+		enText := slug.MakeLang("This & that", "en")
27
+		fmt.Println(enText) // Will print 'this-and-that'
28
+
29
+		deText := slug.MakeLang("Diese & Dass", "de")
30
+		fmt.Println(deText) // Will print 'diese-und-dass'
31
+
32
+		slug.CustomSub = map[string]string{
33
+			"water": "sand",
34
+		}
35
+		textSub := slug.Make("water is hot")
36
+		fmt.Println(textSub) // Will print 'sand-is-hot'
37
+	}
38
+
39
+Requests or bugs?
40
+
41
+https://github.com/gosimple/slug/issues
42
+*/
43
+package slug

+ 57
- 0
vendor/src/github.com/gosimple/slug/languages_substitution.go 查看文件

@@ -0,0 +1,57 @@
1
+// Copyright 2013 by Dobrosław Żybort. All rights reserved.
2
+// This Source Code Form is subject to the terms of the Mozilla Public
3
+// License, v. 2.0. If a copy of the MPL was not distributed with this
4
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+
6
+package slug
7
+
8
+func init() {
9
+	// Merge language subs with the default one
10
+	for _, sub := range []*map[rune]string{&deSub, &enSub, &plSub, &esSub} {
11
+		for key, value := range defaultSub {
12
+			(*sub)[key] = value
13
+		}
14
+	}
15
+}
16
+
17
+var defaultSub = map[rune]string{
18
+	'"':  "",
19
+	'\'': "",
20
+	'’':  "",
21
+	'‒':  "-", // figure dash
22
+	'–':  "-", // en dash
23
+	'—':  "-", // em dash
24
+	'―':  "-", // horizontal bar
25
+}
26
+
27
+var deSub = map[rune]string{
28
+	'&': "und",
29
+	'@': "an",
30
+}
31
+
32
+var enSub = map[rune]string{
33
+	'&': "and",
34
+	'@': "at",
35
+}
36
+
37
+var plSub = map[rune]string{
38
+	'&': "i",
39
+	'@': "na",
40
+}
41
+
42
+var esSub = map[rune]string{
43
+	'&': "y",
44
+	'@': "en",
45
+}
46
+
47
+var grSub = map[rune]string{
48
+	'&': "kai",
49
+	'η': "i",
50
+	'ή': "i",
51
+	'Η': "i",
52
+	'ι': "i",
53
+	'ί': "i",
54
+	'Ι': "i",
55
+	'χ': "x",
56
+	'Χ': "x",
57
+}

+ 157
- 0
vendor/src/github.com/gosimple/slug/slug.go 查看文件

@@ -0,0 +1,157 @@
1
+// Copyright 2013 by Dobrosław Żybort. All rights reserved.
2
+// This Source Code Form is subject to the terms of the Mozilla Public
3
+// License, v. 2.0. If a copy of the MPL was not distributed with this
4
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+
6
+package slug
7
+
8
+import (
9
+	"bytes"
10
+	"regexp"
11
+	"sort"
12
+	"strings"
13
+
14
+	"github.com/rainycape/unidecode"
15
+)
16
+
17
+var (
18
+	// CustomSub stores custom substitution map
19
+	CustomSub map[string]string
20
+	// CustomRuneSub stores custom rune substitution map
21
+	CustomRuneSub map[rune]string
22
+
23
+	// MaxLength stores maximum slug length.
24
+	// It's smart so it will cat slug after full word.
25
+	// By default slugs aren't shortened.
26
+	// If MaxLength is smaller than length of the first word, then returned
27
+	// slug will contain only substring from the first word truncated
28
+	// after MaxLength.
29
+	MaxLength int
30
+
31
+	regexpNonAuthorizedChars = regexp.MustCompile("[^a-z0-9-_]")
32
+	regexpMultipleDashes     = regexp.MustCompile("-+")
33
+)
34
+
35
+//=============================================================================
36
+
37
+// Make returns slug generated from provided string. Will use "en" as language
38
+// substitution.
39
+func Make(s string) (slug string) {
40
+	return MakeLang(s, "en")
41
+}
42
+
43
+// MakeLang returns slug generated from provided string and will use provided
44
+// language for chars substitution.
45
+func MakeLang(s string, lang string) (slug string) {
46
+	slug = strings.TrimSpace(s)
47
+
48
+	// Custom substitutions
49
+	// Always substitute runes first
50
+	slug = SubstituteRune(slug, CustomRuneSub)
51
+	slug = Substitute(slug, CustomSub)
52
+
53
+	// Process string with selected substitution language
54
+	switch lang {
55
+	case "de":
56
+		slug = SubstituteRune(slug, deSub)
57
+	case "en":
58
+		slug = SubstituteRune(slug, enSub)
59
+	case "pl":
60
+		slug = SubstituteRune(slug, plSub)
61
+	case "es":
62
+		slug = SubstituteRune(slug, esSub)
63
+	case "gr":
64
+		slug = SubstituteRune(slug, grSub)
65
+	default: // fallback to "en" if lang not found
66
+		slug = SubstituteRune(slug, enSub)
67
+	}
68
+
69
+	// Process all non ASCII symbols
70
+	slug = unidecode.Unidecode(slug)
71
+
72
+	slug = strings.ToLower(slug)
73
+
74
+	// Process all remaining symbols
75
+	slug = regexpNonAuthorizedChars.ReplaceAllString(slug, "-")
76
+	slug = regexpMultipleDashes.ReplaceAllString(slug, "-")
77
+	slug = strings.Trim(slug, "-")
78
+
79
+	if MaxLength > 0 {
80
+		slug = smartTruncate(slug)
81
+	}
82
+
83
+	return slug
84
+}
85
+
86
+// Substitute returns string with superseded all substrings from
87
+// provided substitution map. Substitution map will be applied in alphabetic
88
+// order. Many passes, on one substitution another one could apply.
89
+func Substitute(s string, sub map[string]string) (buf string) {
90
+	buf = s
91
+	var keys []string
92
+	for k := range sub {
93
+		keys = append(keys, k)
94
+	}
95
+	sort.Strings(keys)
96
+
97
+	for _, key := range keys {
98
+		buf = strings.Replace(buf, key, sub[key], -1)
99
+	}
100
+	return
101
+}
102
+
103
+// SubstituteRune substitutes string chars with provided rune
104
+// substitution map. One pass.
105
+func SubstituteRune(s string, sub map[rune]string) string {
106
+	var buf bytes.Buffer
107
+	for _, c := range s {
108
+		if d, ok := sub[c]; ok {
109
+			buf.WriteString(d)
110
+		} else {
111
+			buf.WriteRune(c)
112
+		}
113
+	}
114
+	return buf.String()
115
+}
116
+
117
+func smartTruncate(text string) string {
118
+	if len(text) < MaxLength {
119
+		return text
120
+	}
121
+
122
+	var truncated string
123
+	words := strings.SplitAfter(text, "-")
124
+	// If MaxLength is smaller than length of the first word return word
125
+	// truncated after MaxLength.
126
+	if len(words[0]) > MaxLength {
127
+		return words[0][:MaxLength]
128
+	}
129
+	for _, word := range words {
130
+		if len(truncated)+len(word)-1 <= MaxLength {
131
+			truncated = truncated + word
132
+		} else {
133
+			break
134
+		}
135
+	}
136
+	return strings.Trim(truncated, "-")
137
+}
138
+
139
+// IsSlug returns True if provided text does not contain white characters,
140
+// punctuation, all letters are lower case and only from ASCII range.
141
+// It could contain `-` and `_` but not at the beginning or end of the text.
142
+// It should be in range of the MaxLength var if specified.
143
+// All output from slug.Make(text) should pass this test.
144
+func IsSlug(text string) bool {
145
+	if text == "" ||
146
+		(MaxLength > 0 && len(text) > MaxLength) ||
147
+		text[0] == '-' || text[0] == '_' ||
148
+		text[len(text)-1] == '-' || text[len(text)-1] == '_' {
149
+		return false
150
+	}
151
+	for _, c := range text {
152
+		if (c < 'a' || c > 'z') && c != '-' && c != '_' && (c < '0' || c > '9') {
153
+			return false
154
+		}
155
+	}
156
+	return true
157
+}

+ 421
- 0
vendor/src/github.com/gosimple/slug/slug_test.go 查看文件

@@ -0,0 +1,421 @@
1
+// Copyright 2013 by Dobrosław Żybort. All rights reserved.
2
+// This Source Code Form is subject to the terms of the Mozilla Public
3
+// License, v. 2.0. If a copy of the MPL was not distributed with this
4
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
+
6
+package slug
7
+
8
+import (
9
+	"testing"
10
+)
11
+
12
+//=============================================================================
13
+
14
+func TestSlugMake(t *testing.T) {
15
+	var testCases = []struct {
16
+		in   string
17
+		want string
18
+	}{
19
+		{"DOBROSLAWZYBORT", "dobroslawzybort"},
20
+		{"Dobroslaw Zybort", "dobroslaw-zybort"},
21
+		{"  Dobroslaw     Zybort  ?", "dobroslaw-zybort"},
22
+		{"Dobrosław Żybort", "dobroslaw-zybort"},
23
+		{"Ala ma 6 kotów.", "ala-ma-6-kotow"},
24
+
25
+		{"áÁàÀãÃâÂäÄąĄą̊Ą̊", "aaaaaaaaaaaaaa"},
26
+		{"ćĆĉĈçÇ", "cccccc"},
27
+		{"éÉèÈẽẼêÊëËęĘ", "eeeeeeeeeeee"},
28
+		{"íÍìÌĩĨîÎïÏįĮ", "iiiiiiiiiiii"},
29
+		{"łŁ", "ll"},
30
+		{"ńŃ", "nn"},
31
+		{"óÓòÒõÕôÔöÖǫǪǭǬø", "ooooooooooooooo"},
32
+		{"śŚ", "ss"},
33
+		{"úÚùÙũŨûÛüÜųŲ", "uuuuuuuuuuuu"},
34
+		{"y̨Y̨", "yy"},
35
+		{"źŹżŹ", "zzzz"},
36
+		{"·/,:;`˜'\"", ""},
37
+		{"2000–2013", "2000-2013"},
38
+		{"style—not", "style-not"},
39
+		{"test_slug", "test_slug"},
40
+		{"Æ", "ae"},
41
+		{"Ich heiße", "ich-heisse"},
42
+
43
+		{"This & that", "this-and-that"},
44
+		{"fácil €", "facil-eu"},
45
+		{"smile ☺", "smile"},
46
+		{"Hellö Wörld хелло ворлд", "hello-world-khello-vorld"},
47
+		{"\"C'est déjà l’été.\"", "cest-deja-lete"},
48
+		{"jaja---lol-méméméoo--a", "jaja-lol-mememeoo-a"},
49
+		{"影師", "ying-shi"},
50
+	}
51
+
52
+	for index, st := range testCases {
53
+		got := Make(st.in)
54
+		if got != st.want {
55
+			t.Errorf(
56
+				"%d. Make(%#v) = %#v; want %#v",
57
+				index, st.in, got, st.want)
58
+		}
59
+	}
60
+}
61
+
62
+func TestSlugMakeLang(t *testing.T) {
63
+	var testCases = []struct {
64
+		lang string
65
+		in   string
66
+		want string
67
+	}{
68
+		{"en", "This & that", "this-and-that"},
69
+		{"de", "This & that", "this-und-that"},
70
+		{"pl", "This & that", "this-i-that"},
71
+		{"es", "This & that", "this-y-that"},
72
+		{"gr", "This & that", "this-kai-that"},
73
+		{"test", "This & that", "this-and-that"}, // unknown lang, fallback to "en"
74
+	}
75
+
76
+	for index, smlt := range testCases {
77
+		got := MakeLang(smlt.in, smlt.lang)
78
+		if got != smlt.want {
79
+			t.Errorf(
80
+				"%d. MakeLang(%#v, %#v) = %#v; want %#v",
81
+				index, smlt.in, smlt.lang, got, smlt.want)
82
+		}
83
+	}
84
+}
85
+
86
+func TestSlugMakeUserSubstituteLang(t *testing.T) {
87
+	var testCases = []struct {
88
+		cSub map[string]string
89
+		lang string
90
+		in   string
91
+		want string
92
+	}{
93
+		{map[string]string{"'": " "}, "en", "That's great", "that-s-great"},
94
+		{map[string]string{"&": "or"}, "en", "This & that", "this-or-that"},                   // by default "&" => "and"
95
+		{map[string]string{"&": "or"}, "de", "This & that", "this-or-that"},                   // by default "&" => "und"
96
+		{map[string]string{"&": "or", "@": "the"}, "de", "@ This & that", "the-this-or-that"}, // by default "&" => "und", "@" => "an"
97
+	}
98
+
99
+	for index, smust := range testCases {
100
+		CustomSub = smust.cSub
101
+		got := MakeLang(smust.in, smust.lang)
102
+		if got != smust.want {
103
+			t.Errorf(
104
+				"%d. %#v; MakeLang(%#v, %#v) = %#v; want %#v",
105
+				index, smust.cSub, smust.in, smust.lang,
106
+				got, smust.want)
107
+
108
+		}
109
+	}
110
+}
111
+
112
+func TestSlugMakeSubstituteOrderLang(t *testing.T) {
113
+	// Always substitute runes first
114
+	var testCases = []struct {
115
+		rSub map[rune]string
116
+		sSub map[string]string
117
+		in   string
118
+		want string
119
+	}{
120
+		{map[rune]string{'o': "left"}, map[string]string{"o": "right"}, "o o", "left-left"},
121
+		{map[rune]string{'o': "left", 'a': "r"}, map[string]string{"o": "right"}, "o a o", "left-r-left"},
122
+		{map[rune]string{'o': "left"}, map[string]string{"o": "right", "a": "r"}, "a o a o", "r-left-r-left"},
123
+		{map[rune]string{'&': "down"}, map[string]string{"&": "up"}, "&", "down"},
124
+	}
125
+
126
+	for index, smsot := range testCases {
127
+		CustomRuneSub = smsot.rSub
128
+		CustomSub = smsot.sSub
129
+		got := Make(smsot.in)
130
+		if got != smsot.want {
131
+			t.Errorf(
132
+				"%d. %#v; %#v; Make(%#v) = %#v; want %#v",
133
+				index, smsot.rSub, smsot.sSub, smsot.in,
134
+				got, smsot.want)
135
+
136
+		}
137
+	}
138
+}
139
+
140
+func TestSubstituteLang(t *testing.T) {
141
+	var testCases = []struct {
142
+		cSub map[string]string
143
+		in   string
144
+		want string
145
+	}{
146
+		{map[string]string{"o": "no"}, "o o o", "no no no"},
147
+		{map[string]string{"o": "no", "a": "or"}, "o a o", "no nor no"},
148
+		{map[string]string{"a": "or", "o": "no"}, "o a o", "no nor no"},
149
+		{map[string]string{"'": " "}, "That's great", "That s great"},
150
+	}
151
+
152
+	for index, sst := range testCases {
153
+		got := Substitute(sst.in, sst.cSub)
154
+		if got != sst.want {
155
+			t.Errorf(
156
+				"%d. Substitute(%#v, %#v) = %#v; want %#v",
157
+				index, sst.in, sst.cSub, got, sst.want)
158
+		}
159
+	}
160
+}
161
+
162
+func TestSubstituteRuneLang(t *testing.T) {
163
+	var testCases = []struct {
164
+		cSub map[rune]string
165
+		in   string
166
+		want string
167
+	}{
168
+		{map[rune]string{'o': "no"}, "o o o", "no no no"},
169
+		{map[rune]string{'o': "no", 'a': "or"}, "o a o", "no or no"},
170
+		{map[rune]string{'a': "or", 'o': "no"}, "o a o", "no or no"},
171
+		{map[rune]string{'\'': " "}, "That's great", "That s great"},
172
+	}
173
+
174
+	for index, ssrt := range testCases {
175
+		got := SubstituteRune(ssrt.in, ssrt.cSub)
176
+		if got != ssrt.want {
177
+			t.Errorf(
178
+				"%d. SubstituteRune(%#v, %#v) = %#v; want %#v",
179
+				index, ssrt.in, ssrt.cSub, got, ssrt.want)
180
+		}
181
+	}
182
+}
183
+
184
+func TestSlugMakeSmartTruncate(t *testing.T) {
185
+	var testCases = []struct {
186
+		in        string
187
+		maxLength int
188
+		want      string
189
+	}{
190
+		{"DOBROSLAWZYBORT", 100, "dobroslawzybort"},
191
+		{"Dobroslaw Zybort", 100, "dobroslaw-zybort"},
192
+		{"Dobroslaw Zybort", 12, "dobroslaw"},
193
+		{"  Dobroslaw     Zybort  ?", 12, "dobroslaw"},
194
+		{"Ala ma 6 kotów.", 10, "ala-ma-6"},
195
+		{"Dobrosław Żybort", 5, "dobro"},
196
+	}
197
+
198
+	for index, smstt := range testCases {
199
+		MaxLength = smstt.maxLength
200
+		got := Make(smstt.in)
201
+		if got != smstt.want {
202
+			t.Errorf(
203
+				"%d. MaxLength = %v; Make(%#v) = %#v; want %#v",
204
+				index, smstt.maxLength, smstt.in, got, smstt.want)
205
+		}
206
+	}
207
+}
208
+
209
+func TestIsSlug(t *testing.T) {
210
+	MaxLength = 0
211
+	type args struct {
212
+		text string
213
+	}
214
+	tests := []struct {
215
+		name string
216
+		args args
217
+		want bool
218
+	}{
219
+		{"some", args{"some"}, true},
220
+		{"with -", args{"some-more"}, true},
221
+		{"with _", args{"some_more"}, true},
222
+		{"with numbers", args{"number-2"}, true},
223
+		{"empty string", args{""}, false},
224
+		{"upper case", args{"Some-more"}, false},
225
+		{"space", args{"some more"}, false},
226
+		{"starts with '-'", args{"-some"}, false},
227
+		{"ends with '-'", args{"some-"}, false},
228
+		{"starts with '_'", args{"_some"}, false},
229
+		{"ends with '_'", args{"some_"}, false},
230
+		{"outside ASCII", args{"Dobrosław Żybort"}, false},
231
+		{"outside ASCII –", args{"2000–2013"}, false},
232
+		{"smile ☺", args{"smile ☺"}, false},
233
+	}
234
+	for _, tt := range tests {
235
+		t.Run(tt.name, func(t *testing.T) {
236
+			if got := IsSlug(tt.args.text); got != tt.want {
237
+				t.Errorf("IsSlug() = %v, want %v", got, tt.want)
238
+			}
239
+		})
240
+	}
241
+
242
+	t.Run("MaxLength", func(t *testing.T) {
243
+		MaxLength = 4
244
+		if got := IsSlug("012345"); got != false {
245
+			t.Errorf("IsSlug() = %v, want %v", got, false)
246
+		}
247
+		MaxLength = 0
248
+	})
249
+}
250
+
251
+func BenchmarkMakeShortAscii(b *testing.B) {
252
+	b.ReportAllocs()
253
+	for n := 0; n < b.N; n++ {
254
+		Make("Hello world")
255
+	}
256
+}
257
+func BenchmarkMakeShort(b *testing.B) {
258
+	b.ReportAllocs()
259
+	for n := 0; n < b.N; n++ {
260
+		Make("хелло ворлд")
261
+	}
262
+}
263
+
264
+func BenchmarkMakeShortSymbols(b *testing.B) {
265
+	b.ReportAllocs()
266
+	for n := 0; n < b.N; n++ {
267
+		Make("·/,:;`˜'\" &€£¥")
268
+	}
269
+}
270
+
271
+func BenchmarkMakeMediumAscii(b *testing.B) {
272
+	b.ReportAllocs()
273
+	for n := 0; n < b.N; n++ {
274
+		Make("ABCDE FGHIJ KLMNO PQRST UWXYZ ABCDE FGHIJ KLMNO PQRST UWXYZ ABCDE")
275
+	}
276
+}
277
+
278
+func BenchmarkMakeMedium(b *testing.B) {
279
+	b.ReportAllocs()
280
+	for n := 0; n < b.N; n++ {
281
+		Make("ヲァィゥェ ォャュョッ ーアイウエ オカキクケ コサシスセ ソタチツテ トナニヌネ ノハヒフヘ ホマミムメ モヤユヨラ リルレロワ")
282
+	}
283
+}
284
+
285
+func BenchmarkMakeLongAscii(b *testing.B) {
286
+	longStr := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi " +
287
+		"pulvinar sodales ultrices. Nulla facilisi. Sed at vestibulum erat. Ut " +
288
+		"sit amet urna posuere, sagittis eros ac, varius nisi. Morbi ullamcorper " +
289
+		"odio at nunc pulvinar mattis. Vestibulum rutrum, ante eu dictum mattis, " +
290
+		"elit risus finibus nunc, consectetur facilisis eros leo ut sapien. Sed " +
291
+		"pulvinar volutpat mi. Cras semper mi ac eros accumsan, at feugiat massa " +
292
+		"elementum. Morbi eget dolor sit amet purus condimentum egestas non ut " +
293
+		"sapien. Duis feugiat magna vitae nisi lobortis, quis finibus sem " +
294
+		"sollicitudin. Pellentesque eleifend blandit ipsum, ut porta arcu " +
295
+		"ultricies et. Fusce vel ipsum porta, placerat diam ac, consectetur " +
296
+		"magna. Nulla in porta sem. Suspendisse commodo, felis in molestie " +
297
+		"ultricies, arcu ipsum aliquet turpis, elementum dapibus ipsum lorem a " +
298
+		"nisl. Etiam varius imperdiet placerat. Aliquam euismod lacus arcu, " +
299
+		"ultrices hendrerit est pellentesque vel. Aliquam sit amet laoreet leo. " +
300
+		"Integer eros libero, mollis sed posuere."
301
+
302
+	b.ReportAllocs()
303
+	b.ResetTimer()
304
+	for n := 0; n < b.N; n++ {
305
+		Make(longStr)
306
+	}
307
+}
308
+
309
+func BenchmarkSubstituteRuneShort(b *testing.B) {
310
+	shortStr := "Hello/Hi world"
311
+	subs := map[rune]string{'o': "no", '/': "slash"}
312
+
313
+	b.ReportAllocs()
314
+	b.ResetTimer()
315
+	for n := 0; n < b.N; n++ {
316
+		SubstituteRune(shortStr, subs)
317
+	}
318
+}
319
+
320
+func BenchmarkSubstituteRuneLong(b *testing.B) {
321
+	longStr := "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi " +
322
+		"pulvinar sodales ultrices. Nulla facilisi. Sed at vestibulum erat. Ut " +
323
+		"sit amet urna posuere, sagittis eros ac, varius nisi. Morbi ullamcorper " +
324
+		"odio at nunc pulvinar mattis. Vestibulum rutrum, ante eu dictum mattis, " +
325
+		"elit risus finibus nunc, consectetur facilisis eros leo ut sapien. Sed " +
326
+		"pulvinar volutpat mi. Cras semper mi ac eros accumsan, at feugiat massa " +
327
+		"elementum. Morbi eget dolor sit amet purus condimentum egestas non ut " +
328
+		"sapien. Duis feugiat magna vitae nisi lobortis, quis finibus sem " +
329
+		"sollicitudin. Pellentesque eleifend blandit ipsum, ut porta arcu " +
330
+		"ultricies et. Fusce vel ipsum porta, placerat diam ac, consectetur " +
331
+		"magna. Nulla in porta sem. Suspendisse commodo, felis in molestie " +
332
+		"ultricies, arcu ipsum aliquet turpis, elementum dapibus ipsum lorem a " +
333
+		"nisl. Etiam varius imperdiet placerat. Aliquam euismod lacus arcu, " +
334
+		"ultrices hendrerit est pellentesque vel. Aliquam sit amet laoreet leo. " +
335
+		"Integer eros libero, mollis sed posuere."
336
+	subs := map[rune]string{
337
+		'o': "no",
338
+		'/': "slash",
339
+		'i': "done",
340
+		'E': "es",
341
+		'a': "ASD",
342
+		'1': "one",
343
+		'l': "onetwo",
344
+	}
345
+
346
+	b.ReportAllocs()
347
+	b.ResetTimer()
348
+	for n := 0; n < b.N; n++ {
349
+		SubstituteRune(longStr, subs)
350
+	}
351
+}
352
+
353
+func BenchmarkSmartTruncateShort(b *testing.B) {
354
+	shortStr := "Hello-world"
355
+	MaxLength = 8
356
+
357
+	b.ReportAllocs()
358
+	b.ResetTimer()
359
+	for n := 0; n < b.N; n++ {
360
+		smartTruncate(shortStr)
361
+	}
362
+}
363
+
364
+func BenchmarkSmartTruncateLong(b *testing.B) {
365
+	longStr := "Lorem-ipsum-dolor-sit-amet,-consectetur-adipiscing-elit.-Morbi-" +
366
+		"pulvinar-sodales-ultrices.-Nulla-facilisi.-Sed-at-vestibulum-erat.-Ut-" +
367
+		"sit-amet-urna-posuere,-sagittis-eros-ac,-varius-nisi.-Morbi-ullamcorper-" +
368
+		"odio-at-nunc-pulvinar-mattis.-Vestibulum-rutrum,-ante-eu-dictum-mattis,-" +
369
+		"elit-risus-finibus-nunc,-consectetur-facilisis-eros-leo-ut-sapien.-Sed-" +
370
+		"pulvinar-volutpat-mi.-Cras-semper-mi-ac-eros-accumsan,-at-feugiat-massa-" +
371
+		"elementum.-Morbi-eget-dolor-sit-amet-purus-condimentum-egestas-non-ut-" +
372
+		"sapien.-Duis-feugiat-magna-vitae-nisi-lobortis,-quis-finibus-sem-" +
373
+		"sollicitudin.-Pellentesque-eleifend-blandit-ipsum,-ut-porta-arcu-" +
374
+		"ultricies-et.-Fusce-vel-ipsum-porta,-placerat-diam-ac,-consectetur-" +
375
+		"magna.-Nulla-in-porta-sem.-Suspendisse-commodo,-felis-in-molestie-" +
376
+		"ultricies,-arcu-ipsum-aliquet-turpis,-elementum-dapibus-ipsum-lorem-a-" +
377
+		"nisl.-Etiam-varius-imperdiet-placerat.-Aliquam-euismod-lacus-arcu,-" +
378
+		"ultrices-hendrerit-est-pellentesque-vel.-Aliquam-sit-amet-laoreet-leo.-" +
379
+		"Integer-eros-libero,-mollis-sed-posuere."
380
+	MaxLength = 256
381
+
382
+	b.ReportAllocs()
383
+	b.ResetTimer()
384
+	for n := 0; n < b.N; n++ {
385
+		smartTruncate(longStr)
386
+	}
387
+}
388
+
389
+func BenchmarkIsSlugShort(b *testing.B) {
390
+	shortStr := "hello-world"
391
+
392
+	b.ReportAllocs()
393
+	b.ResetTimer()
394
+	for n := 0; n < b.N; n++ {
395
+		IsSlug(shortStr)
396
+	}
397
+}
398
+
399
+func BenchmarkIsSlugLong(b *testing.B) {
400
+	longStr := "lorem-ipsum-dolor-sit-amet-consectetur-adipiscing-elit-morbi-" +
401
+		"pulvinar-sodales-ultrices-nulla-facilisi-sed-at-vestibulum-erat-ut-" +
402
+		"sit-amet-urna-posuere-sagittis-eros-ac-varius-nisi-morbi-ullamcorper-" +
403
+		"odio-at-nunc-pulvinar-mattis-vestibulum-rutrum-ante-eu-dictum-mattis,-" +
404
+		"elit-risus-finibus-nunc-consectetur-facilisis-eros-leo-ut-sapien-sed-" +
405
+		"pulvinar-volutpat-mi-cras-semper-mi-ac-eros-accumsan-at-feugiat-massa-" +
406
+		"elementum-morbi-eget-dolor-sit-amet-purus-condimentum-egestas-non-ut-" +
407
+		"sapien-duis-feugiat-magna-vitae-nisi-lobortis-quis-finibus-sem-" +
408
+		"sollicitudin-pellentesque-eleifend-blandit-ipsum-ut-porta-arcu-" +
409
+		"ultricies-et-fusce-vel-ipsum-porta-placerat-diam-ac-consectetur-" +
410
+		"magna-nulla-in-porta-sem-suspendisse-commodo-felis-in-molestie-" +
411
+		"ultricies-arcu-ipsum-aliquet-turpis-elementum-dapibus-ipsum-lorem-a-" +
412
+		"nisl-etiam-varius-imperdiet-placerat-aliquam-euismod-lacus-arcu-" +
413
+		"ultrices-hendrerit-est-pellentesque-vel-aliquam-sit-amet-laoreet-leo-" +
414
+		"integer-eros-libero-mollis-sed-posuere"
415
+
416
+	b.ReportAllocs()
417
+	b.ResetTimer()
418
+	for n := 0; n < b.N; n++ {
419
+		IsSlug(longStr)
420
+	}
421
+}

+ 203
- 0
vendor/src/github.com/rainycape/unidecode/LICENSE 查看文件

@@ -0,0 +1,203 @@
1
+Copyright 2014 Rainy Cape S.L. <hello@rainycape.com>
2
+
3
+Apache License
4
+                           Version 2.0, January 2004
5
+                        http://www.apache.org/licenses/
6
+
7
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+   1. Definitions.
10
+
11
+      "License" shall mean the terms and conditions for use, reproduction,
12
+      and distribution as defined by Sections 1 through 9 of this document.
13
+
14
+      "Licensor" shall mean the copyright owner or entity authorized by
15
+      the copyright owner that is granting the License.
16
+
17
+      "Legal Entity" shall mean the union of the acting entity and all
18
+      other entities that control, are controlled by, or are under common
19
+      control with that entity. For the purposes of this definition,
20
+      "control" means (i) the power, direct or indirect, to cause the
21
+      direction or management of such entity, whether by contract or
22
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
23
+      outstanding shares, or (iii) beneficial ownership of such entity.
24
+
25
+      "You" (or "Your") shall mean an individual or Legal Entity
26
+      exercising permissions granted by this License.
27
+
28
+      "Source" form shall mean the preferred form for making modifications,
29
+      including but not limited to software source code, documentation
30
+      source, and configuration files.
31
+
32
+      "Object" form shall mean any form resulting from mechanical
33
+      transformation or translation of a Source form, including but
34
+      not limited to compiled object code, generated documentation,
35
+      and conversions to other media types.
36
+
37
+      "Work" shall mean the work of authorship, whether in Source or
38
+      Object form, made available under the License, as indicated by a
39
+      copyright notice that is included in or attached to the work
40
+      (an example is provided in the Appendix below).
41
+
42
+      "Derivative Works" shall mean any work, whether in Source or Object
43
+      form, that is based on (or derived from) the Work and for which the
44
+      editorial revisions, annotations, elaborations, or other modifications
45
+      represent, as a whole, an original work of authorship. For the purposes
46
+      of this License, Derivative Works shall not include works that remain
47
+      separable from, or merely link (or bind by name) to the interfaces of,
48
+      the Work and Derivative Works thereof.
49
+
50
+      "Contribution" shall mean any work of authorship, including
51
+      the original version of the Work and any modifications or additions
52
+      to that Work or Derivative Works thereof, that is intentionally
53
+      submitted to Licensor for inclusion in the Work by the copyright owner
54
+      or by an individual or Legal Entity authorized to submit on behalf of
55
+      the copyright owner. For the purposes of this definition, "submitted"
56
+      means any form of electronic, verbal, or written communication sent
57
+      to the Licensor or its representatives, including but not limited to
58
+      communication on electronic mailing lists, source code control systems,
59
+      and issue tracking systems that are managed by, or on behalf of, the
60
+      Licensor for the purpose of discussing and improving the Work, but
61
+      excluding communication that is conspicuously marked or otherwise
62
+      designated in writing by the copyright owner as "Not a Contribution."
63
+
64
+      "Contributor" shall mean Licensor and any individual or Legal Entity
65
+      on behalf of whom a Contribution has been received by Licensor and
66
+      subsequently incorporated within the Work.
67
+
68
+   2. Grant of Copyright License. Subject to the terms and conditions of
69
+      this License, each Contributor hereby grants to You a perpetual,
70
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71
+      copyright license to reproduce, prepare Derivative Works of,
72
+      publicly display, publicly perform, sublicense, and distribute the
73
+      Work and such Derivative Works in Source or Object form.
74
+
75
+   3. Grant of Patent License. Subject to the terms and conditions of
76
+      this License, each Contributor hereby grants to You a perpetual,
77
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78
+      (except as stated in this section) patent license to make, have made,
79
+      use, offer to sell, sell, import, and otherwise transfer the Work,
80
+      where such license applies only to those patent claims licensable
81
+      by such Contributor that are necessarily infringed by their
82
+      Contribution(s) alone or by combination of their Contribution(s)
83
+      with the Work to which such Contribution(s) was submitted. If You
84
+      institute patent litigation against any entity (including a
85
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
86
+      or a Contribution incorporated within the Work constitutes direct
87
+      or contributory patent infringement, then any patent licenses
88
+      granted to You under this License for that Work shall terminate
89
+      as of the date such litigation is filed.
90
+
91
+   4. Redistribution. You may reproduce and distribute copies of the
92
+      Work or Derivative Works thereof in any medium, with or without
93
+      modifications, and in Source or Object form, provided that You
94
+      meet the following conditions:
95
+
96
+      (a) You must give any other recipients of the Work or
97
+          Derivative Works a copy of this License; and
98
+
99
+      (b) You must cause any modified files to carry prominent notices
100
+          stating that You changed the files; and
101
+
102
+      (c) You must retain, in the Source form of any Derivative Works
103
+          that You distribute, all copyright, patent, trademark, and
104
+          attribution notices from the Source form of the Work,
105
+          excluding those notices that do not pertain to any part of
106
+          the Derivative Works; and
107
+
108
+      (d) If the Work includes a "NOTICE" text file as part of its
109
+          distribution, then any Derivative Works that You distribute must
110
+          include a readable copy of the attribution notices contained
111
+          within such NOTICE file, excluding those notices that do not
112
+          pertain to any part of the Derivative Works, in at least one
113
+          of the following places: within a NOTICE text file distributed
114
+          as part of the Derivative Works; within the Source form or
115
+          documentation, if provided along with the Derivative Works; or,
116
+          within a display generated by the Derivative Works, if and
117
+          wherever such third-party notices normally appear. The contents
118
+          of the NOTICE file are for informational purposes only and
119
+          do not modify the License. You may add Your own attribution
120
+          notices within Derivative Works that You distribute, alongside
121
+          or as an addendum to the NOTICE text from the Work, provided
122
+          that such additional attribution notices cannot be construed
123
+          as modifying the License.
124
+
125
+      You may add Your own copyright statement to Your modifications and
126
+      may provide additional or different license terms and conditions
127
+      for use, reproduction, or distribution of Your modifications, or
128
+      for any such Derivative Works as a whole, provided Your use,
129
+      reproduction, and distribution of the Work otherwise complies with
130
+      the conditions stated in this License.
131
+
132
+   5. Submission of Contributions. Unless You explicitly state otherwise,
133
+      any Contribution intentionally submitted for inclusion in the Work
134
+      by You to the Licensor shall be under the terms and conditions of
135
+      this License, without any additional terms or conditions.
136
+      Notwithstanding the above, nothing herein shall supersede or modify
137
+      the terms of any separate license agreement you may have executed
138
+      with Licensor regarding such Contributions.
139
+
140
+   6. Trademarks. This License does not grant permission to use the trade
141
+      names, trademarks, service marks, or product names of the Licensor,
142
+      except as required for reasonable and customary use in describing the
143
+      origin of the Work and reproducing the content of the NOTICE file.
144
+
145
+   7. Disclaimer of Warranty. Unless required by applicable law or
146
+      agreed to in writing, Licensor provides the Work (and each
147
+      Contributor provides its Contributions) on an "AS IS" BASIS,
148
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149
+      implied, including, without limitation, any warranties or conditions
150
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151
+      PARTICULAR PURPOSE. You are solely responsible for determining the
152
+      appropriateness of using or redistributing the Work and assume any
153
+      risks associated with Your exercise of permissions under this License.
154
+
155
+   8. Limitation of Liability. In no event and under no legal theory,
156
+      whether in tort (including negligence), contract, or otherwise,
157
+      unless required by applicable law (such as deliberate and grossly
158
+      negligent acts) or agreed to in writing, shall any Contributor be
159
+      liable to You for damages, including any direct, indirect, special,
160
+      incidental, or consequential damages of any character arising as a
161
+      result of this License or out of the use or inability to use the
162
+      Work (including but not limited to damages for loss of goodwill,
163
+      work stoppage, computer failure or malfunction, or any and all
164
+      other commercial damages or losses), even if such Contributor
165
+      has been advised of the possibility of such damages.
166
+
167
+   9. Accepting Warranty or Additional Liability. While redistributing
168
+      the Work or Derivative Works thereof, You may choose to offer,
169
+      and charge a fee for, acceptance of support, warranty, indemnity,
170
+      or other liability obligations and/or rights consistent with this
171
+      License. However, in accepting such obligations, You may act only
172
+      on Your own behalf and on Your sole responsibility, not on behalf
173
+      of any other Contributor, and only if You agree to indemnify,
174
+      defend, and hold each Contributor harmless for any liability
175
+      incurred by, or claims asserted against, such Contributor by reason
176
+      of your accepting any such warranty or additional liability.
177
+
178
+   END OF TERMS AND CONDITIONS
179
+
180
+   APPENDIX: How to apply the Apache License to your work.
181
+
182
+      To apply the Apache License to your work, attach the following
183
+      boilerplate notice, with the fields enclosed by brackets "{}"
184
+      replaced with your own identifying information. (Don't include
185
+      the brackets!)  The text should be enclosed in the appropriate
186
+      comment syntax for the file format. We also recommend that a
187
+      file or class name and description of purpose be included on the
188
+      same "printed page" as the copyright notice for easier
189
+      identification within third-party archives.
190
+
191
+   Copyright {yyyy} {name of copyright owner}
192
+
193
+   Licensed under the Apache License, Version 2.0 (the "License");
194
+   you may not use this file except in compliance with the License.
195
+   You may obtain a copy of the License at
196
+
197
+       http://www.apache.org/licenses/LICENSE-2.0
198
+
199
+   Unless required by applicable law or agreed to in writing, software
200
+   distributed under the License is distributed on an "AS IS" BASIS,
201
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
202
+   See the License for the specific language governing permissions and
203
+   limitations under the License.

+ 6
- 0
vendor/src/github.com/rainycape/unidecode/README.md 查看文件

@@ -0,0 +1,6 @@
1
+unidecode
2
+=========
3
+
4
+Unicode transliterator in Golang - Replaces non-ASCII characters with their ASCII approximations.
5
+
6
+[![GoDoc](https://godoc.org/github.com/rainycape/unidecode?status.svg)](https://godoc.org/github.com/rainycape/unidecode)

+ 41
- 0
vendor/src/github.com/rainycape/unidecode/decode.go 查看文件

@@ -0,0 +1,41 @@
1
+package unidecode
2
+
3
+import (
4
+	"compress/zlib"
5
+	"encoding/binary"
6
+	"io"
7
+	"strings"
8
+)
9
+
10
+var (
11
+	transliterations [65536][]rune
12
+	transCount       = rune(len(transliterations))
13
+	getUint16        = binary.LittleEndian.Uint16
14
+)
15
+
16
+func decodeTransliterations() {
17
+	r, err := zlib.NewReader(strings.NewReader(tableData))
18
+	if err != nil {
19
+		panic(err)
20
+	}
21
+	defer r.Close()
22
+	tmp1 := make([]byte, 2)
23
+	tmp2 := tmp1[:1]
24
+	for {
25
+		if _, err := io.ReadAtLeast(r, tmp1, 2); err != nil {
26
+			if err == io.EOF {
27
+				break
28
+			}
29
+			panic(err)
30
+		}
31
+		chr := getUint16(tmp1)
32
+		if _, err := io.ReadAtLeast(r, tmp2, 1); err != nil {
33
+			panic(err)
34
+		}
35
+		b := make([]byte, int(tmp2[0]))
36
+		if _, err := io.ReadFull(r, b); err != nil {
37
+			panic(err)
38
+		}
39
+		transliterations[int(chr)] = []rune(string(b))
40
+	}
41
+}

+ 71
- 0
vendor/src/github.com/rainycape/unidecode/make_table.go 查看文件

@@ -0,0 +1,71 @@
1
+// +build none
2
+
3
+package main
4
+
5
+import (
6
+	"bytes"
7
+	"compress/zlib"
8
+	"encoding/binary"
9
+	"fmt"
10
+	"go/format"
11
+	"io/ioutil"
12
+	"strconv"
13
+	"strings"
14
+)
15
+
16
+func main() {
17
+	data, err := ioutil.ReadFile("table.txt")
18
+	if err != nil {
19
+		panic(err)
20
+	}
21
+	var buf bytes.Buffer
22
+	for _, line := range strings.Split(string(data), "\n") {
23
+		if strings.HasPrefix(line, "/*") || line == "" {
24
+			continue
25
+		}
26
+		sep := strings.IndexByte(line, ':')
27
+		if sep == -1 {
28
+			panic(line)
29
+		}
30
+		val, err := strconv.ParseInt(line[:sep], 0, 32)
31
+		if err != nil {
32
+			panic(err)
33
+		}
34
+		s, err := strconv.Unquote(line[sep+2:])
35
+		if err != nil {
36
+			panic(err)
37
+		}
38
+		if s == "" {
39
+			continue
40
+		}
41
+		if err := binary.Write(&buf, binary.LittleEndian, uint16(val)); err != nil {
42
+			panic(err)
43
+		}
44
+		if err := binary.Write(&buf, binary.LittleEndian, uint8(len(s))); err != nil {
45
+			panic(err)
46
+		}
47
+		buf.WriteString(s)
48
+	}
49
+	var cbuf bytes.Buffer
50
+	w, err := zlib.NewWriterLevel(&cbuf, zlib.BestCompression)
51
+	if err != nil {
52
+		panic(err)
53
+	}
54
+	if _, err := w.Write(buf.Bytes()); err != nil {
55
+		panic(err)
56
+	}
57
+	if err := w.Close(); err != nil {
58
+		panic(err)
59
+	}
60
+	buf.Reset()
61
+	buf.WriteString("package unidecode\n")
62
+	buf.WriteString("// AUTOGENERATED - DO NOT EDIT!\n\n")
63
+	fmt.Fprintf(&buf, "var tableData = %q;\n", cbuf.String())
64
+	dst, err := format.Source(buf.Bytes())
65
+	if err != nil {
66
+		panic(err)
67
+	}
68
+	if err := ioutil.WriteFile("table.go", dst, 0644); err != nil {
69
+		panic(err)
70
+	}
71
+}

+ 5
- 0
vendor/src/github.com/rainycape/unidecode/table.go
文件差异内容过多而无法显示
查看文件


+ 46731
- 0
vendor/src/github.com/rainycape/unidecode/table.txt
文件差异内容过多而无法显示
查看文件


+ 58
- 0
vendor/src/github.com/rainycape/unidecode/unidecode.go 查看文件

@@ -0,0 +1,58 @@
1
+// Package unidecode implements a unicode transliterator
2
+// which replaces non-ASCII characters with their ASCII
3
+// approximations.
4
+package unidecode
5
+
6
+//go:generate go run make_table.go
7
+
8
+import (
9
+	"sync"
10
+	"unicode"
11
+)
12
+
13
+const pooledCapacity = 64
14
+
15
+var (
16
+	slicePool    sync.Pool
17
+	decodingOnce sync.Once
18
+)
19
+
20
+// Unidecode implements a unicode transliterator, which
21
+// replaces non-ASCII characters with their ASCII
22
+// counterparts.
23
+// Given an unicode encoded string, returns
24
+// another string with non-ASCII characters replaced
25
+// with their closest ASCII counterparts.
26
+// e.g. Unicode("áéíóú") => "aeiou"
27
+func Unidecode(s string) string {
28
+	decodingOnce.Do(decodeTransliterations)
29
+	l := len(s)
30
+	var r []rune
31
+	if l > pooledCapacity {
32
+		r = make([]rune, 0, len(s))
33
+	} else {
34
+		if x := slicePool.Get(); x != nil {
35
+			r = x.([]rune)[:0]
36
+		} else {
37
+			r = make([]rune, 0, pooledCapacity)
38
+		}
39
+	}
40
+	for _, c := range s {
41
+		if c <= unicode.MaxASCII {
42
+			r = append(r, c)
43
+			continue
44
+		}
45
+		if c > unicode.MaxRune || c > transCount {
46
+			/* Ignore reserved chars */
47
+			continue
48
+		}
49
+		if d := transliterations[c]; d != nil {
50
+			r = append(r, d...)
51
+		}
52
+	}
53
+	res := string(r)
54
+	if l <= pooledCapacity {
55
+		slicePool.Put(r)
56
+	}
57
+	return res
58
+}

+ 57
- 0
vendor/src/github.com/rainycape/unidecode/unidecode_test.go 查看文件

@@ -0,0 +1,57 @@
1
+package unidecode
2
+
3
+import (
4
+	"testing"
5
+)
6
+
7
+func testTransliteration(original string, decoded string, t *testing.T) {
8
+	if r := Unidecode(original); r != decoded {
9
+		t.Errorf("Expected '%s', got '%s'\n", decoded, r)
10
+	}
11
+}
12
+
13
+func TestASCII(t *testing.T) {
14
+	s := "ABCDEF"
15
+	testTransliteration(s, s, t)
16
+}
17
+
18
+func TestKnosos(t *testing.T) {
19
+	o := "Κνωσός"
20
+	d := "Knosos"
21
+	testTransliteration(o, d, t)
22
+}
23
+
24
+func TestBeiJing(t *testing.T) {
25
+	o := "\u5317\u4EB0"
26
+	d := "Bei Jing "
27
+	testTransliteration(o, d, t)
28
+}
29
+
30
+func TestEmoji(t *testing.T) {
31
+	o := "Hey Luna t belle 😵😂"
32
+	d := "Hey Luna t belle "
33
+	testTransliteration(o, d, t)
34
+}
35
+
36
+func BenchmarkUnidecode(b *testing.B) {
37
+	cases := []string{
38
+		"ABCDEF",
39
+		"Κνωσός",
40
+		"\u5317\u4EB0",
41
+	}
42
+	for ii := 0; ii < b.N; ii++ {
43
+		for _, v := range cases {
44
+			_ = Unidecode(v)
45
+		}
46
+	}
47
+}
48
+
49
+func BenchmarkDecodeTable(b *testing.B) {
50
+	for ii := 0; ii < b.N; ii++ {
51
+		decodeTransliterations()
52
+	}
53
+}
54
+
55
+func init() {
56
+	decodeTransliterations()
57
+}