/
item.go
193 lines (162 loc) · 4.4 KB
/
item.go
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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package inventory
import (
"fmt"
"image"
"image/jpeg"
"io"
"io/ioutil"
"os"
"path/filepath"
"sort"
"time"
"github.com/disintegration/imaging"
"github.com/edwvee/exiffix"
"gopkg.in/yaml.v2"
)
const (
itemYAML = "info.yaml"
itemPic = "picture.jpg"
itemLocPic = "location.jpg"
retCODE = "RETURN_CODE"
)
// Item is the item in the inventory.
type Item struct {
ID string `yaml:"id"`
SKU string `yaml:"sku"`
Name string `yaml:"name"`
Type string `yaml:"itemtype"`
Value string `yaml:"value"`
Size string `yaml:"size"`
Quantity string `yaml:"quantity"`
Price string `yaml:"price"`
Location string `yaml:"location"`
Updated time.Time `yaml:"update"`
}
// Delete deletes the item from the disk
func (i *Item) Delete() error {
err := os.RemoveAll(i.path(""))
if err != nil {
return fmt.Errorf("inventory: could not delete item directory: %w", err)
}
return nil
}
// Update updates the information of the item on disk.
func (i *Item) Update() error {
i.Updated = time.Now()
err := os.MkdirAll(i.path(""), os.ModePerm)
if err != nil {
return fmt.Errorf("inventory: could not create item directory: %w", err)
}
data, err := yaml.Marshal(i)
if err != nil {
return fmt.Errorf("inventory: could not marshal yaml file: %w", err)
}
ioutil.WriteFile(i.path(itemYAML), data, 0644)
return nil
}
// SetPicture sets the thumbnail picture of the item. To save space, the image
// is resized within 1000x1000 pixels and encoded as jpeg.
func (i *Item) SetPicture(r io.ReadSeeker) error {
return parseImg(r, 1000, i.path(itemPic))
}
// SetLocationPicture sets the thumbnail picture of the item. To save space, the image
// is resized within 1000x1000 pixels and encoded as jpeg.
func (i *Item) SetLocationPicture(r io.ReadSeeker) error {
return parseImg(r, 1500, i.path(itemLocPic))
}
func parseImg(r io.ReadSeeker, size int, filepath string) error {
data, _, err := exiffix.Decode(r)
if err != nil {
return fmt.Errorf("inventory: could not decode image: %w", err)
}
img := imaging.Thumbnail(data, size, size, imaging.Lanczos)
file, err := os.Create(filepath)
if err != nil {
return fmt.Errorf("inventory: could not create image file: %w", err)
}
defer file.Close()
err = jpeg.Encode(file, img, nil)
if err != nil {
return fmt.Errorf("inventory: could not encode image file: %w", err)
}
return nil
}
// Picture returns the picture associated with the item.
func (i *Item) Picture() (image.Image, error) {
return getImg(i.path(itemPic))
}
// LocationPicture returns the picture of the location associated with the item.
func (i *Item) LocationPicture() (image.Image, error) {
return getImg(i.path(itemLocPic))
}
func getImg(filepath string) (image.Image, error) {
file, err := os.Open(filepath)
if err != nil {
return nil, fmt.Errorf("inventory: could not open image file: %w", err)
}
defer file.Close()
img, err := jpeg.Decode(file)
if err != nil {
return nil, fmt.Errorf("inventory: could not decode image: %w", err)
}
return img, nil
}
func (i *Item) path(filename string) string {
if filename == "" {
return filepath.Join(getDir(), i.ID)
}
return filepath.Join(getDir(), i.ID, filename)
}
// by is the type of the `Less` function.
type by func(i1, i2 *Item) bool
// sorter sorts a list of items by a specific variable.
func (b by) sorter(items []*Item, reversed bool) {
is := &itemSorter{
items: items,
by: b,
}
if reversed {
sort.Sort(sort.Reverse(is))
} else {
sort.Sort(is)
}
}
type itemSorter struct {
items []*Item
by func(i1, i2 *Item) bool
}
func (is *itemSorter) Len() int {
return len(is.items)
}
func (is *itemSorter) Swap(i, j int) {
is.items[i], is.items[j] = is.items[j], is.items[i]
}
func (is *itemSorter) Less(i, j int) bool {
return is.by(is.items[i], is.items[j])
}
type sortBy byte
const (
// ByName sorts items by name.
ByName sortBy = (1 << iota)
// ByDate sorts items by update date.
ByDate
// ByPrice sorts items by price.
ByPrice
)
// Sort sorts a slice of items by the speciafied element.
func Sort(element sortBy, items []*Item, reversed bool) {
switch element {
case ByName:
by(func(i1, i2 *Item) bool {
return i1.Name < i2.Name
}).sorter(items, reversed)
case ByDate:
by(func(i1, i2 *Item) bool {
return i1.Updated.Before(i2.Updated)
}).sorter(items, reversed)
case ByPrice:
by(func(i1, i2 *Item) bool {
return i1.Price < i2.Price
}).sorter(items, reversed)
}
}