From 57ee41b2cf19b7f0a5e9045098cc90d369148b8c Mon Sep 17 00:00:00 2001 From: Vartan Benohanian Date: Sun, 30 Aug 2020 21:25:01 -0400 Subject: [PATCH] Do unmarshaling in thing struct Signed-off-by: Vartan Benohanian --- reddit/message.go | 30 +++++----- reddit/subreddit.go | 9 +-- reddit/things.go | 131 +++++++++++++++++++++++++++++++------------- reddit/user.go | 9 +-- 4 files changed, 111 insertions(+), 68 deletions(-) diff --git a/reddit/message.go b/reddit/message.go index 16ffd95..5c67701 100644 --- a/reddit/message.go +++ b/reddit/message.go @@ -35,14 +35,17 @@ type Message struct { IsComment bool `json:"was_comment"` } +type inboxThing struct { + Kind string `json:"kind"` + Data *Message `json:"data"` +} + type inboxListing struct { inboxThings after string before string } -var _ anchor = &inboxListing{} - func (l *inboxListing) After() string { return l.after } @@ -73,14 +76,12 @@ func (l *inboxListing) UnmarshalJSON(b []byte) error { return nil } -// The returned JSON for comments is a bit different. -// It looks for like the Message struct. type inboxThings struct { Comments []*Message Messages []*Message } -// init initializes or clears the inbox. +// init initializes or clears the listing. func (t *inboxThings) init() { t.Comments = make([]*Message, 0) t.Messages = make([]*Message, 0) @@ -90,27 +91,24 @@ func (t *inboxThings) init() { func (t *inboxThings) UnmarshalJSON(b []byte) error { t.init() - var things []thing + var things []inboxThing if err := json.Unmarshal(b, &things); err != nil { return err } + t.add(things...) + return nil +} + +func (t *inboxThings) add(things ...inboxThing) { for _, thing := range things { switch thing.Kind { case kindComment: - v := new(Message) - if err := json.Unmarshal(thing.Data, v); err == nil { - t.Comments = append(t.Comments, v) - } + t.Comments = append(t.Comments, thing.Data) case kindMessage: - v := new(Message) - if err := json.Unmarshal(thing.Data, v); err == nil { - t.Messages = append(t.Messages, v) - } + t.Messages = append(t.Messages, thing.Data) } } - - return nil } // SendMessageRequest represents a request to send a message. diff --git a/reddit/subreddit.go b/reddit/subreddit.go index 39b8a73..97b6656 100644 --- a/reddit/subreddit.go +++ b/reddit/subreddit.go @@ -17,11 +17,6 @@ type SubredditService struct { client *Client } -type rootSubreddit struct { - Kind string `json:"kind,omitempty"` - Data *Subreddit `json:"data,omitempty"` -} - type rootSubredditNames struct { Names []string `json:"names,omitempty"` } @@ -133,13 +128,13 @@ func (s *SubredditService) Get(ctx context.Context, name string) (*Subreddit, *R return nil, nil, err } - root := new(rootSubreddit) + root := new(thing) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err } - return root.Data, resp, nil + return root.Subreddit(), resp, nil } // Popular returns popular subreddits. diff --git a/reddit/things.go b/reddit/things.go index 5266848..43cfc0e 100644 --- a/reddit/things.go +++ b/reddit/things.go @@ -2,6 +2,7 @@ package reddit import ( "encoding/json" + "fmt" ) const ( @@ -23,8 +24,79 @@ const ( // Its kind reprsents what it is and what is stored in the Data field. // e.g. t1 = comment, t2 = user, t3 = post, etc. type thing struct { - Kind string `json:"kind"` - Data json.RawMessage `json:"data"` + Kind string `json:"kind"` + 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 { @@ -32,7 +104,7 @@ type anchor interface { 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. type listing struct { things @@ -40,8 +112,6 @@ type listing struct { before string } -var _ anchor = &listing{} - func (l *listing) After() string { return l.after } @@ -100,42 +170,27 @@ func (t *things) UnmarshalJSON(b []byte) error { return err } + t.add(things...) + return nil +} + +func (t *things) add(things ...thing) { for _, thing := range things { - switch thing.Kind { - case kindComment: - v := new(Comment) - if err := json.Unmarshal(thing.Data, v); err == nil { - t.Comments = append(t.Comments, v) - } - case kindMore: - v := new(More) - if err := json.Unmarshal(thing.Data, v); err == nil { - t.Mores = append(t.Mores, v) - } - case kindAccount: - v := new(User) - 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) - } + switch v := thing.Data.(type) { + case *Comment: + t.Comments = append(t.Comments, v) + case *More: + t.Mores = append(t.Mores, v) + case *User: + t.Users = append(t.Users, v) + case *Post: + t.Posts = append(t.Posts, v) + case *Subreddit: + t.Subreddits = append(t.Subreddits, v) + case *ModAction: + t.ModActions = append(t.ModActions, v) } } - - return nil } // Comment is a comment posted by a user. diff --git a/reddit/user.go b/reddit/user.go index 1b8633c..f79985a 100644 --- a/reddit/user.go +++ b/reddit/user.go @@ -15,11 +15,6 @@ type UserService struct { client *Client } -type rootUser struct { - Kind string `json:"kind,omitempty"` - Data *User `json:"data,omitempty"` -} - // User represents a Reddit user. type User struct { // 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 } - root := new(rootUser) + root := new(thing) resp, err := s.client.Do(ctx, req, root) if err != nil { return nil, resp, err } - return root.Data, resp, nil + return root.User(), resp, nil } // GetMultipleByID returns multiple users from their full IDs.