Do unmarshaling in thing struct

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian
2020-08-30 21:25:01 -04:00
parent e1dff15901
commit 57ee41b2cf
4 changed files with 111 additions and 68 deletions
+14 -16
View File
@@ -35,14 +35,17 @@ type Message struct {
IsComment bool `json:"was_comment"` IsComment bool `json:"was_comment"`
} }
type inboxThing struct {
Kind string `json:"kind"`
Data *Message `json:"data"`
}
type inboxListing struct { type inboxListing struct {
inboxThings inboxThings
after string after string
before string before string
} }
var _ anchor = &inboxListing{}
func (l *inboxListing) After() string { func (l *inboxListing) After() string {
return l.after return l.after
} }
@@ -73,14 +76,12 @@ func (l *inboxListing) UnmarshalJSON(b []byte) error {
return nil return nil
} }
// The returned JSON for comments is a bit different.
// It looks for like the Message struct.
type inboxThings struct { type inboxThings struct {
Comments []*Message Comments []*Message
Messages []*Message Messages []*Message
} }
// init initializes or clears the inbox. // init initializes or clears the listing.
func (t *inboxThings) init() { func (t *inboxThings) init() {
t.Comments = make([]*Message, 0) t.Comments = make([]*Message, 0)
t.Messages = make([]*Message, 0) t.Messages = make([]*Message, 0)
@@ -90,27 +91,24 @@ func (t *inboxThings) init() {
func (t *inboxThings) UnmarshalJSON(b []byte) error { func (t *inboxThings) UnmarshalJSON(b []byte) error {
t.init() t.init()
var things []thing var things []inboxThing
if err := json.Unmarshal(b, &things); err != nil { if err := json.Unmarshal(b, &things); err != nil {
return err return err
} }
t.add(things...)
return nil
}
func (t *inboxThings) add(things ...inboxThing) {
for _, thing := range things { for _, thing := range things {
switch thing.Kind { switch thing.Kind {
case kindComment: case kindComment:
v := new(Message) t.Comments = append(t.Comments, thing.Data)
if err := json.Unmarshal(thing.Data, v); err == nil {
t.Comments = append(t.Comments, v)
}
case kindMessage: case kindMessage:
v := new(Message) t.Messages = append(t.Messages, thing.Data)
if err := json.Unmarshal(thing.Data, v); err == nil {
t.Messages = append(t.Messages, v)
}
} }
} }
return nil
} }
// SendMessageRequest represents a request to send a message. // SendMessageRequest represents a request to send a message.
+2 -7
View File
@@ -17,11 +17,6 @@ type SubredditService struct {
client *Client client *Client
} }
type rootSubreddit struct {
Kind string `json:"kind,omitempty"`
Data *Subreddit `json:"data,omitempty"`
}
type rootSubredditNames struct { type rootSubredditNames struct {
Names []string `json:"names,omitempty"` Names []string `json:"names,omitempty"`
} }
@@ -133,13 +128,13 @@ func (s *SubredditService) Get(ctx context.Context, name string) (*Subreddit, *R
return nil, nil, err return nil, nil, err
} }
root := new(rootSubreddit) root := new(thing)
resp, err := s.client.Do(ctx, req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
return root.Data, resp, nil return root.Subreddit(), resp, nil
} }
// Popular returns popular subreddits. // Popular returns popular subreddits.
+93 -38
View File
@@ -2,6 +2,7 @@ package reddit
import ( import (
"encoding/json" "encoding/json"
"fmt"
) )
const ( const (
@@ -23,8 +24,79 @@ const (
// Its kind reprsents what it is and what is stored in the Data field. // Its kind reprsents what it is and what is stored in the Data field.
// e.g. t1 = comment, t2 = user, t3 = post, etc. // e.g. t1 = comment, t2 = user, t3 = post, etc.
type thing struct { type thing struct {
Kind string `json:"kind"` Kind string `json:"kind"`
Data json.RawMessage `json:"data"` Data interface{} `json:"data"`
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (t *thing) UnmarshalJSON(b []byte) error {
root := new(struct {
Kind string `json:"kind"`
Data json.RawMessage `json:"data"`
})
err := json.Unmarshal(b, root)
if err != nil {
return err
}
t.Kind = root.Kind
var v interface{}
switch t.Kind {
case kindComment:
v = new(Comment)
case kindMore:
v = new(More)
case kindAccount:
v = new(User)
case kindPost:
v = new(Post)
case kindSubreddit:
v = new(Subreddit)
case kindModAction:
v = new(ModAction)
default:
return fmt.Errorf("unrecognized kind: %q", t.Kind)
}
err = json.Unmarshal(root.Data, v)
if err != nil {
return err
}
t.Data = v
return nil
}
func (t *thing) Comment() *Comment {
v, _ := t.Data.(*Comment)
return v
}
func (t *thing) More() *More {
v, _ := t.Data.(*More)
return v
}
func (t *thing) User() *User {
v, _ := t.Data.(*User)
return v
}
func (t *thing) Post() *Post {
v, _ := t.Data.(*Post)
return v
}
func (t *thing) Subreddit() *Subreddit {
v, _ := t.Data.(*Subreddit)
return v
}
func (t *thing) ModAction() *ModAction {
v, _ := t.Data.(*ModAction)
return v
} }
type anchor interface { type anchor interface {
@@ -32,7 +104,7 @@ type anchor interface {
Before() string Before() string
} }
// listing holds things coming from the Reddit API. // listing is a list of things coming from the Reddit API.
// It also contains the after/before anchors useful for subsequent requests. // It also contains the after/before anchors useful for subsequent requests.
type listing struct { type listing struct {
things things
@@ -40,8 +112,6 @@ type listing struct {
before string before string
} }
var _ anchor = &listing{}
func (l *listing) After() string { func (l *listing) After() string {
return l.after return l.after
} }
@@ -100,42 +170,27 @@ func (t *things) UnmarshalJSON(b []byte) error {
return err return err
} }
t.add(things...)
return nil
}
func (t *things) add(things ...thing) {
for _, thing := range things { for _, thing := range things {
switch thing.Kind { switch v := thing.Data.(type) {
case kindComment: case *Comment:
v := new(Comment) t.Comments = append(t.Comments, v)
if err := json.Unmarshal(thing.Data, v); err == nil { case *More:
t.Comments = append(t.Comments, v) t.Mores = append(t.Mores, v)
} case *User:
case kindMore: t.Users = append(t.Users, v)
v := new(More) case *Post:
if err := json.Unmarshal(thing.Data, v); err == nil { t.Posts = append(t.Posts, v)
t.Mores = append(t.Mores, v) case *Subreddit:
} t.Subreddits = append(t.Subreddits, v)
case kindAccount: case *ModAction:
v := new(User) t.ModActions = append(t.ModActions, v)
if err := json.Unmarshal(thing.Data, v); err == nil {
t.Users = append(t.Users, v)
}
case kindPost:
v := new(Post)
if err := json.Unmarshal(thing.Data, v); err == nil {
t.Posts = append(t.Posts, v)
}
case kindSubreddit:
v := new(Subreddit)
if err := json.Unmarshal(thing.Data, v); err == nil {
t.Subreddits = append(t.Subreddits, v)
}
case kindModAction:
v := new(ModAction)
if err := json.Unmarshal(thing.Data, v); err == nil {
t.ModActions = append(t.ModActions, v)
}
} }
} }
return nil
} }
// Comment is a comment posted by a user. // Comment is a comment posted by a user.
+2 -7
View File
@@ -15,11 +15,6 @@ type UserService struct {
client *Client client *Client
} }
type rootUser struct {
Kind string `json:"kind,omitempty"`
Data *User `json:"data,omitempty"`
}
// User represents a Reddit user. // User represents a Reddit user.
type User struct { type User struct {
// this is not the full ID, watch out. // this is not the full ID, watch out.
@@ -83,13 +78,13 @@ func (s *UserService) Get(ctx context.Context, username string) (*User, *Respons
return nil, nil, err return nil, nil, err
} }
root := new(rootUser) root := new(thing)
resp, err := s.client.Do(ctx, req, root) resp, err := s.client.Do(ctx, req, root)
if err != nil { if err != nil {
return nil, resp, err return nil, resp, err
} }
return root.Data, resp, nil return root.User(), resp, nil
} }
// GetMultipleByID returns multiple users from their full IDs. // GetMultipleByID returns multiple users from their full IDs.