-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathissues.go
More file actions
141 lines (115 loc) · 3.2 KB
/
issues.go
File metadata and controls
141 lines (115 loc) · 3.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
package main
import (
"encoding/json"
"io/ioutil"
"sort"
"github.com/dnote/doctor/semver"
"github.com/dnote/fileutils"
"github.com/pkg/errors"
)
type issue struct {
name string
minVersion *semver.Version
maxVersion *semver.Version
desc string
fix func(Ctx) (bool, error)
}
var (
v0_4_0 = semver.Version{Major: 0, Minor: 4, Patch: 0}
)
var i1 = issue{
name: "duplicate-json-note-uuid",
minVersion: &v0_4_0,
maxVersion: nil,
desc: `Under 0.4.4, some notes have duplicate uuids if they were edited.
Duplicates have the same added_on but successively incrementing edited_on values.
Therefore the fix is to simply take the note with the latest edited_on value and discard the outdated ones.
The cause may be related to sync but is unknown. But will no longer possible in v0.4.5 and above because SQLite imposes uniqueness constraint on note uuid.`,
fix: func(ctx Ctx) (bool, error) {
// if a legacy json dnote is not found, do not proceed
notePath := getJSONDnotePath(ctx)
if !fileutils.Exists(notePath) {
return false, nil
}
diagnosed := false
rawDnote, err := readJSONDnote(ctx)
if err != nil {
return false, errors.Wrap(err, "getting dnote")
}
var dnote dnoteV0_4_0V0_4_4
if err := json.Unmarshal(rawDnote, &dnote); err != nil {
return false, errors.Wrap(err, "unmarshalling notes")
}
type tmpNote struct {
content noteV0_4_0V0_4_4
bookName string
}
// build a flat slice of notes and mark notes by bookname
tmpNotes := []tmpNote{}
for _, book := range dnote {
for _, note := range book.Notes {
tmp := tmpNote{
content: note,
bookName: book.Name,
}
tmpNotes = append(tmpNotes, tmp)
}
}
// sort by uuid and then edited_on
sort.Slice(tmpNotes, func(i, j int) bool {
ni := tmpNotes[i]
nj := tmpNotes[j]
if ni.content.UUID == nj.content.UUID {
return ni.content.EditedOn <= nj.content.EditedOn
}
return ni.content.UUID <= nj.content.UUID
})
// remove duplicates
deduped := []tmpNote{}
for i := 0; i < len(tmpNotes); i++ {
current := tmpNotes[i]
if i == len(tmpNotes)-1 {
deduped = append(deduped, current)
break
}
next := tmpNotes[i+1]
if current.content.UUID != next.content.UUID {
deduped = append(deduped, current)
} else {
diagnosed = true
}
}
// put notes back to dnote structure
dnote = dnoteV0_4_0V0_4_4{}
for _, tmpNote := range deduped {
bookName := tmpNote.bookName
_, ok := dnote[bookName]
var notes []noteV0_4_0V0_4_4
if ok {
notes = append(dnote[bookName].Notes, tmpNote.content)
} else {
notes = []noteV0_4_0V0_4_4{tmpNote.content}
}
dnote[bookName] = bookV0_4_0V0_4_4{
Name: bookName,
Notes: notes,
}
}
d, err := json.MarshalIndent(dnote, "", " ")
if err != nil {
return diagnosed, errors.Wrap(err, "marhsalling deduplicated dnote")
}
err = ioutil.WriteFile(notePath, d, 0644)
if err != nil {
return diagnosed, errors.Wrap(err, "writing dnote file")
}
return diagnosed, nil
},
}
func (i issue) relevant(version semver.Version) bool {
return (i.minVersion == nil || version.Gte(*i.minVersion)) &&
(i.maxVersion == nil || version.Lte(*i.maxVersion))
}
var issues = []issue{
i1,
}