108 lines
2.1 KiB
Go
108 lines
2.1 KiB
Go
package command
|
|||
|
|
|
||
|
|
import (
|
||
|
|
"context"
|
||
|
|
"fmt"
|
||
|
|
"net/url"
|
||
|
|
"os"
|
||
|
|
"strings"
|
||
|
|
|
||
|
|
"github.com/StevanFreeborn/gator/internal/database"
|
||
|
|
"github.com/StevanFreeborn/gator/internal/state"
|
||
|
|
"golang.org/x/term"
|
||
|
|
)
|
||
|
|
|
||
|
|
func normalizeFeedURL(raw string) (string, error) {
|
||
|
|
u, err := url.Parse(raw)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return "", err
|
||
|
|
}
|
||
|
|
|
||
|
|
u.Scheme = strings.ToLower(u.Scheme)
|
||
|
|
u.Host = strings.ToLower(u.Host)
|
||
|
|
|
||
|
|
if (u.Scheme == "http" && u.Port() == "80") || (u.Scheme == "https" && u.Port() == "443") {
|
||
|
|
u.Host = u.Hostname()
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(u.Path) > 1 {
|
||
|
|
u.Path = strings.TrimSuffix(u.Path, "/")
|
||
|
|
}
|
||
|
|
|
||
|
|
u.Fragment = ""
|
||
|
|
|
||
|
|
return u.String(), nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func resolveFeedByNameOrURL(s *state.State, identifier string) (database.Feed, error) {
|
||
|
|
if strings.TrimSpace(identifier) == "" {
|
||
|
|
return database.Feed{}, fmt.Errorf("Feed identifier cannot be empty")
|
||
|
|
}
|
||
|
|
|
||
|
|
feeds, err := s.Database.GetAllFeeds(context.Background())
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return database.Feed{}, fmt.Errorf("Failed to load feeds")
|
||
|
|
}
|
||
|
|
|
||
|
|
var matches []database.Feed
|
||
|
|
|
||
|
|
for _, feed := range feeds {
|
||
|
|
if strings.EqualFold(feed.Name, identifier) {
|
||
|
|
matches = append(matches, feed)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(matches) == 1 {
|
||
|
|
return matches[0], nil
|
||
|
|
}
|
||
|
|
|
||
|
|
if len(matches) > 1 {
|
||
|
|
return database.Feed{}, fmt.Errorf("Multiple feeds match name '%s'; use a url instead", identifier)
|
||
|
|
}
|
||
|
|
|
||
|
|
normalizedURL, err := normalizeFeedURL(identifier)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return database.Feed{}, fmt.Errorf("No feed found matching name or url '%s'", identifier)
|
||
|
|
}
|
||
|
|
|
||
|
|
feed, err := s.Database.GetFeedByUrl(context.Background(), normalizedURL)
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return database.Feed{}, fmt.Errorf("No feed found matching name or url '%s'", identifier)
|
||
|
|
}
|
||
|
|
|
||
|
|
return feed, nil
|
||
|
|
}
|
||
|
|
|
||
|
|
func enableKeypressExit(cancel context.CancelFunc) (func(), error) {
|
||
|
|
oldState, err := term.MakeRaw(int(os.Stdin.Fd()))
|
||
|
|
|
||
|
|
if err != nil {
|
||
|
|
return nil, fmt.Errorf("Failed to enable raw mode: %v", err)
|
||
|
|
}
|
||
|
|
|
||
|
|
go func() {
|
||
|
|
buf := make([]byte, 1)
|
||
|
|
|
||
|
|
for {
|
||
|
|
n, err := os.Stdin.Read(buf)
|
||
|
|
|
||
|
|
if err != nil || n == 0 {
|
||
|
|
return
|
||
|
|
}
|
||
|
|
|
||
|
|
if buf[0] == 'q' || buf[0] == 'Q' || buf[0] == 3 {
|
||
|
|
cancel()
|
||
|
|
return
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}()
|
||
|
|
|
||
|
|
return func() {
|
||
|
|
term.Restore(int(os.Stdin.Fd()), oldState)
|
||
|
|
}, nil
|
||
|
|
}
|