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
|
package filenamify
import (
"errors"
"math"
"path/filepath"
"regexp"
)
type Options struct {
// String for substitution
Replacement string
// maxlength
MaxLength int
}
const MAX_FILENAME_LENGTH = 100
func Filenamify(str string, options Options) (string, error) {
var replacement string
reControlCharsRegex := regexp.MustCompile("[\u0000-\u001f\u0080-\u009f]")
reRelativePathRegex := regexp.MustCompile(`^\.+`)
// https://github.com/sindresorhus/filename-reserved-regex/blob/master/index.js
filenameReservedRegex := regexp.MustCompile(`[<>:"/\\|?*\x00-\x1F]`)
filenameReservedWindowsNamesRegex := regexp.MustCompile(`(?i)^(con|prn|aux|nul|com[0-9]|lpt[0-9])$`)
if options.Replacement == "" {
replacement = "!"
} else {
replacement = options.Replacement
}
if filenameReservedRegex.MatchString(replacement) && reControlCharsRegex.MatchString(replacement) {
return "", errors.New("Replacement string cannot contain reserved filename characters")
}
// reserved word
str = filenameReservedRegex.ReplaceAllString(str, replacement)
// continue
str = reControlCharsRegex.ReplaceAllString(str, replacement)
str = reRelativePathRegex.ReplaceAllString(str, replacement)
// for repeat
if len(replacement) > 0 {
str = trimRepeated(str, replacement)
if len(str) > 1 {
str = stripOuter(str, replacement)
}
}
// for windows names
if filenameReservedWindowsNamesRegex.MatchString(str) {
str = str + replacement
}
// limit length
var limitLength int
if options.MaxLength > 0 {
limitLength = options.MaxLength
} else {
limitLength = MAX_FILENAME_LENGTH
}
strBuf := []byte(str)
strBuf = strBuf[0:int(math.Min(float64(limitLength), float64(len(strBuf))))]
return string(strBuf), nil
}
func FilenamifyV2(str string, optFuns ...func(options *Options)) (string, error) {
options := Options{
Replacement: "!",
MaxLength: MAX_FILENAME_LENGTH,
}
for _, fn := range optFuns {
fn(&options)
}
return Filenamify(str, options)
}
func Path(filePath string, options Options) (string, error) {
p, err := filepath.Abs(filePath)
if err != nil {
return "", err
}
p, err = Filenamify(filepath.Base(p), options)
if err != nil {
return "", err
}
return filepath.Join(filepath.Dir(p), p), nil
}
func PathV2(str string, optFuns ...func(options *Options)) (string, error) {
options := Options{
Replacement: "!",
MaxLength: MAX_FILENAME_LENGTH,
}
for _, fn := range optFuns {
fn(&options)
}
return Path(str, options)
}
func escapeStringRegexp(str string) string {
// https://github.com/sindresorhus/escape-string-regexp/blob/master/index.js
reg := regexp.MustCompile(`[|\\{}()[\]^$+*?.-]`)
str = reg.ReplaceAllStringFunc(str, func(s string) string {
return `\` + s
})
return str
}
func trimRepeated(str string, replacement string) string {
reg := regexp.MustCompile(`(?:` + escapeStringRegexp(replacement) + `){2,}`)
return reg.ReplaceAllString(str, replacement)
}
func stripOuter(input string, substring string) string {
// https://github.com/sindresorhus/strip-outer/blob/master/index.js
substring = escapeStringRegexp(substring)
reg := regexp.MustCompile(`^` + substring + `|` + substring + `$`)
return reg.ReplaceAllString(input, "")
}
|