7b83e8366a
Signed-off-by: Vartan Benohanian <vartanbeno@gmail.com>
319 lines
7.5 KiB
Go
319 lines
7.5 KiB
Go
package geddit
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
)
|
|
|
|
// SearchService handles communication with the search
|
|
// related methods of the Reddit API
|
|
// IMPORTANT: for searches to include NSFW results, the
|
|
// user must check the following in their preferences:
|
|
// "include not safe for work (NSFW) search results in searches"
|
|
// Note: The "limit" parameter in searches is prone to inconsistent
|
|
// behaviour.
|
|
type SearchService interface {
|
|
Posts(query string, opts ...SearchOpt) *PostSearcher
|
|
Subreddits(query string, opts ...SearchOpt) *SubredditSearcher
|
|
Users(query string, opts ...SearchOpt) *UserSearcher
|
|
}
|
|
|
|
// SearchServiceOp implements the VoteService interface
|
|
type SearchServiceOp struct {
|
|
client *Client
|
|
}
|
|
|
|
var _ SearchService = &SearchServiceOp{}
|
|
|
|
// Posts searches for posts.
|
|
// By default, it searches for the most relevant posts of all time.
|
|
func (s *SearchServiceOp) Posts(query string, opts ...SearchOpt) *PostSearcher {
|
|
sr := new(PostSearcher)
|
|
sr.client = s.client
|
|
sr.opts.Query = query
|
|
sr.opts.Type = "link"
|
|
sr.opts.Sort = SortRelevance.String()
|
|
sr.opts.Timespan = TimespanAll.String()
|
|
for _, opt := range opts {
|
|
opt(sr)
|
|
}
|
|
return sr
|
|
}
|
|
|
|
// Subreddits searches for subreddits.
|
|
func (s *SearchServiceOp) Subreddits(query string, opts ...SearchOpt) *SubredditSearcher {
|
|
sr := new(SubredditSearcher)
|
|
sr.client = s.client
|
|
sr.opts.Query = query
|
|
sr.opts.Type = "sr"
|
|
for _, opt := range opts {
|
|
opt(sr)
|
|
}
|
|
return sr
|
|
}
|
|
|
|
// Users searches for users.
|
|
func (s *SearchServiceOp) Users(query string, opts ...SearchOpt) *UserSearcher {
|
|
sr := new(UserSearcher)
|
|
sr.client = s.client
|
|
sr.opts.Query = query
|
|
sr.opts.Type = "user"
|
|
for _, opt := range opts {
|
|
opt(sr)
|
|
}
|
|
return sr
|
|
}
|
|
|
|
// PostSearcher helps conducts searches that return posts.
|
|
type PostSearcher struct {
|
|
clientSearcher
|
|
subreddits []string
|
|
after string
|
|
Results []Link
|
|
}
|
|
|
|
func (s *PostSearcher) search(ctx context.Context) (*Links, *Response, error) {
|
|
path := "search"
|
|
if len(s.subreddits) > 0 {
|
|
path = fmt.Sprintf("r/%s/search", strings.Join(s.subreddits, "+"))
|
|
}
|
|
|
|
root, resp, err := s.clientSearcher.Do(ctx, path)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
|
|
return root.getLinks(), resp, nil
|
|
}
|
|
|
|
// Search runs the searcher.
|
|
// The first return value tells the user if there are
|
|
// more results that were cut off (due to the limit).
|
|
func (s *PostSearcher) Search(ctx context.Context) (bool, *Response, error) {
|
|
root, resp, err := s.search(ctx)
|
|
if err != nil {
|
|
return false, resp, err
|
|
}
|
|
|
|
s.Results = root.Links
|
|
s.after = root.After
|
|
|
|
// if the "after" value is non-empty, it
|
|
// means there are more results to come.
|
|
moreResultsExist := s.after != ""
|
|
|
|
return moreResultsExist, resp, nil
|
|
}
|
|
|
|
// More runs the searcher again and adds to the results.
|
|
// The first return value tells the user if there are
|
|
// more results that were cut off (due to the limit).
|
|
func (s *PostSearcher) More(ctx context.Context) (bool, *Response, error) {
|
|
if s.after == "" {
|
|
return s.Search(ctx)
|
|
}
|
|
|
|
s.setAfter(s.after)
|
|
|
|
root, resp, err := s.search(ctx)
|
|
if err != nil {
|
|
return false, resp, err
|
|
}
|
|
|
|
s.Results = append(s.Results, root.Links...)
|
|
s.after = root.After
|
|
|
|
// if the "after" value is non-empty, it
|
|
// means there are more results to come.
|
|
moreResultsExist := s.after != ""
|
|
|
|
return moreResultsExist, resp, nil
|
|
}
|
|
|
|
// All runs the searcher until it yields no more results.
|
|
// The limit is set to 100, just to make the least amount
|
|
// of requests possible. It is reset to its original value after.
|
|
func (s *PostSearcher) All(ctx context.Context) error {
|
|
limit := s.opts.Limit
|
|
|
|
s.setLimit(100)
|
|
defer s.setLimit(limit)
|
|
|
|
var ok = true
|
|
var err error
|
|
|
|
for ok {
|
|
ok, _, err = s.More(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// SubredditSearcher helps conducts searches that return subreddits.
|
|
type SubredditSearcher struct {
|
|
clientSearcher
|
|
after string
|
|
Results []Subreddit
|
|
}
|
|
|
|
func (s *SubredditSearcher) search(ctx context.Context) (*Subreddits, *Response, error) {
|
|
path := "search"
|
|
root, resp, err := s.clientSearcher.Do(ctx, path)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
return root.getSubreddits(), resp, nil
|
|
}
|
|
|
|
// Search runs the searcher.
|
|
// The first return value tells the user if there are
|
|
// more results that were cut off (due to the limit).
|
|
func (s *SubredditSearcher) Search(ctx context.Context) (bool, *Response, error) {
|
|
root, resp, err := s.search(ctx)
|
|
if err != nil {
|
|
return false, resp, err
|
|
}
|
|
|
|
s.Results = root.Subreddits
|
|
s.after = root.After
|
|
|
|
// if the "after" value is non-empty, it
|
|
// means there are more results to come.
|
|
moreResultsExist := s.after != ""
|
|
|
|
return moreResultsExist, resp, nil
|
|
}
|
|
|
|
// More runs the searcher again and adds to the results.
|
|
// The first return value tells the user if there are
|
|
// more results that were cut off (due to the limit).
|
|
func (s *SubredditSearcher) More(ctx context.Context) (bool, *Response, error) {
|
|
if s.after == "" {
|
|
return s.Search(ctx)
|
|
}
|
|
|
|
s.setAfter(s.after)
|
|
|
|
root, resp, err := s.search(ctx)
|
|
if err != nil {
|
|
return false, resp, err
|
|
}
|
|
|
|
s.Results = append(s.Results, root.Subreddits...)
|
|
s.after = root.After
|
|
|
|
// if the "after" value is non-empty, it
|
|
// means there are more results to come.
|
|
moreResultsExist := s.after != ""
|
|
|
|
return moreResultsExist, resp, nil
|
|
}
|
|
|
|
// All runs the searcher until it yields no more results.
|
|
// The limit is set to 100, just to make the least amount
|
|
// of requests possible. It is reset to its original value after.
|
|
func (s *SubredditSearcher) All(ctx context.Context) error {
|
|
limit := s.opts.Limit
|
|
|
|
s.setLimit(100)
|
|
defer s.setLimit(limit)
|
|
|
|
var ok = true
|
|
var err error
|
|
|
|
for ok {
|
|
ok, _, err = s.More(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// UserSearcher helps conducts searches that return users.
|
|
type UserSearcher struct {
|
|
clientSearcher
|
|
after string
|
|
Results []User
|
|
}
|
|
|
|
func (s *UserSearcher) search(ctx context.Context) (*Users, *Response, error) {
|
|
path := "search"
|
|
root, resp, err := s.clientSearcher.Do(ctx, path)
|
|
if err != nil {
|
|
return nil, resp, err
|
|
}
|
|
return root.getUsers(), resp, nil
|
|
}
|
|
|
|
// Search runs the searcher.
|
|
// The first return value tells the user if there are
|
|
// more results that were cut off (due to the limit).
|
|
func (s *UserSearcher) Search(ctx context.Context) (bool, *Response, error) {
|
|
root, resp, err := s.search(ctx)
|
|
if err != nil {
|
|
return false, resp, err
|
|
}
|
|
|
|
s.Results = root.Users
|
|
s.after = root.After
|
|
|
|
// if the "after" value is non-empty, it
|
|
// means there are more results to come.
|
|
moreResultsExist := s.after != ""
|
|
|
|
return moreResultsExist, resp, nil
|
|
}
|
|
|
|
// More runs the searcher again and adds to the results.
|
|
// The first return value tells the user if there are
|
|
// more results that were cut off (due to the limit).
|
|
func (s *UserSearcher) More(ctx context.Context) (bool, *Response, error) {
|
|
if s.after == "" {
|
|
return s.Search(ctx)
|
|
}
|
|
|
|
s.setAfter(s.after)
|
|
|
|
root, resp, err := s.search(ctx)
|
|
if err != nil {
|
|
return false, resp, err
|
|
}
|
|
|
|
s.Results = append(s.Results, root.Users...)
|
|
s.after = root.After
|
|
|
|
// if the "after" value is non-empty, it
|
|
// means there are more results to come.
|
|
moreResultsExist := s.after != ""
|
|
|
|
return moreResultsExist, resp, nil
|
|
}
|
|
|
|
// All runs the searcher until it yields no more results.
|
|
// The limit is set to 100, just to make the least amount
|
|
// of requests possible. It is reset to its original value after.
|
|
func (s *UserSearcher) All(ctx context.Context) error {
|
|
limit := s.opts.Limit
|
|
|
|
s.setLimit(100)
|
|
defer s.setLimit(limit)
|
|
|
|
var ok = true
|
|
var err error
|
|
|
|
for ok {
|
|
ok, _, err = s.More(ctx)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|