package models import ( "bufio" "fmt" "strings" "time" "unicode" ) type Post struct { ID int64 SiteID int64 GUID string Title string Body string Slug string CreatedAt time.Time PublishedAt time.Time } func (p *Post) BestSlug() string { if p.Slug != "" { return p.Slug } bestDateToUse := p.PublishedAt if bestDateToUse.IsZero() { bestDateToUse = p.CreatedAt } slugPath := firstNWords(p.Title, 3, wordForSlug) if slugPath == "" { slugPath = firstNWords(p.Body, 3, wordForSlug) } if slugPath != "" { slugPath = strings.Replace(strings.ToLower(slugPath), " ", "-", -1) } else { slugPath = p.GUID if slugPath == "" { slugPath = bestDateToUse.Format("150405") } } datePart := fmt.Sprintf("%04d/%02d/%02d", bestDateToUse.Year(), bestDateToUse.Month(), bestDateToUse.Day()) return fmt.Sprintf("/%s/%s", datePart, slugPath) } func wordForSlug(word string) string { var sb strings.Builder for _, c := range word { if unicode.IsLetter(c) || unicode.IsNumber(c) { sb.WriteRune(c) } } return sb.String() } func firstNWords(s string, n int, keepWord func(word string) string) string { if n == 0 { return "" } suitableWords := make([]string, 0, n) scnr := bufio.NewScanner(strings.NewReader(s)) scnr.Split(bufio.ScanWords) for scnr.Scan() { word := scnr.Text() if w := keepWord(word); w != "" { suitableWords = append(suitableWords, w) if len(suitableWords) >= n { break } } } return strings.Join(suitableWords, " ") }