This repository was archived by the owner on Aug 23, 2018. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 54
Expand file tree
/
Copy pathdata.go
More file actions
142 lines (127 loc) · 3.06 KB
/
data.go
File metadata and controls
142 lines (127 loc) · 3.06 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
142
package main
import (
"encoding/xml"
"errors"
"fmt"
"sync"
)
var (
ErrAlreadyExists = errors.New("album already exists")
)
// The DB interface defines methods to manipulate the albums.
type DB interface {
Get(id int) *Album
GetAll() []*Album
Find(band, title string, year int) []*Album
Add(a *Album) (int, error)
Update(a *Album) error
Delete(id int)
}
// Thread-safe in-memory map of albums.
type albumsDB struct {
sync.RWMutex
m map[int]*Album
seq int
}
// The one and only database instance.
var db DB
func init() {
db = &albumsDB{
m: make(map[int]*Album),
}
// Fill the database
db.Add(&Album{Id: 1, Band: "Slayer", Title: "Reign In Blood", Year: 1986})
db.Add(&Album{Id: 2, Band: "Slayer", Title: "Seasons In The Abyss", Year: 1990})
db.Add(&Album{Id: 3, Band: "Bruce Springsteen", Title: "Born To Run", Year: 1975})
}
// GetAll returns all albums from the database.
func (db *albumsDB) GetAll() []*Album {
db.RLock()
defer db.RUnlock()
if len(db.m) == 0 {
return nil
}
ar := make([]*Album, len(db.m))
i := 0
for _, v := range db.m {
ar[i] = v
i++
}
return ar
}
// Find returns albums that match the search criteria.
func (db *albumsDB) Find(band, title string, year int) []*Album {
db.RLock()
defer db.RUnlock()
var res []*Album
for _, v := range db.m {
if v.Band == band || band == "" {
if v.Title == title || title == "" {
if v.Year == year || year == 0 {
res = append(res, v)
}
}
}
}
return res
}
// Get returns the album identified by the id, or nil.
func (db *albumsDB) Get(id int) *Album {
db.RLock()
defer db.RUnlock()
return db.m[id]
}
// Add creates a new album and returns its id, or an error.
func (db *albumsDB) Add(a *Album) (int, error) {
db.Lock()
defer db.Unlock()
// Return an error if band-title already exists
if !db.isUnique(a) {
return 0, ErrAlreadyExists
}
// Get the unique ID
db.seq++
a.Id = db.seq
// Store
db.m[a.Id] = a
return a.Id, nil
}
// Update changes the album identified by the id. It returns an error if the
// updated album is a duplicate.
func (db *albumsDB) Update(a *Album) error {
db.Lock()
defer db.Unlock()
if !db.isUnique(a) {
return ErrAlreadyExists
}
db.m[a.Id] = a
return nil
}
// Delete removes the album identified by the id from the database. It is a no-op
// if the id does not exist.
func (db *albumsDB) Delete(id int) {
db.Lock()
defer db.Unlock()
delete(db.m, id)
}
// Checks if the album already exists in the database, based on the Band and Title
// fields.
func (db *albumsDB) isUnique(a *Album) bool {
for _, v := range db.m {
if v.Band == a.Band && v.Title == a.Title && v.Id != a.Id {
return false
}
}
return true
}
// The Album data structure, serializable in JSON, XML and text using the Stringer interface.
type Album struct {
XMLName xml.Name `json:"-" xml:"album"`
Id int `json:"id" xml:"id,attr"`
Band string `json:"band" xml:"band"`
Title string `json:"title" xml:"title"`
Year int `json:"year" xml:"year"`
}
func (a *Album) String() string {
return fmt.Sprintf("%s - %s (%d)", a.Band, a.Title, a.Year)
}