You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
162 lines
3.7 KiB
162 lines
3.7 KiB
package core
|
|
|
|
import (
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"unicode"
|
|
)
|
|
|
|
// BuildOutputPath computes the full output path for a given input file,
|
|
// applying format, prefix, suffix, replace, case, nospaces and b64 rules.
|
|
func BuildOutputPath(inputPath string, cfg *Config) string {
|
|
relPath, err := filepath.Rel(cfg.InputDir, inputPath)
|
|
if err != nil {
|
|
relPath = filepath.Base(inputPath)
|
|
}
|
|
|
|
base := filepath.Base(relPath)
|
|
dir := filepath.Dir(relPath)
|
|
|
|
ext := filepath.Ext(base)
|
|
stem := base[:len(base)-len(ext)]
|
|
|
|
// Output extension
|
|
outExt := ext
|
|
if cfg.Format != "" {
|
|
outExt = "." + cfg.Format
|
|
switch cfg.Format {
|
|
case "jpeg":
|
|
outExt = ".jpg"
|
|
case "tiff":
|
|
outExt = ".tif"
|
|
}
|
|
}
|
|
|
|
// Stem transformations (order matters)
|
|
stem = ApplyStemReplacements(stem, cfg)
|
|
|
|
if cfg.NoSpacesOn {
|
|
stem = strings.ReplaceAll(stem, " ", cfg.NoSpaces)
|
|
}
|
|
|
|
switch {
|
|
case cfg.Lowercase:
|
|
stem = strings.ToLower(stem)
|
|
case cfg.Uppercase:
|
|
stem = strings.ToUpper(stem)
|
|
}
|
|
|
|
// B64 extension override — always last
|
|
if cfg.B64Enabled {
|
|
outExt = "." + cfg.B64Ext
|
|
}
|
|
|
|
newName := cfg.Prefix + stem + cfg.Suffix + outExt
|
|
|
|
if cfg.KeepStructure {
|
|
return filepath.Join(cfg.OutputDir, dir, newName)
|
|
}
|
|
return filepath.Join(cfg.OutputDir, newName)
|
|
}
|
|
|
|
// ApplyStemReplacements applies -replace / -replacei / -replacep in order.
|
|
func ApplyStemReplacements(stem string, cfg *Config) string {
|
|
if cfg.Replace != "" {
|
|
for _, p := range ParsePairs(cfg.Replace) {
|
|
stem = strings.ReplaceAll(stem, p.From, p.To)
|
|
}
|
|
}
|
|
if cfg.ReplaceI != "" {
|
|
for _, p := range ParsePairs(cfg.ReplaceI) {
|
|
stem = replaceInsensitive(stem, p.From, p.To, false)
|
|
}
|
|
}
|
|
if cfg.ReplaceP != "" {
|
|
for _, p := range ParsePairs(cfg.ReplaceP) {
|
|
stem = replaceInsensitive(stem, p.From, p.To, true)
|
|
}
|
|
}
|
|
return stem
|
|
}
|
|
|
|
// ParsePairs splits "old1/new1,old2/new2" into a slice of ReplacePair.
|
|
func ParsePairs(raw string) []ReplacePair {
|
|
var pairs []ReplacePair
|
|
for _, item := range strings.Split(raw, ",") {
|
|
parts := strings.SplitN(strings.TrimSpace(item), "/", 2)
|
|
if len(parts) == 2 && parts[0] != "" {
|
|
pairs = append(pairs, ReplacePair{From: parts[0], To: parts[1]})
|
|
}
|
|
}
|
|
return pairs
|
|
}
|
|
|
|
func replaceInsensitive(s, from, to string, preserveCase bool) string {
|
|
if from == "" {
|
|
return s
|
|
}
|
|
re := regexp.MustCompile(`(?i)` + regexp.QuoteMeta(from))
|
|
return re.ReplaceAllStringFunc(s, func(match string) string {
|
|
if !preserveCase {
|
|
return to
|
|
}
|
|
return matchCase(match, to)
|
|
})
|
|
}
|
|
|
|
// matchCase projects the capitalisation pattern of src onto dst.
|
|
//
|
|
// "photo" → "image" (all lower → all lower)
|
|
// "Photo" → "Image" (title case → title case)
|
|
// "PHOTO" → "IMAGE" (all upper → all upper)
|
|
// "pHoTo" → "image" (mixed → lower, safe default)
|
|
func matchCase(src, dst string) string {
|
|
srcRunes := []rune(src)
|
|
dstRunes := []rune(dst)
|
|
if len(srcRunes) == 0 || len(dstRunes) == 0 {
|
|
return dst
|
|
}
|
|
|
|
allUpper, allLower := true, true
|
|
for _, r := range srcRunes {
|
|
if unicode.IsLetter(r) {
|
|
if !unicode.IsUpper(r) {
|
|
allUpper = false
|
|
}
|
|
if !unicode.IsLower(r) {
|
|
allLower = false
|
|
}
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case allUpper:
|
|
return strings.ToUpper(dst)
|
|
case allLower:
|
|
return strings.ToLower(dst)
|
|
case unicode.IsUpper(srcRunes[0]):
|
|
out := make([]rune, len(dstRunes))
|
|
for i, r := range dstRunes {
|
|
if i == 0 {
|
|
out[i] = unicode.ToUpper(r)
|
|
} else {
|
|
out[i] = unicode.ToLower(r)
|
|
}
|
|
}
|
|
return string(out)
|
|
default:
|
|
return strings.ToLower(dst)
|
|
}
|
|
}
|
|
|
|
// SanitizeTSKey ensures s is a valid JavaScript identifier.
|
|
func SanitizeTSKey(s string) string {
|
|
re := regexp.MustCompile(`[^\w]`)
|
|
key := re.ReplaceAllString(s, "_")
|
|
if len(key) > 0 && key[0] >= '0' && key[0] <= '9' {
|
|
key = "_" + key
|
|
}
|
|
return key
|
|
}
|