func ValidateTweet(text string) error {
	if text == "" {
		return EmptyError{}
	} else if length := TweetLength(text); length > maxLength {
		return TooLongError(length)
	} else if i := strings.IndexAny(text, invalidChars); i > -1 {
		r, _ := utf8.DecodeRuneInString(text[i:])
		return InvalidCharacterError{Offset: i, Character: r}
	}
	return nil
}
Checks whether a string is a valid tweet. Returns nil if the string
is valid. Otherwise, it returns an error in the following cases:

- The text is too long
- The text is empty
- The text contains invalid characters
package validate
import (
	"fmt"
	"strings"
	"unicode/utf8"
	"github.com/kylemcc/twitter-text-go/extract"
	"golang.org/x/text/unicode/norm"
)
const (
	maxLength           = 140
	shortUrlLength      = 23
	shortHttpsUrlLength = 23
	invalidChars        = "\uFFFE\uFEFF\uFFFF\u202A\u202B\u202C\u202D\u202E"
)
var formC = norm.NFC
type TooLongError int
func (e TooLongError) Error() string {
	return fmt.Sprintf("Length %d exceeds %d characters", int(e), maxLength)
}
type EmptyError struct{}
func (e EmptyError) Error() string {
	return "Tweets may not be empty"
}
type InvalidCharacterError struct {
	Character rune
	Offset    int
}
func (e InvalidCharacterError) Error() string {
	return fmt.Sprintf("Invalid chararcter [%s] found at byte offset %d", string(e.Character), e.Offset)
}
func TweetLength(text string) int {
	length := utf8.RuneCountInString(formC.String(text))
	urls := extract.ExtractUrls(text)
	for _, url := range urls {
		length -= url.Range.Length()
		if strings.HasPrefix(url.Text, "https://") {
			length += shortHttpsUrlLength
		} else {
			length += shortUrlLength
		}
	}
	return length
}
func TweetIsValid(text string) bool {
	err := ValidateTweet(text)
	return err == nil
}