]> git.lizzy.rs Git - go-anidb.git/blob - udp/reply.go
anidb: Add missing loop 'break's to EpisodeByEID
[go-anidb.git] / udp / reply.go
1 package udpapi
2
3 import (
4         "errors"
5         "fmt"
6         "strconv"
7         "strings"
8
9         "encoding/gob"
10 )
11
12 func init() {
13         // implements error
14         gob.RegisterName("*udpapi.APIError", &APIError{})
15         // implements APIReply
16         gob.RegisterName("*udpapi.errorWrapper", &errorWrapper{})
17         // implements APIReply
18         gob.RegisterName("*udpapi.genericReply", &genericReply{})
19 }
20
21 type APIError struct {
22         Code int
23         Desc string
24 }
25
26 func (err *APIError) Error() string {
27         return fmt.Sprint(err.Code, err.Desc)
28 }
29
30 // The interface for all UDP API replies.
31 //
32 // The user should call Error() to verify if the API call completed successfully.
33 type APIReply interface {
34         // An opaque string used as identifying tag.
35         Tag() string
36
37         // The integer code for the reply.
38         Code() int
39
40         // The description for the reply (first line minus code).
41         Text() string
42
43         // Slice with all lines of the reply.
44         Lines() []string
45
46         // Indicates whether the network code detected truncation.
47         Truncated() bool
48
49         // Returns the underlying error, if any.
50         Error() error
51 }
52
53 type errorWrapper struct {
54         err error
55 }
56
57 func (_ *errorWrapper) Tag() string {
58         return ""
59 }
60
61 func (w *errorWrapper) Code() int {
62         switch e := w.err.(type) {
63         case *APIError:
64                 return e.Code
65         default:
66                 return 999
67         }
68 }
69
70 func (w *errorWrapper) Text() string {
71         switch e := w.err.(type) {
72         case *APIError:
73                 return e.Desc
74         default:
75                 return e.Error()
76         }
77 }
78
79 func (w *errorWrapper) Lines() []string {
80         return []string{w.Text()}
81 }
82
83 func (_ *errorWrapper) Truncated() bool {
84         return false
85 }
86
87 func (w *errorWrapper) Error() error {
88         return w.err
89 }
90
91 func newErrorWrapper(err error) APIReply {
92         return &errorWrapper{
93                 err: err,
94         }
95 }
96
97 type genericReply struct {
98         raw       []byte
99         text      string
100         lines     []string
101         tag       string
102         code      int
103         truncated bool
104         err       error
105 }
106
107 // The value APIReply.Error() returns after a timeout.
108 // Unrelated to the server-side error 604 TIMEOUT - DELAY AND RESUBMIT.
109 var TimeoutError = errors.New("Timeout")
110
111 func newGenericReply(raw []byte) (r *genericReply) {
112         str := string(raw)
113         lines := strings.Split(str, "\n")
114         parts := strings.Fields(lines[0])
115
116         // invalid packet
117         if len(parts) < 1 {
118                 return nil
119         }
120
121         // Drop lines that are only whitespace
122         for len(lines) > 0 && strings.TrimSpace(lines[len(lines)-1]) == "" {
123                 lines = lines[:len(lines)-1]
124         }
125
126         // XXX: REQUIRES that the tag is not parsable as a base 10 number.
127         // Just prepending any sent tag with 'T' ought to be enough
128         tag := ""
129         text := ""
130         code, err := strconv.ParseInt(parts[0], 10, 16)
131         if err != nil && len(parts) > 1 {
132                 tag = parts[0]
133                 code, err = strconv.ParseInt(parts[1], 10, 16)
134
135                 if len(parts) > 2 {
136                         text = strings.Join(parts[2:], " ")
137                 }
138         } else if len(parts) > 1 {
139                 text = strings.Join(parts[1:], " ")
140         }
141
142         e := err
143         // 720-799 range is for notifications
144         // 799 is an API server shutdown notice, so I guess it's okay to be an error
145         if err == nil && code < 200 || (code > 299 && code < 720) || code > 798 {
146                 e = &APIError{Code: int(code), Desc: text}
147         }
148
149         return &genericReply{
150                 tag:   tag,
151                 code:  int(code),
152                 text:  text,
153                 lines: lines,
154                 err:   e,
155         }
156 }
157
158 func (r *genericReply) Tag() string {
159         return r.tag
160 }
161
162 func (r *genericReply) Code() int {
163         return r.code
164 }
165
166 func (r *genericReply) Text() string {
167         return r.text
168 }
169
170 func (r *genericReply) Lines() []string {
171         return r.lines
172 }
173
174 func (r *genericReply) Truncated() bool {
175         return r.truncated
176 }
177
178 func (r *genericReply) Error() error {
179         return r.err
180 }