furl - store.go
1 package furl
2
3 import "sync"
4
5 // The Store interface allows for setting a custom storage solution to Furl,
6 // such as a database or keystore.
7 //
8 // The Get method should be used to retireve the URL associated with the passed
9 // key.
10 //
11 // The Tx method should start a thread safe writing context that will be used
12 // for creating new keys. See the Tx interface for more details.
13 type Store interface {
14 Get(key string) (string, bool)
15 Tx(func(tx Tx))
16 }
17
18 // The Tx interface represents a thread safe writing context for generating and
19 // storing keys and their corresponding URLs.
20 //
21 // The Has method may be called multiple times per Store.Tx call.
22 //
23 // The Set method will be called at most one time per Store.Tx call, and will
24 // be used to set the uniquely generated or passed key and its corresponding
25 // URL. The implementation of this method can be used to provide a more
26 // permanent storage for the key:url store.
27 type Tx interface {
28 Has(key string) bool
29 Set(key, url string)
30 }
31
32 // The StoreOption type is used to specify optional params to the NewStore
33 // function call.
34 type StoreOption func(*mapStore)
35
36 // The Data StoreOption is used to set the initial map of keys -> urls. The
37 // passed map should not be accessed by anything other than Furl until Furl is
38 // no longer is use.
39 //
40 // NB: Neither the keys or URLs are checked to be valid.
41 func Data(data map[string]string) StoreOption {
42 return func(m *mapStore) {
43 m.urls = data
44 }
45 }
46
47 // The Save StoreOption is used to set a function that stores the keys and urls
48 // outside of Furl. For example, could be used to write to a file that be later
49 // loaded to provide the data for a future instance of Furl.
50 func Save(save func(key, url string)) StoreOption {
51 return func(m *mapStore) {
52 m.save = save
53 }
54 }
55
56 func noSave(_, _ string) {}
57
58 // NewStore creates a map based implementation of the Store interface, with the
59 // following defaults that can be changed by adding StoreOption params:
60 //
61 // urls: By default, the Store is created with an empty map. This can be changed
62 // with the Data StoreOption.
63 //
64 // save: By default, there is no permanent storage of the key:url map. This can
65 // be changed by the Save StoreOption.
66 func NewStore(opts ...StoreOption) Store {
67 m := &mapStore{
68 save: noSave,
69 }
70 for _, o := range opts {
71 o(m)
72 }
73 if m.urls == nil {
74 m.urls = make(map[string]string)
75 }
76 return m
77 }
78
79 type mapStore struct {
80 mu sync.RWMutex
81 urls map[string]string
82 save func(string, string)
83 }
84
85 func (m *mapStore) Get(key string) (string, bool) {
86 m.mu.RLock()
87 url, ok := m.urls[key]
88 m.mu.RUnlock()
89 return url, ok
90 }
91
92 func (m *mapStore) Tx(fn func(tx Tx)) {
93 m.mu.Lock()
94 fn(m)
95 m.mu.Unlock()
96 }
97
98 func (m *mapStore) Has(key string) bool {
99 _, ok := m.urls[key]
100 return ok
101 }
102
103 func (m *mapStore) Set(key, url string) {
104 m.urls[key] = url
105 m.save(key, url)
106 }
107