@@ -2,55 +2,88 @@ package bookmarks
2
2
3
3
import (
4
4
"encoding/base32"
5
+ "encoding/xml"
6
+ "fmt"
7
+ "io/ioutil"
8
+ "os"
5
9
"sort"
6
10
"strings"
7
11
8
12
"github.com/makeworld-the-better-one/amfora/config"
9
13
)
10
14
11
- var bkmkStore = config .BkmkStore
15
+ func Init () error {
16
+ f , err := os .Open (config .BkmkPath )
17
+ if err == nil {
18
+ // File exists and could be opened
12
19
13
- // bkmkKey returns the viper key for the given bookmark URL.
14
- // Note that URLs are the keys, NOT the bookmark name.
15
- func bkmkKey (url string ) string {
16
- // Keys are base32 encoded URLs to prevent any special chars like periods from being used
17
- return "bookmarks." + base32 .StdEncoding .EncodeToString ([]byte (url ))
18
- }
20
+ fi , err := f .Stat ()
21
+ if err == nil && fi .Size () > 0 {
22
+ // File is not empty
19
23
20
- func Set (url , name string ) {
21
- bkmkStore .Set (bkmkKey (url ), name )
22
- bkmkStore .WriteConfig () //nolint:errcheck
23
- }
24
+ xbelBytes , err := ioutil .ReadAll (f )
25
+ f .Close ()
26
+ if err != nil {
27
+ return fmt .Errorf ("read bookmarks.xml error: %w" , err )
28
+ }
29
+ err = xml .Unmarshal (xbelBytes , & data )
30
+ if err != nil {
31
+ return fmt .Errorf ("bookmarks.xml is corrupted: %w" , err )
32
+ }
33
+ }
34
+ f .Close ()
35
+ } else if ! os .IsNotExist (err ) {
36
+ // There's an error opening the file, but it's not bc is doesn't exist
37
+ return fmt .Errorf ("open bookmarks.xml error: %w" , err )
38
+ }
24
39
25
- // Get returns the NAME of the bookmark, given the URL.
26
- // It also returns a bool indicating whether it exists.
27
- func Get (url string ) (string , bool ) {
28
- name := bkmkStore .GetString (bkmkKey (url ))
29
- return name , name != ""
30
- }
40
+ if data .Bookmarks == nil {
41
+ data .Bookmarks = make ([]* xbelBookmark , 0 )
42
+ data .Version = xbelVersion
43
+ }
31
44
32
- func Remove (url string ) {
33
- // XXX: Viper can't actually delete keys, which means the bookmarks file might get clouded
34
- // with non-entries over time.
35
- bkmkStore .Set (bkmkKey (url ), "" )
36
- bkmkStore .WriteConfig () //nolint:errcheck
37
- }
45
+ if config .BkmkStore != nil {
46
+ // There's still bookmarks stored in the old format
47
+ // Add them and delete the file
38
48
39
- // All returns all the bookmarks in a map of URLs to names.
40
- // It also returns a slice of map keys, sorted so that the map *values*
41
- // are in alphabetical order, with case ignored.
42
- func All () (map [string ]string , []string ) {
43
- bkmks := make (map [string ]string )
49
+ names , urls := oldBookmarks ()
50
+ for i := range names {
51
+ data .Bookmarks = append (data .Bookmarks , & xbelBookmark {
52
+ URL : urls [i ],
53
+ Name : names [i ],
54
+ })
55
+ }
56
+
57
+ err := writeXbel ()
58
+ if err != nil {
59
+ return fmt .Errorf ("error saving old bookmarks into new format: %w" , err )
60
+ }
61
+
62
+ err = os .Remove (config .OldBkmkPath )
63
+ if err != nil {
64
+ return fmt .Errorf (
65
+ "couldn't delete old bookmarks file (%s), you must delete it yourself to prevent duplicate bookmarks: %w" ,
66
+ config .OldBkmkPath ,
67
+ err ,
68
+ )
69
+ }
70
+ config .BkmkStore = nil
71
+ }
72
+
73
+ return nil
74
+ }
44
75
45
- bkmksMap , ok := bkmkStore .AllSettings ()["bookmarks" ].(map [string ]interface {})
76
+ // oldBookmarks returns a slice of names and a slice of URLs of the
77
+ // bookmarks in config.BkmkStore.
78
+ func oldBookmarks () ([]string , []string ) {
79
+ bkmksMap , ok := config .BkmkStore .AllSettings ()["bookmarks" ].(map [string ]interface {})
46
80
if ! ok {
47
81
// No bookmarks stored yet, return empty map
48
- return bkmks , []string {}
82
+ return [] string {} , []string {}
49
83
}
50
84
51
- inverted := make (map [string ]string ) // Holds inverted map, name->URL
52
- names := make ([]string , 0 , len (bkmksMap )) // Holds bookmark names, for sorting
53
- keys := make ([]string , 0 , len (bkmksMap )) // Final sorted keys (URLs), for returning at the end
85
+ names := make ([]string , 0 , len (bkmksMap ))
86
+ urls := make ([]string , 0 , len (bkmksMap ))
54
87
55
88
for b32Url , name := range bkmksMap {
56
89
if n , ok := name .(string ); n == "" || ! ok {
@@ -63,15 +96,89 @@ func All() (map[string]string, []string) {
63
96
// This would only happen if a user messed around with the bookmarks file
64
97
continue
65
98
}
66
- bkmks [string (url )] = name .(string )
67
- inverted [name .(string )] = string (url )
68
99
names = append (names , name .(string ))
100
+ urls = append (urls , string (url ))
101
+ }
102
+ return names , urls
103
+ }
104
+
105
+ func writeXbel () error {
106
+ xbelBytes , err := xml .MarshalIndent (& data , "" , " " )
107
+ if err != nil {
108
+ return err
109
+ }
110
+
111
+ xbelBytes = append (xbelHeader , xbelBytes ... )
112
+ err = ioutil .WriteFile (config .BkmkPath , xbelBytes , 0666 )
113
+ if err != nil {
114
+ return err
115
+ }
116
+ return nil
117
+ }
118
+
119
+ // Change the name of the bookmark at the provided URL.
120
+ func Change (url , name string ) {
121
+ for _ , bkmk := range data .Bookmarks {
122
+ if bkmk .URL == url {
123
+ bkmk .Name = name
124
+ writeXbel () //nolint:errcheck
125
+ return
126
+ }
69
127
}
128
+ }
129
+
130
+ // Add will add a new bookmark.
131
+ func Add (url , name string ) {
132
+ data .Bookmarks = append (data .Bookmarks , & xbelBookmark {
133
+ URL : url ,
134
+ Name : name ,
135
+ })
136
+ writeXbel () //nolint:errcheck
137
+ }
138
+
139
+ // Get returns the NAME of the bookmark, given the URL.
140
+ // It also returns a bool indicating whether it exists.
141
+ func Get (url string ) (string , bool ) {
142
+ for _ , bkmk := range data .Bookmarks {
143
+ if bkmk .URL == url {
144
+ return bkmk .Name , true
145
+ }
146
+ }
147
+ return "" , false
148
+ }
149
+
150
+ func Remove (url string ) {
151
+ for i , bkmk := range data .Bookmarks {
152
+ if bkmk .URL == url {
153
+ data .Bookmarks [i ] = data .Bookmarks [len (data .Bookmarks )- 1 ]
154
+ data .Bookmarks = data .Bookmarks [:len (data .Bookmarks )- 1 ]
155
+ writeXbel () //nolint:errcheck
156
+ return
157
+ }
158
+ }
159
+ }
160
+
161
+ // All returns all the bookmarks in a map of URLs to names.
162
+ // It also returns a slice of map keys, sorted so that the map *values*
163
+ // are in alphabetical order, with case ignored.
164
+ func All () (map [string ]string , []string ) {
165
+ bkmksMap := make (map [string ]string )
166
+
167
+ inverted := make (map [string ]string ) // Holds inverted map, name->URL
168
+ names := make ([]string , len (data .Bookmarks )) // Holds bookmark names, for sorting
169
+ keys := make ([]string , len (data .Bookmarks )) // Final sorted keys (URLs), for returning at the end
170
+
171
+ for i , bkmk := range data .Bookmarks {
172
+ bkmksMap [bkmk .URL ] = bkmk .Name
173
+ inverted [bkmk .Name ] = bkmk .URL
174
+ names [i ] = bkmk .Name
175
+ }
176
+
70
177
// Sort, then turn back into URL keys
71
178
sort .Strings (names )
72
- for _ , name := range names {
73
- keys = append ( keys , inverted [name ])
179
+ for i , name := range names {
180
+ keys [ i ] = inverted [name ]
74
181
}
75
182
76
- return bkmks , keys
183
+ return bkmksMap , keys
77
184
}
0 commit comments