a6366eb36f69c2b7d22b69dc854dee16e116ea18
[lightning.git] / hgvs / diff.go
1 package hgvs
2
3 import (
4         "fmt"
5         "time"
6
7         "github.com/sergi/go-diff/diffmatchpatch"
8 )
9
10 type Variant struct {
11         Position int
12         Ref      string
13         New      string
14 }
15
16 func (v *Variant) String() string {
17         switch {
18         case len(v.New) == 0 && len(v.Ref) == 1:
19                 return fmt.Sprintf("%ddel", v.Position)
20         case len(v.New) == 0:
21                 return fmt.Sprintf("%d_%ddel", v.Position, v.Position+len(v.Ref)-1)
22         case len(v.Ref) == 1 && len(v.New) == 1:
23                 return fmt.Sprintf("%d%s>%s", v.Position, v.Ref, v.New)
24         case len(v.Ref) == 0:
25                 return fmt.Sprintf("%d_%dins%s", v.Position-1, v.Position, v.New)
26         case len(v.Ref) == 1 && len(v.New) > 0:
27                 return fmt.Sprintf("%ddelins%s", v.Position, v.New)
28         default:
29                 return fmt.Sprintf("%d_%ddelins%s", v.Position, v.Position+len(v.Ref)-1, v.New)
30         }
31 }
32
33 func Diff(a, b string, timeout time.Duration) ([]Variant, bool) {
34         dmp := diffmatchpatch.New()
35         var deadline time.Time
36         if timeout > 0 {
37                 deadline = time.Now().Add(timeout)
38         }
39         diffs := dmp.DiffBisect(a, b, deadline)
40         timedOut := false
41         if timeout > 0 && time.Now().After(deadline) {
42                 timedOut = true
43         }
44         diffs = cleanup(dmp.DiffCleanupEfficiency(diffs))
45         pos := 1
46         var variants []Variant
47         for i := 0; i < len(diffs); i++ {
48                 switch diffs[i].Type {
49                 case diffmatchpatch.DiffEqual:
50                         pos += len(diffs[i].Text)
51                 case diffmatchpatch.DiffDelete:
52                         if i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffInsert {
53                                 // deletion followed by insertion
54                                 variants = append(variants, Variant{Position: pos, Ref: diffs[i].Text, New: diffs[i+1].Text})
55                                 pos += len(diffs[i].Text)
56                                 i++
57                         } else {
58                                 variants = append(variants, Variant{Position: pos, Ref: diffs[i].Text})
59                                 pos += len(diffs[i].Text)
60                         }
61                 case diffmatchpatch.DiffInsert:
62                         if i+1 < len(diffs) && diffs[i+1].Type == diffmatchpatch.DiffDelete {
63                                 // insertion followed by deletion
64                                 variants = append(variants, Variant{Position: pos, Ref: diffs[i+1].Text, New: diffs[i].Text})
65                                 pos += len(diffs[i+1].Text)
66                                 i++
67                         } else {
68                                 variants = append(variants, Variant{Position: pos, New: diffs[i].Text})
69                         }
70                 }
71         }
72         return variants, timedOut
73 }
74
75 func cleanup(in []diffmatchpatch.Diff) (out []diffmatchpatch.Diff) {
76         for i := 0; i < len(in); i++ {
77                 d := in[i]
78                 for i < len(in)-1 && in[i].Type == in[i+1].Type {
79                         d.Text += in[i+1].Text
80                         i++
81                 }
82                 out = append(out, d)
83         }
84         return
85 }