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

7 months ago
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
}

Powered by TurnKey Linux.