package naming import ( "fmt" "regexp" "strconv" "strings" "unicode" ) type Config struct { RestrictCharacters bool TruncateTo int } var tokenRe = regexp.MustCompile(`\{([a-z_]+)(?::0?(\d+))?\}`) var invalidPathRe = regexp.MustCompile(`[<>:"/\\|?*\x00-\x1F]`) func FormatTemplate(template string, values map[string]string) string { return tokenRe.ReplaceAllStringFunc(template, func(m string) string { groups := tokenRe.FindStringSubmatch(m) if len(groups) < 2 { return m } key := groups[1] val := values[key] if len(groups) >= 3 && groups[2] != "" { if n, err := strconv.Atoi(val); err == nil { if width, widthErr := strconv.Atoi(groups[2]); widthErr == nil { return fmt.Sprintf("%0*d", width, n) } } } return val }) } func CleanName(in string, cfg Config) string { s := strings.TrimSpace(in) s = invalidPathRe.ReplaceAllString(s, "_") if cfg.RestrictCharacters { r := make([]rune, 0, len(s)) for _, ch := range s { if ch >= 32 && ch <= 126 { r = append(r, ch) } } s = string(r) } if cfg.TruncateTo > 0 { runes := []rune(s) if len(runes) > cfg.TruncateTo { s = string(runes[:cfg.TruncateTo]) } } s = strings.TrimSpace(s) if s == "" { return "Unknown" } return s } func YearFromDate(date string) string { if len(date) >= 4 { prefix := date[:4] for _, ch := range prefix { if !unicode.IsDigit(ch) { return "Unknown" } } return prefix } return "Unknown" }