63 lines
1.5 KiB
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)
|
|
}
|