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 }) }