File: filenamify.go

package info (click to toggle)
golang-github-flytam-filenamify 1.1.1-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 84 kB
  • sloc: makefile: 2
file content (129 lines) | stat: -rw-r--r-- 3,227 bytes parent folder | download
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, "")
}