github.com/josephvusich/fdf@v0.0.0-20230522095411-9326dd32e33f/comparer.go (about) 1 package main 2 3 import ( 4 "fmt" 5 "strconv" 6 "strings" 7 ) 8 9 type comparer interface { 10 AreEqual(a, b *fileRecord) bool 11 HashFunc(*fileRecord) interface{} 12 } 13 14 type propertyComparer struct { 15 getter func(*fileRecord) string 16 ranges [][2]int 17 } 18 19 func (c *propertyComparer) AreEqual(a, b *fileRecord) bool { 20 for _, r := range c.ranges { 21 if getRange(c.getter(a), r[0], r[1]) != getRange(c.getter(b), r[0], r[1]) { 22 return false 23 } 24 } 25 return true 26 } 27 28 func (c *propertyComparer) HashFunc(r *fileRecord) interface{} { 29 return c.getter(r) 30 } 31 32 func getRange(s string, offset, length int) (partial string) { 33 if length == 0 { 34 return "" 35 } 36 defer func() { 37 if r := recover(); r != nil { 38 partial = "" 39 } 40 }() 41 if offset < 0 { 42 offset += len(s) 43 for offset < 0 { 44 offset++ 45 if length > 0 { 46 length-- 47 } 48 } 49 } 50 if length < 0 { 51 length += 1 + len(s) 52 } else { 53 length += offset 54 } 55 partial = s[offset:length] 56 return partial 57 } 58 59 func newComparer(input string, getter func(*fileRecord) string) (*propertyComparer, error) { 60 c := &propertyComparer{ 61 getter: getter, 62 } 63 64 if input == ":" { 65 input = "0:-1" 66 } 67 68 for _, r := range strings.Split(input, ",") { 69 values := strings.Split(r, ":") 70 if len(values) != 2 { 71 return nil, fmt.Errorf("invalid range spec: %s", r) 72 } 73 74 var nums [2]int 75 for i, v := range values { 76 n, err := strconv.Atoi(v) 77 if err != nil { 78 return nil, fmt.Errorf("invalid range spec `%s`: %w", r, err) 79 } 80 nums[i] = n 81 } 82 83 c.ranges = append(c.ranges, nums) 84 } 85 return c, nil 86 }