mirror of
https://github.com/coder/coder.git
synced 2026-06-02 20:48:20 +00:00
17ddee05e5
- Update go.mod to use Go 1.24.1 - Update GitHub Actions setup-go action to use Go 1.24.1 - Fix linting issues with golangci-lint by: - Updating to golangci-lint v1.57.1 (more compatible with Go 1.24.1) 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com> --------- Co-authored-by: Claude <claude@anthropic.com>
103 lines
2.5 KiB
Go
103 lines
2.5 KiB
Go
package levenshtein
|
|
|
|
import (
|
|
"golang.org/x/exp/constraints"
|
|
"golang.org/x/xerrors"
|
|
)
|
|
|
|
// Matches returns the closest matches to the needle from the haystack.
|
|
// The maxDistance parameter is the maximum Matches distance to consider.
|
|
// If no matches are found, an empty slice is returned.
|
|
func Matches(needle string, maxDistance int, haystack ...string) (matches []string) {
|
|
for _, hay := range haystack {
|
|
if d, err := Distance(needle, hay, maxDistance); err == nil && d <= maxDistance {
|
|
matches = append(matches, hay)
|
|
}
|
|
}
|
|
|
|
return matches
|
|
}
|
|
|
|
var ErrMaxDist = xerrors.New("levenshtein: maxDist exceeded")
|
|
|
|
// Distance returns the edit distance between a and b using the
|
|
// Wagner-Fischer algorithm.
|
|
// A and B must be less than 255 characters long.
|
|
// maxDist is the maximum distance to consider.
|
|
// A value of -1 for maxDist means no maximum.
|
|
func Distance(a, b string, maxDist int) (int, error) {
|
|
if len(a) > 255 {
|
|
return 0, xerrors.Errorf("levenshtein: a must be less than 255 characters long")
|
|
}
|
|
if len(b) > 255 {
|
|
return 0, xerrors.Errorf("levenshtein: b must be less than 255 characters long")
|
|
}
|
|
// #nosec G115 - Safe conversion since we've checked that len(a) < 255
|
|
m := uint8(len(a))
|
|
// #nosec G115 - Safe conversion since we've checked that len(b) < 255
|
|
n := uint8(len(b))
|
|
|
|
// Special cases for empty strings
|
|
if m == 0 {
|
|
return int(n), nil
|
|
}
|
|
if n == 0 {
|
|
return int(m), nil
|
|
}
|
|
|
|
// Allocate a matrix of size m+1 * n+1
|
|
d := make([][]uint8, 0)
|
|
var i, j uint8
|
|
for i = 0; i < m+1; i++ {
|
|
di := make([]uint8, n+1)
|
|
d = append(d, di)
|
|
}
|
|
|
|
// Source prefixes
|
|
for i = 1; i < m+1; i++ {
|
|
d[i][0] = i
|
|
}
|
|
|
|
// Target prefixes
|
|
for j = 1; j < n; j++ {
|
|
d[0][j] = j // nolint:gosec // this cannot overflow
|
|
}
|
|
|
|
// Compute the distance
|
|
for j = 0; j < n; j++ {
|
|
for i = 0; i < m; i++ {
|
|
var subCost uint8
|
|
// Equal
|
|
if a[i] != b[j] {
|
|
subCost = 1
|
|
}
|
|
// Don't forget: matrix is +1 size
|
|
d[i+1][j+1] = minOf(
|
|
d[i][j+1]+1, // deletion
|
|
d[i+1][j]+1, // insertion
|
|
d[i][j]+subCost, // substitution
|
|
)
|
|
// check maxDist on the diagonal
|
|
// #nosec G115 - Safe conversion as maxDist is expected to be small for edit distances
|
|
if maxDist > -1 && i == j && d[i+1][j+1] > uint8(maxDist) {
|
|
return int(d[i+1][j+1]), ErrMaxDist
|
|
}
|
|
}
|
|
}
|
|
|
|
return int(d[m][n]), nil
|
|
}
|
|
|
|
func minOf[T constraints.Ordered](ts ...T) T {
|
|
if len(ts) == 0 {
|
|
panic("minOf: no arguments")
|
|
}
|
|
m := ts[0]
|
|
for _, t := range ts[1:] {
|
|
if t < m {
|
|
m = t
|
|
}
|
|
}
|
|
return m
|
|
}
|