Handle listings better by using custom unmarshaling

Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
This commit is contained in:
Vartan Benohanian
2020-05-03 17:31:35 -04:00
parent 460554e19e
commit 7922711d51
9 changed files with 988 additions and 345 deletions
+190 -2
View File
@@ -343,7 +343,6 @@ func CheckResponse(r *http.Response) error {
// ListOptions are the optional parameters to the various endpoints that return lists
type ListOptions struct {
Sort string `url:"sort,omitempty"`
Type string `url:"type,omitempty"` // links or comments
// For getting submissions
@@ -354,7 +353,6 @@ type ListOptions struct {
After string `url:"after,omitempty"`
Before string `url:"before,omitempty"`
Limit int `url:"limit,omitempty"` // default: 25
Count int `url:"count,omitempty"` // default: 0
}
func addOptions(s string, opt interface{}) (string, error) {
@@ -388,6 +386,61 @@ type root struct {
Data interface{} `json:"data,omitempty"`
}
type rootListing struct {
Kind string `json:"kind,omitempty"`
Data *Listing `json:"data"`
}
// Listing holds things coming from the Reddit API
// It also contains the after/before anchors useful for subsequent requests
type Listing struct {
Things Things `json:"children"`
After string `json:"after"`
Before string `json:"before"`
}
// Things are stuff!
type Things struct {
Comments []Comment `json:"comments,omitempty"`
Links []Link `json:"links,omitempty"`
Subreddits []Subreddit `json:"subreddits,omitempty"`
// todo: add the other kinds of things
}
// UnmarshalJSON implements the json.Unmarshaler interface.
func (l *Things) UnmarshalJSON(b []byte) error {
var children []map[string]interface{}
if err := json.Unmarshal(b, &children); err != nil {
return err
}
for _, child := range children {
byteValue, _ := json.Marshal(child)
switch child["kind"] {
case kindComment:
root := new(commentRoot)
if err := json.Unmarshal(byteValue, root); err == nil && root.Data != nil {
l.Comments = append(l.Comments, *root.Data)
}
case kindAccount:
case kindLink:
root := new(linkRoot)
if err := json.Unmarshal(byteValue, root); err == nil && root.Data != nil {
l.Links = append(l.Links, *root.Data)
}
case kindMessage:
case kindSubreddit:
root := new(subredditRoot)
if err := json.Unmarshal(byteValue, root); err == nil && root.Data != nil {
l.Subreddits = append(l.Subreddits, *root.Data)
}
case kindAward:
}
}
return nil
}
const (
kindListing string = "Listing"
kindComment string = "t1"
@@ -397,3 +450,138 @@ const (
kindSubreddit string = "t5"
kindAward string = "t6"
)
type commentRoot struct {
Kind string `json:"kind,omitempty"`
Data *Comment `json:"data,omitempty"`
}
type linkRoot struct {
Kind string `json:"kind,omitempty"`
Data *Link `json:"data,omitempty"`
}
type subredditRoot struct {
Kind string `json:"kind,omitempty"`
Data *Subreddit `json:"data,omitempty"`
}
// Comment is a comment posted by a user
type Comment struct {
ID string `json:"id,omitempty"`
FullID string `json:"name,omitempty"`
ParentID string `json:"parent_id,omitempty"`
Permalink string `json:"permalink,omitempty"`
Body string `json:"body,omitempty"`
Author string `json:"author,omitempty"`
AuthorID string `json:"author_fullname,omitempty"`
AuthorFlairText string `json:"author_flair_text,omitempty"`
AuthorFlairID string `json:"author_flair_template_id,omitempty"`
Subreddit string `json:"subreddit,omitempty"`
SubredditNamePrefixed string `json:"subreddit_name_prefixed,omitempty"`
SubredditID string `json:"subreddit_id,omitempty"`
Score int `json:"score"`
Controversiality int `json:"controversiality"`
Created *Timestamp `json:"created_utc,omitempty"`
Edited *Timestamp `json:"edited,omitempty"`
LinkID string `json:"link_id,omitempty"`
// These don't appear when submitting a comment
LinkTitle string `json:"link_title,omitempty"`
LinkPermalink string `json:"link_permalink,omitempty"`
LinkAuthor string `json:"link_author,omitempty"`
LinkNumComments int `json:"num_comments"`
IsSubmitter bool `json:"is_submitter"`
ScoreHidden bool `json:"score_hidden"`
Saved bool `json:"saved"`
Stickied bool `json:"stickied"`
Locked bool `json:"locked"`
CanGild bool `json:"can_gild"`
NSFW bool `json:"over_18"`
// If a comment has no replies, its "replies" value is "",
// which the unmarshaler doesn't like
// So we capture this varying field in RepliesRaw, and then
// fill it in Replies
RepliesRaw json.RawMessage `json:"replies,omitempty"`
Replies []commentRoot `json:"-"`
}
// Link is a submitted post on Reddit
type Link struct {
ID string `json:"id,omitempty"`
FullID string `json:"name,omitempty"`
Created *Timestamp `json:"created_utc,omitempty"`
Edited *Timestamp `json:"edited,omitempty"`
Permalink string `json:"permalink,omitempty"`
URL string `json:"url,omitempty"`
Title string `json:"title,omitempty"`
Body string `json:"selftext,omitempty"`
Score int `json:"score"`
NumberOfComments int `json:"num_comments"`
SubredditID string `json:"subreddit_id,omitempty"`
SubredditName string `json:"subreddit,omitempty"`
SubredditNamePrefixed string `json:"subreddit_name_prefixed,omitempty"`
AuthorID string `json:"author_fullname,omitempty"`
AuthorName string `json:"author,omitempty"`
Spoiler bool `json:"spoiler"`
Locked bool `json:"locked"`
NSFW bool `json:"over_18"`
IsSelfPost bool `json:"is_self"`
Saved bool `json:"saved"`
Stickied bool `json:"stickied"`
}
// Subreddit holds information about a subreddit
type Subreddit struct {
ID string `json:"id,omitempty"`
FullID string `json:"name,omitempty"`
Created *Timestamp `json:"created_utc,omitempty"`
URL string `json:"url,omitempty"`
Name string `json:"display_name,omitempty"`
NamePrefixed string `json:"display_name_prefixed,omitempty"`
Title string `json:"title,omitempty"`
PublicDescription string `json:"public_description,omitempty"`
Type string `json:"subreddit_type,omitempty"`
SuggestedCommentSort string `json:"suggested_comment_sort,omitempty"`
Subscribers int `json:"subscribers"`
ActiveUserCount *int `json:"active_user_count,omitempty"`
NSFW bool `json:"over18"`
UserIsMod bool `json:"user_is_moderator"`
}
func (rl *rootListing) getLinks() *LinkList {
if rl == nil || rl.Data == nil {
return nil
}
return &LinkList{
Links: rl.Data.Things.Links,
After: rl.Data.After,
Before: rl.Data.Before,
}
}
func (rl *rootListing) getComments() *CommentList {
if rl == nil || rl.Data == nil {
return nil
}
return &CommentList{
Comments: rl.Data.Things.Comments,
After: rl.Data.After,
Before: rl.Data.Before,
}
}