verstak/internal/core/templates/safename.go

63 lines
1.5 KiB
Go

package templates
import (
"fmt"
"os"
"path/filepath"
"strings"
"unicode"
)
// SafeDisplayNameToPathSegment converts a user-provided title to a safe
// filesystem path segment. It preserves human readability (Cyrillic, spaces)
// but removes or replaces characters illegal in filenames.
//
// If the resulting path would collide with an existing entry, callers should
// append a numeric suffix like " (2)".
func SafeDisplayNameToPathSegment(title string) string {
title = strings.TrimSpace(title)
if title == "" {
return "Без названия"
}
var result strings.Builder
for _, r := range title {
switch {
case r == '/' || r == '\\':
result.WriteRune('_')
case r == ':' || r == '*' || r == '?' || r == '"' || r == '<' || r == '>' || r == '|':
result.WriteRune(' ')
case unicode.IsControl(r):
case r == '.' && result.Len() == 0:
result.WriteRune('_')
default:
result.WriteRune(r)
}
}
seg := strings.TrimSpace(result.String())
if seg == "" {
seg = "Без названия"
}
if len(seg) > 200 {
seg = seg[:200]
}
return seg
}
// UniquePath returns a unique path by appending a numeric suffix if needed.
func UniquePath(basePath string) string {
if _, err := os.Stat(basePath); os.IsNotExist(err) {
return basePath
}
ext := filepath.Ext(basePath)
stem := strings.TrimSuffix(basePath, ext)
for i := 2; i < 1000; i++ {
candidate := fmt.Sprintf("%s (%d)%s", stem, i, ext)
if _, err := os.Stat(candidate); os.IsNotExist(err) {
return candidate
}
}
return fmt.Sprintf("%s_%d%s", stem, 1000, ext)
}