diff --git a/category/tags.go b/category/tags.go index 9b99248..a3a092a 100644 --- a/category/tags.go +++ b/category/tags.go @@ -1,26 +1,47 @@ package category import ( "strings" "unicode" ) // titleFromHashtag generates an all-lowercase title, with spaces inserted based on initial capitalization -- e.g. // "MyWordyTag" becomes "my wordy tag". func titleFromHashtag(hashtag string) string { var t strings.Builder var prev rune for i, c := range hashtag { if unicode.IsUpper(c) { if i > 0 && !unicode.IsUpper(prev) { // Insert space if previous rune wasn't also uppercase (e.g. an abbreviation) t.WriteRune(' ') } t.WriteRune(unicode.ToLower(c)) } else { t.WriteRune(c) } prev = c } return t.String() } + +// HashtagFromTitle generates a valid single-word, camelCase hashtag from a title (which might include spaces, +// punctuation, etc.). +func HashtagFromTitle(title string) string { + var t strings.Builder + var prev rune + for _, c := range title { + if !unicode.IsLetter(c) && !unicode.IsNumber(c) { + prev = c + continue + } + if unicode.IsSpace(prev) { + // Uppercase next word + t.WriteRune(unicode.ToUpper(c)) + } else { + t.WriteRune(c) + } + prev = c + } + return t.String() +} diff --git a/category/tags_test.go b/category/tags_test.go index 9889cfc..92a45c1 100644 --- a/category/tags_test.go +++ b/category/tags_test.go @@ -1,31 +1,60 @@ package category import "testing" func TestTitleFromHashtag(t *testing.T) { tests := []struct { name string hashtag string expTitle string }{ {"proper noun", "Jane", "jane"}, {"full name", "JaneDoe", "jane doe"}, {"us words", "unitedStates", "united states"}, {"usa", "USA", "usa"}, {"us monoword", "unitedstates", "unitedstates"}, {"100dto", "100DaysToOffload", "100 days to offload"}, {"iphone", "iPhone", "iphone"}, {"ilike", "iLikeThis", "i like this"}, {"abird", "aBird", "a bird"}, {"all caps", "URGENT", "urgent"}, {"smartphone", "スマートフォン", "スマートフォン"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { res := titleFromHashtag(test.hashtag) if res != test.expTitle { t.Fatalf("#%s: got '%s' expected '%s'", test.hashtag, res, test.expTitle) } }) } } + +func TestHashtagFromTitle(t *testing.T) { + tests := []struct { + name string + title string + expHashtag string + }{ + {"proper noun", "Jane", "Jane"}, + {"full name", "Jane Doe", "JaneDoe"}, + {"us upper words", "United States", "UnitedStates"}, + {"us lower words", "united states", "unitedStates"}, + {"usa", "USA", "USA"}, + {"100dto", "100 Days To Offload", "100DaysToOffload"}, + {"iphone", "iPhone", "iPhone"}, + {"ilike", "I like this", "ILikeThis"}, + {"abird", "a Bird", "aBird"}, + {"all caps", "URGENT", "URGENT"}, + {"punctuation", "John’s Stories", "JohnsStories"}, + {"smartphone", "スマートフォン", "スマートフォン"}, + } + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + res := HashtagFromTitle(test.title) + if res != test.expHashtag { + t.Fatalf("%s: got '%s' expected '%s'", test.title, res, test.expHashtag) + } + }) + } +}