Files
gator/internal/command/command.go
T
Stevan Freeborn c012f6008c feat: implemented many commands
- added register command
- added reset command
- added users command
- added agg command
- added feeds command
2026-08-01 14:26:19 -05:00

269 lines
5.5 KiB
Go

package command
import (
"cmp"
"context"
"fmt"
"net/url"
"slices"
"strings"
"time"
"github.com/StevanFreeborn/gator/internal/database"
"github.com/StevanFreeborn/gator/internal/rss"
"github.com/StevanFreeborn/gator/internal/state"
"github.com/google/uuid"
)
type Command struct {
Name string
Handler CommandHandler
}
type CommandHandler func(s *state.State) error
func newCommand(name string, handler CommandHandler) *Command {
return &Command{
Name: name,
Handler: handler,
}
}
type CommandRegistry struct {
commands map[string]*Command
}
func (c *CommandRegistry) register(cmd *Command) error {
c.commands[cmd.Name] = cmd
return nil
}
func NewRegistry() *CommandRegistry {
cr := CommandRegistry{
commands: map[string]*Command{},
}
commands := []*Command{
loginCommand(),
registerCommand(),
resetCommand(),
usersCommand(),
aggCommand(),
addFeedCommand(),
feedsCommand(),
}
for _, cmd := range commands {
cr.register(cmd)
}
return &cr
}
func (c *CommandRegistry) RunCommand(cmdName string, s *state.State) error {
cmd, found := c.commands[cmdName]
if !found {
return fmt.Errorf("No '%s' registered", cmdName)
}
return cmd.Handler(s)
}
func loginCommand() *Command {
return newCommand("login", func(s *state.State) error {
if len(s.Arguments) == 0 {
return fmt.Errorf("Did not receive expected username argument")
}
username := s.Arguments[0]
_, err := s.Database.GetUserByName(context.Background(), username)
if err != nil {
return fmt.Errorf("Failed to login")
}
err = s.Config.SetUser(username)
if err != nil {
return err
}
fmt.Printf("Current user set to '%s'\n", username)
return nil
})
}
func registerCommand() *Command {
return newCommand("register", func(s *state.State) error {
if len(s.Arguments) == 0 {
return fmt.Errorf("Did not receive expected username argument")
}
createUserParams := database.CreateUserParams{
ID: uuid.New(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Name: s.Arguments[0],
}
createdUser, err := s.Database.CreateUser(context.Background(), createUserParams)
if err != nil {
return err
}
s.Config.SetUser(createdUser.Name)
fmt.Printf("Successfully registered user '%s':\n", createdUser.Name)
fmt.Printf(" Id => %s\n", createdUser.ID)
fmt.Printf(" CreatedAt => %s\n", createdUser.CreatedAt)
fmt.Printf(" UpdatedAt => %s\n", createdUser.UpdatedAt)
fmt.Printf("Current user set to user '%s':\n", createdUser.Name)
return nil
})
}
func resetCommand() *Command {
return newCommand("reset", func(s *state.State) error {
err := s.Database.DeleteAllUsers(context.Background())
if err != nil {
return err
}
fmt.Println("Successfully delete all users")
return nil
})
}
func usersCommand() *Command {
return newCommand("users", func(s *state.State) error {
users, err := s.Database.GetAllUsers(context.Background())
if err != nil {
return err
}
slices.SortFunc(users, func(a, b database.User) int {
return cmp.Compare(a.Name, b.Name)
})
for _, user := range users {
msg := "* %s"
if user.Name == s.Config.CurrentUserName {
msg += " (current)"
}
fmt.Printf(msg+"\n", user.Name)
}
return nil
})
}
func aggCommand() *Command {
return newCommand("agg", func(s *state.State) error {
// if len(s.Arguments) == 0 {
// return fmt.Errorf("Did not receive expected feed argument")
// }
// feed := s.Arguments[0]
//
// validUrl, err := url.Parse(feed)
//
// if err != nil {
// return fmt.Errorf("Feed '%s' is not a valid url", feed)
// }
rssFeed, err := rss.FetchFeed(context.Background(), "https://www.wagslane.dev/index.xml")
if err != nil {
return err
}
fmt.Printf("%v\n", rssFeed)
return nil
})
}
func addFeedCommand() *Command {
return newCommand("addfeed", func(s *state.State) error {
if len(s.Arguments) < 2 {
return fmt.Errorf("Did not receive expected feed name and url")
}
feedName := s.Arguments[0]
feedUrl := s.Arguments[1]
validatedFeedUrl, err := url.Parse(feedUrl)
if strings.TrimSpace(feedName) == "" {
return fmt.Errorf("Feed name cannot be empty")
}
if err != nil {
return fmt.Errorf("Feed url '%s' is not a valid url", feedUrl)
}
currentUser, err := s.GetCurrentUser(context.Background())
if err != nil {
return fmt.Errorf("Currently logged in user does not exist. Cannot add feed for non-existent user.")
}
createFeedParams := database.CreateFeedParams{
ID: uuid.New(),
UserID: currentUser.ID,
Name: feedName,
Url: validatedFeedUrl.String(),
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
}
createdFeed, err := s.Database.CreateFeed(context.Background(), createFeedParams)
if err != nil {
return err
}
fmt.Printf("Successfully added feed '%s' with url '%s'\n", createdFeed.Name, createdFeed.Url)
fmt.Printf(" Id => %s\n", createdFeed.ID)
fmt.Printf(" UserId => %s\n", createdFeed.UserID)
fmt.Printf(" CreatedAt => %s\n", createdFeed.CreatedAt)
fmt.Printf(" UpdatedAt => %s\n", createdFeed.UpdatedAt)
return nil
})
}
func feedsCommand() *Command {
return newCommand("feeds", func(s *state.State) error {
feeds, err := s.Database.GetAllFeeds(context.Background())
if err != nil {
return err
}
for _, feed := range feeds {
feedsUserName := "Unknown"
user, err := s.Database.GetUserById(context.Background(), feed.UserID)
if err == nil {
feedsUserName = user.Name
}
fmt.Printf("* %s [%s] (%s)\n", feed.Name, feed.Url, feedsUserName)
}
return nil
})
}