package compare import ( "regexp" "strings" "gitea.zaclys.com/bvaudour/gob/unidecode" ) var ( digit = regexp.MustCompile(`[^0-9]+|[0-9]+`) ToLower = strings.ToLower ToAscii = unidecode.String ) func isNumber(input uint8) bool { return input >= '0' && input <= '9' } // CompareFunc représente une fonction de comparaison de deux chaînes. type CompareFunc func(string, string) int // TransformFunc représente une fonction de transformation de chaîne. type TransformFunc func(string) string // BuildCompare retourne une fonction de comparaison à partir // d’un ensemble de comparaison à appliquer. func BuildCompare(cmps ...CompareFunc) CompareFunc { return func(e1, e2 string) int { for _, cmp := range cmps { if c := cmp(e1, e2); c != 0 { return c } } return 0 } } // BuildTransform retourne une fonction de transformation à partir // de transformations successives. func BuildTransform(transforms ...TransformFunc) TransformFunc { return func(e string) string { for _, t := range transforms { e = t(e) } return e } } func compareT(e1, e2 string, cb TransformFunc) int { return strings.Compare(cb(e1), cb(e2)) } // Compare compare deux chaînes après transformation. func Compare(e1, e2 string, t ...TransformFunc) int { return compareT(e1, e2, BuildTransform(t...)) } // CompareInsensitive compare deux chaîne sans tenir compte de la casse. func CompareInsensitive(e1, e2 string) int { return Compare(e1, e2, ToLower) } // CompareAscii compare deux chaînes sans tenir compte des accents. func CompareAscii(e1, e2 string) int { return Compare(e1, e2, ToAscii) } // CompareAscii compare deux chaînes sans tenir compte des accents ni de la casse. func CompareAsciiInsensitive(e1, e2 string) int { return Compare(e1, e2, ToAscii, ToLower) } func naturalT(e1, e2 string, cb TransformFunc) int { e1, e2 = cb(e1), cb(e2) sl1 := digit.FindAllString(strings.Replace(e1, " ", "", -1), -1) sl2 := digit.FindAllString(strings.Replace(e2, " ", "", -1), -1) l := len(sl1) if len(sl2) < l { l = len(sl2) } for i, s1 := range sl1[:l] { s2 := sl2[i] // Both parts are equal if s1 == s2 { continue } n1, n2 := isNumber(s1[0]), isNumber(s2[0]) switch { case n1 && n2: s1 = strings.TrimLeft(s1, "0") s2 = strings.TrimLeft(s2, "0") return Compare(s1, s2) case n1: return -1 case n2: return 1 } return Compare(s1, s2) } l = len(e1) if len(e2) < len(e1) { l = len(e2) } // Fall back for cases where space characters have been annihliated by the replacment call // Here we iterate over the unmolsested string and prioritize numbers over for i, b1 := range []byte(e1[:l]) { b2 := e2[i] switch { case isNumber(b1): return -1 case isNumber(b2): return 1 } } return Compare(e1, e2) } // Natural compare des chaînes de façon “naturelle” (ie. en analysant // les nombres éventuels compris dans la chaîne). func Natural(e1, e2 string, t ...TransformFunc) int { return naturalT(e1, e2, BuildTransform(t...)) } // NaturalInsensitive compare des chaînes de manière naturelle sans tenir // compte de la casse. func NaturalInsensitive(e1, e2 string) int { return Natural(e1, e2, ToLower) } // NaturalAscii compare des chaînes de manière naturelle sans tenir // compte des accents. func NaturalAscii(e1, e2 string) int { return Natural(e1, e2, ToAscii) } // NaturalAsciiInsensitive compare des chaînes de manière naturelle sans tenir // compte de la casse ni des accents. func NaturalAsciiInsensitive(e1, e2 string) int { return Natural(e1, e2, ToAscii, ToLower) } // Less transforme une succession de fonctions de comparaison // en <. func Less(cmps ...CompareFunc) func(string, string) bool { return func(e1, e2 string) bool { return BuildCompare(cmps...)(e1, e2) < 0 } } // Equal transforme une succession de fonctions de comparaison // en =. func Equal(cmps ...CompareFunc) func(string, string) bool { return func(e1, e2 string) bool { return BuildCompare(cmps...)(e1, e2) == 0 } }