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) }