]> git.lizzy.rs Git - mt.git/blob - inv.go
Add SerializeKeep method to Inv and InvList
[mt.git] / inv.go
1 package mt
2
3 import (
4         "fmt"
5         "io"
6         "reflect"
7 )
8
9 type Inv []NamedInvList
10
11 type NamedInvList struct {
12         Name string
13         InvList
14 }
15
16 func (inv Inv) List(name string) *NamedInvList {
17         for i, l := range inv {
18                 if l.Name == name {
19                         return &inv[i]
20                 }
21         }
22         return nil
23 }
24
25 func (i Inv) Serialize(w io.Writer) error {
26         return i.SerializeKeep(w, nil)
27 }
28
29 func (i Inv) SerializeKeep(w io.Writer, old Inv) error {
30         ew := &errWriter{w: w}
31
32         for _, l := range i {
33                 if reflect.DeepEqual(&i, old.List(l.Name)) {
34                         fmt.Fprintln(ew, "KeepList", l.Name)
35                         continue
36                 }
37
38                 fmt.Fprintln(ew, "List", l.Name, len(l.Stacks))
39                 l.Serialize(ew)
40         }
41         fmt.Fprintln(ew, "EndInventory")
42
43         return ew.err
44 }
45
46 func (i *Inv) Deserialize(r io.Reader) (err error) {
47         s := new(sentinal)
48         defer s.recover(&err)
49
50         old := *i
51         *i = nil
52
53         for {
54                 if err := readCmdLn(r, map[string]interface{}{
55                         "List": func(name string, size int) {
56                                 l := old.List(name)
57                                 if l == nil {
58                                         l = &NamedInvList{Name: name}
59                                 }
60
61                                 if err := l.Deserialize(r); err != nil {
62                                         s.ret(fmt.Errorf("List %s %d: %w", name, size, err))
63                                 }
64                                 if len(l.Stacks) != size {
65                                         s.ret(fmt.Errorf("List %s %d: contains %d stacks", name, size, len(l.Stacks)))
66                                 }
67
68                                 *i = append(*i, *l)
69                         },
70                         "KeepList": func(name string) {
71                                 l := old.List(name)
72                                 if l == nil {
73                                         s.ret(fmt.Errorf("KeepList %s: list does not exist", name))
74                                 }
75
76                                 *i = append(*i, *l)
77                         },
78                         "EndInventory": func() {
79                                 s.ret(nil)
80                         },
81                 }); err != nil {
82                         if err == io.EOF {
83                                 return io.ErrUnexpectedEOF
84                         }
85                         return err
86                 }
87         }
88 }
89
90 type InvList struct {
91         Width  int
92         Stacks []Stack
93 }
94
95 func (l InvList) Serialize(w io.Writer) error {
96         return l.SerializeKeep(w, InvList{})
97 }
98
99 func (l InvList) SerializeKeep(w io.Writer, old InvList) error {
100         ew := &errWriter{w: w}
101
102         fmt.Fprintln(ew, "Width", l.Width)
103         for i, s := range l.Stacks {
104                 if i < len(old.Stacks) && s == old.Stacks[i] {
105                         fmt.Fprintln(ew, "Keep")
106                 }
107
108                 if s.Count > 0 {
109                         fmt.Fprintln(ew, "Item", s)
110                 } else {
111                         fmt.Fprintln(ew, "Empty")
112                 }
113         }
114         fmt.Fprintln(ew, "EndInventoryList")
115
116         return ew.err
117 }
118
119 func (l *InvList) Deserialize(r io.Reader) (err error) {
120         s := new(sentinal)
121         defer s.recover(&err)
122
123         if _, err := fmt.Fscanf(r, "Width %d\n", &l.Width); err != nil {
124                 return err
125         }
126
127         old := l.Stacks
128         l.Stacks = nil
129
130         for {
131                 if err := readCmdLn(r, map[string]interface{}{
132                         "Empty": func() {
133                                 l.Stacks = append(l.Stacks, Stack{})
134                         },
135                         "Item": func(stk Stack) {
136                                 l.Stacks = append(l.Stacks, stk)
137                         },
138                         "Keep": func() {
139                                 if i := len(l.Stacks); i < len(old) {
140                                         l.Stacks = append(l.Stacks, old[i])
141                                 } else {
142                                         l.Stacks = append(l.Stacks, Stack{})
143                                 }
144                         },
145                         "EndInventoryList": func() {
146                                 s.ret(nil)
147                         },
148                 }); err != nil {
149                         if err == io.EOF {
150                                 return io.ErrUnexpectedEOF
151                         }
152                         return err
153                 }
154         }
155 }
156
157 func readCmdLn(r io.Reader, cmds map[string]interface{}) error {
158         if _, ok := r.(io.RuneScanner); !ok {
159                 r = &readRune{Reader: r, peekRune: -1}
160         }
161
162         var cmd string
163         if _, err := fmt.Fscan(r, &cmd); err != nil {
164                 return err
165         }
166
167         f, ok := cmds[cmd]
168         if !ok {
169                 return fmt.Errorf("unsupported line type: %+q", cmd)
170         }
171
172         t := reflect.TypeOf(f)
173
174         a := make([]interface{}, t.NumIn())
175         for i := range a {
176                 a[i] = reflect.New(t.In(i)).Interface()
177         }
178         fmt.Fscanln(r, a...)
179
180         args := make([]reflect.Value, t.NumIn())
181         for i := range args {
182                 args[i] = reflect.ValueOf(a[i]).Elem()
183         }
184         reflect.ValueOf(f).Call(args)
185
186         return nil
187 }
188
189 type sentinal struct {
190         err error
191 }
192
193 func (s *sentinal) ret(err error) {
194         s.err = err
195         panic(s)
196 }
197
198 func (s *sentinal) recover(p *error) {
199         if r := recover(); r != nil {
200                 if r == s {
201                         *p = s.err
202                 } else {
203                         panic(r)
204                 }
205         }
206 }
207
208 type errWriter struct {
209         w   io.Writer
210         err error
211 }
212
213 func (ew *errWriter) Write(p []byte) (int, error) {
214         if ew.err != nil {
215                 return 0, ew.err
216         }
217
218         n, err := ew.w.Write(p)
219         if err != nil {
220                 ew.err = err
221         }
222         return n, err
223 }