amfora/bookmarks/bookmarks.go
2021-05-14 18:45:41 -04:00

194 lines
4.4 KiB
Go

package bookmarks
import (
"encoding/base32"
"encoding/xml"
"fmt"
"io/ioutil"
"os"
"sort"
"strings"
"github.com/makeworld-the-better-one/amfora/config"
)
func Init() error {
f, err := os.Open(config.BkmkPath)
if err == nil {
// File exists and could be opened
fi, err := f.Stat()
if err == nil && fi.Size() > 0 {
// File is not empty
xbelBytes, err := ioutil.ReadAll(f)
f.Close()
if err != nil {
return fmt.Errorf("read bookmarks.xml error: %w", err)
}
err = xml.Unmarshal(xbelBytes, &data)
if err != nil {
return fmt.Errorf("bookmarks.xml is corrupted: %w", err)
}
}
f.Close()
} else if !os.IsNotExist(err) {
// There's an error opening the file, but it's not bc is doesn't exist
return fmt.Errorf("open bookmarks.xml error: %w", err)
}
if data.Bookmarks == nil {
data.Bookmarks = make([]*xbelBookmark, 0)
data.Version = xbelVersion
}
if config.BkmkStore != nil {
// There's still bookmarks stored in the old format
// Add them and delete the file
names, urls := oldBookmarks()
for i := range names {
data.Bookmarks = append(data.Bookmarks, &xbelBookmark{
URL: urls[i],
Name: names[i],
})
}
err := writeXbel()
if err != nil {
return fmt.Errorf("error saving old bookmarks into new format: %w", err)
}
err = os.Remove(config.OldBkmkPath)
if err != nil {
//nolint:goerr113
return fmt.Errorf(
"couldn't delete old bookmarks file (%s), you must delete it yourself to prevent duplicate bookmarks: %w",
config.OldBkmkPath,
err,
)
}
config.BkmkStore = nil
}
return nil
}
// oldBookmarks returns a slice of names and a slice of URLs of the
// bookmarks in config.BkmkStore.
func oldBookmarks() ([]string, []string) {
bkmksMap, ok := config.BkmkStore.AllSettings()["bookmarks"].(map[string]interface{})
if !ok {
// No bookmarks stored yet, return empty map
return []string{}, []string{}
}
names := make([]string, 0, len(bkmksMap))
urls := make([]string, 0, len(bkmksMap))
for b32Url, name := range bkmksMap {
if n, ok := name.(string); n == "" || !ok {
// name is not a string, or it's empty - ignore
// Likely means it is a removed bookmark
continue
}
url, err := base32.StdEncoding.DecodeString(strings.ToUpper(b32Url))
if err != nil {
// This would only happen if a user messed around with the bookmarks file
continue
}
names = append(names, name.(string))
urls = append(urls, string(url))
}
return names, urls
}
func writeXbel() error {
xbelBytes, err := xml.MarshalIndent(&data, "", " ")
if err != nil {
return err
}
xbelBytes = append(xbelHeader, xbelBytes...)
err = ioutil.WriteFile(config.BkmkPath, xbelBytes, 0666)
if err != nil {
return err
}
return nil
}
// Change the name of the bookmark at the provided URL.
func Change(url, name string) {
for _, bkmk := range data.Bookmarks {
if bkmk.URL == url {
bkmk.Name = name
writeXbel() //nolint:errcheck
return
}
}
}
// Add will add a new bookmark.
func Add(url, name string) {
data.Bookmarks = append(data.Bookmarks, &xbelBookmark{
URL: url,
Name: name,
})
writeXbel() //nolint:errcheck
}
// Get returns the NAME of the bookmark, given the URL.
// It also returns a bool indicating whether it exists.
func Get(url string) (string, bool) {
for _, bkmk := range data.Bookmarks {
if bkmk.URL == url {
return bkmk.Name, true
}
}
return "", false
}
func Remove(url string) {
for i, bkmk := range data.Bookmarks {
if bkmk.URL == url {
data.Bookmarks[i] = data.Bookmarks[len(data.Bookmarks)-1]
data.Bookmarks = data.Bookmarks[:len(data.Bookmarks)-1]
writeXbel() //nolint:errcheck
return
}
}
}
// bkmkNameSlice is used for sorting bookmarks alphabetically.
// It implements sort.Interface.
type bkmkNameSlice struct {
names []string
urls []string
}
func (b *bkmkNameSlice) Len() int {
return len(b.names)
}
func (b *bkmkNameSlice) Less(i, j int) bool {
return b.names[i] < b.names[j]
}
func (b *bkmkNameSlice) Swap(i, j int) {
b.names[i], b.names[j] = b.names[j], b.names[i]
b.urls[i], b.urls[j] = b.urls[j], b.urls[i]
}
// All returns all the bookmarks, as two arrays, one for names and one for URLs.
// They are sorted alphabetically.
func All() ([]string, []string) {
b := bkmkNameSlice{
make([]string, len(data.Bookmarks)),
make([]string, len(data.Bookmarks)),
}
for i, bkmk := range data.Bookmarks {
b.names[i] = bkmk.Name
b.urls[i] = bkmk.URL
}
sort.Sort(&b)
return b.names, b.urls
}