refactor: update code for the executor package

Changes:

- moved the command names from main to the executor package.
- moved the unknownCommandError to the executor package and renamed it
  to UnknownCommandError.
- define the command summaries within the executor package.
- replace the switch statement in main.run() with a hashmap for looking
  up the corresponding executor to the given command.
- transformed BlockExecutor to BlockOrUnblockExecutor.
- transformed FollowExecutor to FollowOrUnfollowExecutor.

Additional fixes:

- fixed flag for the version executor.
This commit is contained in:
Dan Anglin 2024-06-10 18:59:01 +01:00
parent ec706b43e9
commit c04cd7e8a5
Signed by: dananglin
GPG key ID: 0C1D44CFBEE68638
11 changed files with 290 additions and 253 deletions

View file

@ -1,13 +0,0 @@
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later
package main
type unknownCommandError struct {
subcommand string
}
func (e unknownCommandError) Error() string {
return "unknown command '" + e.subcommand + "'"
}

View file

@ -13,25 +13,6 @@ import (
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
)
const (
commandLogin string = "login"
commandVersion string = "version"
commandShow string = "show"
commandSwitch string = "switch"
commandCreate string = "create"
commandDelete string = "delete"
commandEdit string = "edit"
commandWhoami string = "whoami"
commandAdd string = "add"
commandRemove string = "remove"
commandFollow string = "follow"
commandUnfollow string = "unfollow"
commandBlock string = "block"
commandUnblock string = "unblock"
commandAccept string = "accept"
commandReject string = "reject"
)
var (
binaryVersion string
buildTime string
@ -47,33 +28,23 @@ func main() {
}
func run() error {
commandSummaries := map[string]string{
commandLogin: "Login to an account on GoToSocial",
commandVersion: "Print the application's version and build information",
commandShow: "Print details about a specified resource",
commandSwitch: "Perform a switch operation (e.g. switch logged in accounts)",
commandCreate: "Create a specific resource",
commandDelete: "Delete a specific resource",
commandEdit: "Edit a specific resource",
commandWhoami: "Print the account that you are currently logged in to",
commandAdd: "Add a resource to another resource",
commandRemove: "Remove a resource from another resource",
commandFollow: "Follow a resource (e.g. an account)",
commandUnfollow: "Unfollow a resource (e.g. an account)",
commandBlock: "Block a resource (e.g. an account)",
commandUnblock: "Unblock a resource (e.g. an account)",
commandAccept: "Accept a request (e.g. a follow request)",
commandReject: "Reject a request (e.g. a follow request)",
}
topLevelFlags := executor.TopLevelFlags{
ConfigDir: "",
NoColor: nil,
Pager: "",
}
flag.StringVar(&topLevelFlags.ConfigDir, "config-dir", "", "Specify your config directory")
flag.BoolFunc("no-color", "Disable ANSI colour output when displaying text on screen", func(value string) error {
flag.StringVar(
&topLevelFlags.ConfigDir,
"config-dir",
"",
"Specify your config directory",
)
flag.BoolFunc(
"no-color",
"Disable ANSI colour output when displaying text on screen",
func(value string) error {
boolVal, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("unable to parse %q as a boolean: %w", value, err)
@ -83,10 +54,17 @@ func run() error {
*topLevelFlags.NoColor = boolVal
return nil
})
flag.StringVar(&topLevelFlags.Pager, "pager", "", "Specify your preferred pager to page through long outputs. This is disabled by default.")
},
)
flag.Usage = usageFunc(commandSummaries)
flag.StringVar(
&topLevelFlags.Pager,
"pager",
"",
"Specify your preferred pager to page through long outputs. This is disabled by default.",
)
flag.Usage = usageFunc(executor.CommandSummaryMap())
flag.Parse()
@ -109,127 +87,100 @@ func run() error {
command := flag.Arg(0)
args := flag.Args()[1:]
var err error
switch command {
case commandAccept:
exe := executor.NewAcceptOrRejectExecutor(
executorMap := map[string]executor.Executor{
executor.CommandAccept: executor.NewAcceptOrRejectExecutor(
topLevelFlags,
commandAccept,
commandSummaries[commandAccept],
)
err = executor.Execute(exe, args)
case commandAdd:
exe := executor.NewAddExecutor(
executor.CommandAccept,
executor.CommandSummaryLookup(executor.CommandAccept),
),
executor.CommandAdd: executor.NewAddExecutor(
topLevelFlags,
commandAdd,
commandSummaries[commandAdd],
)
err = executor.Execute(exe, args)
case commandBlock:
exe := executor.NewBlockExecutor(
executor.CommandAdd,
executor.CommandSummaryLookup(executor.CommandAdd),
),
executor.CommandBlock: executor.NewBlockOrUnblockExecutor(
topLevelFlags,
commandBlock,
commandSummaries[commandBlock],
false,
)
err = executor.Execute(exe, args)
case commandCreate:
exe := executor.NewCreateExecutor(
executor.CommandBlock,
executor.CommandSummaryLookup(executor.CommandBlock),
),
executor.CommandCreate: executor.NewCreateExecutor(
topLevelFlags,
commandCreate,
commandSummaries[commandCreate],
)
err = executor.Execute(exe, args)
case commandDelete:
exe := executor.NewDeleteExecutor(
executor.CommandCreate,
executor.CommandSummaryLookup(executor.CommandCreate),
),
executor.CommandDelete: executor.NewDeleteExecutor(
topLevelFlags,
commandDelete,
commandSummaries[commandDelete],
)
err = executor.Execute(exe, args)
case commandEdit:
exe := executor.NewEditExecutor(
executor.CommandDelete,
executor.CommandSummaryLookup(executor.CommandDelete),
),
executor.CommandEdit: executor.NewEditExecutor(
topLevelFlags,
commandEdit,
commandSummaries[commandEdit],
)
err = executor.Execute(exe, args)
case commandFollow:
exe := executor.NewFollowExecutor(
executor.CommandEdit,
executor.CommandSummaryLookup(executor.CommandEdit),
),
executor.CommandFollow: executor.NewFollowOrUnfollowExecutor(
topLevelFlags,
commandFollow,
commandSummaries[commandFollow],
false,
)
err = executor.Execute(exe, args)
case commandLogin:
exe := executor.NewLoginExecutor(
executor.CommandFollow,
executor.CommandSummaryLookup(executor.CommandFollow),
),
executor.CommandLogin: executor.NewLoginExecutor(
topLevelFlags,
commandLogin,
commandSummaries[commandLogin],
)
err = executor.Execute(exe, args)
case commandReject:
exe := executor.NewAcceptOrRejectExecutor(
executor.CommandLogin,
executor.CommandSummaryLookup(executor.CommandLogin),
),
executor.CommandReject: executor.NewAcceptOrRejectExecutor(
topLevelFlags,
commandReject,
commandSummaries[commandReject],
)
err = executor.Execute(exe, args)
case commandRemove:
exe := executor.NewRemoveExecutor(
executor.CommandReject,
executor.CommandSummaryLookup(executor.CommandReject),
),
executor.CommandRemove: executor.NewRemoveExecutor(
topLevelFlags,
commandRemove,
commandSummaries[commandRemove],
)
err = executor.Execute(exe, args)
case commandSwitch:
exe := executor.NewSwitchExecutor(
executor.CommandRemove,
executor.CommandSummaryLookup(executor.CommandRemove),
),
executor.CommandSwitch: executor.NewSwitchExecutor(
topLevelFlags,
commandSwitch,
commandSummaries[commandSwitch],
)
err = executor.Execute(exe, args)
case commandUnfollow:
exe := executor.NewFollowExecutor(
executor.CommandSwitch,
executor.CommandSummaryLookup(executor.CommandSwitch),
),
executor.CommandUnfollow: executor.NewFollowOrUnfollowExecutor(
topLevelFlags,
commandUnfollow,
commandSummaries[commandUnfollow],
true,
)
err = executor.Execute(exe, args)
case commandUnblock:
exe := executor.NewBlockExecutor(
executor.CommandUnfollow,
executor.CommandSummaryLookup(executor.CommandUnfollow),
),
executor.CommandUnblock: executor.NewBlockOrUnblockExecutor(
topLevelFlags,
commandUnblock,
commandSummaries[commandUnblock],
true,
)
err = executor.Execute(exe, args)
case commandShow:
exe := executor.NewShowExecutor(topLevelFlags, commandShow, commandSummaries[commandShow])
err = executor.Execute(exe, args)
case commandVersion:
exe := executor.NewVersionExecutor(
commandVersion,
commandSummaries[commandVersion],
executor.CommandUnblock,
executor.CommandSummaryLookup(executor.CommandUnblock),
),
executor.CommandShow: executor.NewShowExecutor(
topLevelFlags,
executor.CommandShow,
executor.CommandSummaryLookup(executor.CommandShow),
),
executor.CommandVersion: executor.NewVersionExecutor(
executor.CommandVersion,
executor.CommandSummaryLookup(executor.CommandVersion),
binaryVersion,
buildTime,
goVersion,
gitCommit,
)
err = executor.Execute(exe, args)
case commandWhoami:
exe := executor.NewWhoAmIExecutor(topLevelFlags, commandWhoami, commandSummaries[commandWhoami])
err = executor.Execute(exe, args)
default:
flag.Usage()
return unknownCommandError{command}
),
executor.CommandWhoami: executor.NewWhoAmIExecutor(
topLevelFlags,
executor.CommandWhoami,
executor.CommandSummaryLookup(executor.CommandWhoami),
),
}
if err != nil {
exe, ok := executorMap[command]
if !ok {
flag.Usage()
return executor.UnknownCommandError{Command: command}
}
if err := executor.Execute(exe, args); err != nil {
return fmt.Errorf("(%s) %w", command, err)
}

View file

@ -17,7 +17,7 @@ type AcceptOrRejectExecutor struct {
topLevelFlags TopLevelFlags
resourceType string
accountName string
action string
command string
}
func NewAcceptOrRejectExecutor(tlf TopLevelFlags, name, summary string) *AcceptOrRejectExecutor {
@ -25,7 +25,7 @@ func NewAcceptOrRejectExecutor(tlf TopLevelFlags, name, summary string) *AcceptO
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
topLevelFlags: tlf,
action: name,
command: name,
}
acceptExe.StringVar(&acceptExe.resourceType, flagType, "", "Specify the type of resource to accept or reject")
@ -60,10 +60,10 @@ func (a *AcceptOrRejectExecutor) acceptOrRejectFollowRequest(gtsClient *client.C
return fmt.Errorf("received an error while getting the account ID: %w", err)
}
switch a.action {
case "accept":
switch a.command {
case CommandAccept:
return a.acceptFollowRequest(gtsClient, accountID)
case "reject":
case CommandReject:
return a.rejectFollowRequest(gtsClient, accountID)
default:
return nil

View file

@ -11,21 +11,21 @@ import (
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
)
type BlockExecutor struct {
type BlockOrUnblockExecutor struct {
*flag.FlagSet
topLevelFlags TopLevelFlags
resourceType string
accountName string
unblock bool
command string
}
func NewBlockExecutor(tlf TopLevelFlags, name, summary string, unblock bool) *BlockExecutor {
blockExe := BlockExecutor{
func NewBlockOrUnblockExecutor(tlf TopLevelFlags, name, summary string) *BlockOrUnblockExecutor {
blockExe := BlockOrUnblockExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
topLevelFlags: tlf,
unblock: unblock,
command: name,
}
blockExe.StringVar(&blockExe.resourceType, flagType, "", "Specify the type of resource to block or unblock")
@ -36,9 +36,9 @@ func NewBlockExecutor(tlf TopLevelFlags, name, summary string, unblock bool) *Bl
return &blockExe
}
func (b *BlockExecutor) Execute() error {
func (b *BlockOrUnblockExecutor) Execute() error {
funcMap := map[string]func(*client.Client) error{
resourceAccount: b.blockAccount,
resourceAccount: b.blockOrUnblockAccount,
}
doFunc, ok := funcMap[b.resourceType]
@ -54,16 +54,23 @@ func (b *BlockExecutor) Execute() error {
return doFunc(gtsClient)
}
func (b *BlockExecutor) blockAccount(gtsClient *client.Client) error {
func (b *BlockOrUnblockExecutor) blockOrUnblockAccount(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, b.accountName, b.topLevelFlags.ConfigDir)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}
if b.unblock {
switch b.command {
case CommandBlock:
return b.blockAccount(gtsClient, accountID)
case CommandUnblock:
return b.unblockAccount(gtsClient, accountID)
default:
return nil
}
}
func (b *BlockOrUnblockExecutor) blockAccount(gtsClient *client.Client, accountID string) error {
if err := gtsClient.BlockAccount(accountID); err != nil {
return fmt.Errorf("unable to block the account: %w", err)
}
@ -73,7 +80,7 @@ func (b *BlockExecutor) blockAccount(gtsClient *client.Client) error {
return nil
}
func (b *BlockExecutor) unblockAccount(gtsClient *client.Client, accountID string) error {
func (b *BlockOrUnblockExecutor) unblockAccount(gtsClient *client.Client, accountID string) error {
if err := gtsClient.UnblockAccount(accountID); err != nil {
return fmt.Errorf("unable to unblock the account: %w", err)
}

View file

@ -0,0 +1,73 @@
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later
package executor
const (
CommandAccept string = "accept"
CommandAdd string = "add"
CommandBlock string = "block"
CommandCreate string = "create"
CommandDelete string = "delete"
CommandEdit string = "edit"
CommandFollow string = "follow"
CommandLogin string = "login"
CommandReject string = "reject"
CommandRemove string = "remove"
CommandShow string = "show"
CommandSwitch string = "switch"
CommandUnblock string = "unblock"
CommandUnfollow string = "unfollow"
CommandVersion string = "version"
CommandWhoami string = "whoami"
commandAcceptSummary string = "Accept a request (e.g. a follow request)"
commandAddSummary string = "Add a resource to another resource"
commandBlockSummary string = "Block a resource (e.g. an account)"
commandCreateSummary string = "Create a specific resource"
commandDeleteSummary string = "Delete a specific resource"
commandEditSummary string = "Edit a specific resource"
commandFollowSummary string = "Follow a resource (e.g. an account)"
commandLoginSummary string = "Login to an account on GoToSocial"
commandRejectSummary string = "Reject a request (e.g. a follow request)"
commandRemoveSummary string = "Remove a resource from another resource"
commandShowSummary string = "Print details about a specified resource"
commandSwitchSummary string = "Perform a switch operation (e.g. switch logged in accounts)"
commandUnblockSummary string = "Unblock a resource (e.g. an account)"
commandUnfollowSummary string = "Unfollow a resource (e.g. an account)"
commandVersionSummary string = "Print the application's version and build information"
commandWhoamiSummary string = "Print the account that you are currently logged in to"
)
func CommandSummaryMap() map[string]string {
return map[string]string{
CommandAccept: commandAcceptSummary,
CommandAdd: commandAddSummary,
CommandBlock: commandBlockSummary,
CommandCreate: commandCreateSummary,
CommandDelete: commandDeleteSummary,
CommandEdit: commandEditSummary,
CommandFollow: commandFollowSummary,
CommandLogin: commandLoginSummary,
CommandReject: commandRejectSummary,
CommandRemove: commandRemoveSummary,
CommandShow: commandShowSummary,
CommandSwitch: commandSwitchSummary,
CommandUnblock: commandUnblockSummary,
CommandUnfollow: commandUnfollowSummary,
CommandVersion: commandVersionSummary,
CommandWhoami: commandWhoamiSummary,
}
}
func CommandSummaryLookup(command string) string {
commandMap := CommandSummaryMap()
summary, ok := commandMap[command]
if !ok {
return "This command does not have a summary"
}
return summary
}

View file

@ -1,54 +0,0 @@
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later
package executor
const (
flagAccountName = "account-name"
flagBrowser = "browser"
flagContentType = "content-type"
flagContent = "content"
flagEnableFederation = "enable-federation"
flagEnableLikes = "enable-likes"
flagEnableReplies = "enable-replies"
flagEnableReposts = "enable-reposts"
flagFrom = "from"
flagFromFile = "from-file"
flagInstance = "instance"
flagLanguage = "language"
flagLimit = "limit"
flagListID = "list-id"
flagListTitle = "list-title"
flagListRepliesPolicy = "list-replies-policy"
flagMyAccount = "my-account"
flagNotify = "notify"
flagSensitive = "sensitive"
flagSkipRelationship = "skip-relationship"
flagShowPreferences = "show-preferences"
flagShowReposts = "show-reposts"
flagSpoilerText = "spoiler-text"
flagStatusID = "status-id"
flagTag = "tag"
flagTimelineCategory = "timeline-category"
flagTo = "to"
flagType = "type"
flagVisibility = "visibility"
resourceAccount = "account"
resourceBlocked = "blocked"
resourceBookmarks = "bookmarks"
resourceBoost = "boost"
resourceFollowers = "followers"
resourceFollowing = "following"
resourceFollowRequest = "follow-request"
resourceInstance = "instance"
resourceLike = "like"
resourceLiked = "liked"
resourceList = "list"
resourceNote = "note"
resourceStatus = "status"
resourceStar = "star"
resourceStarred = "starred"
resourceTimeline = "timeline"
)

View file

@ -58,3 +58,11 @@ func (e EmptyContentError) Error() string {
return message
}
type UnknownCommandError struct {
Command string
}
func (e UnknownCommandError) Error() string {
return "unknown command '" + e.Command + "'"
}

View file

@ -6,6 +6,39 @@ package executor
import "strings"
const (
flagAccountName = "account-name"
flagBrowser = "browser"
flagContentType = "content-type"
flagContent = "content"
flagEnableFederation = "enable-federation"
flagEnableLikes = "enable-likes"
flagEnableReplies = "enable-replies"
flagEnableReposts = "enable-reposts"
flagFrom = "from"
flagFromFile = "from-file"
flagFull = "full"
flagInstance = "instance"
flagLanguage = "language"
flagLimit = "limit"
flagListID = "list-id"
flagListTitle = "list-title"
flagListRepliesPolicy = "list-replies-policy"
flagMyAccount = "my-account"
flagNotify = "notify"
flagSensitive = "sensitive"
flagSkipRelationship = "skip-relationship"
flagShowPreferences = "show-preferences"
flagShowReposts = "show-reposts"
flagSpoilerText = "spoiler-text"
flagStatusID = "status-id"
flagTag = "tag"
flagTimelineCategory = "timeline-category"
flagTo = "to"
flagType = "type"
flagVisibility = "visibility"
)
type AccountNames []string
func (a *AccountNames) String() string {

View file

@ -11,7 +11,7 @@ import (
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
)
type FollowExecutor struct {
type FollowOrUnfollowExecutor struct {
*flag.FlagSet
topLevelFlags TopLevelFlags
@ -19,14 +19,15 @@ type FollowExecutor struct {
accountName string
showReposts bool
notify bool
unfollow bool
action string
}
func NewFollowExecutor(tlf TopLevelFlags, name, summary string, unfollow bool) *FollowExecutor {
command := FollowExecutor{
func NewFollowOrUnfollowExecutor(tlf TopLevelFlags, name, summary string) *FollowOrUnfollowExecutor {
command := FollowOrUnfollowExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
unfollow: unfollow,
topLevelFlags: tlf,
action: name,
}
command.StringVar(&command.resourceType, flagType, "", "Specify the type of resource to follow")
@ -39,17 +40,17 @@ func NewFollowExecutor(tlf TopLevelFlags, name, summary string, unfollow bool) *
return &command
}
func (c *FollowExecutor) Execute() error {
func (f *FollowOrUnfollowExecutor) Execute() error {
funcMap := map[string]func(*client.Client) error{
resourceAccount: c.followAccount,
resourceAccount: f.followOrUnfollowAccount,
}
doFunc, ok := funcMap[c.resourceType]
doFunc, ok := funcMap[f.resourceType]
if !ok {
return UnsupportedTypeError{resourceType: c.resourceType}
return UnsupportedTypeError{resourceType: f.resourceType}
}
gtsClient, err := client.NewClientFromConfig(c.topLevelFlags.ConfigDir)
gtsClient, err := client.NewClientFromConfig(f.topLevelFlags.ConfigDir)
if err != nil {
return fmt.Errorf("unable to create the GoToSocial client: %w", err)
}
@ -57,20 +58,27 @@ func (c *FollowExecutor) Execute() error {
return doFunc(gtsClient)
}
func (c *FollowExecutor) followAccount(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, c.accountName, c.topLevelFlags.ConfigDir)
func (f *FollowOrUnfollowExecutor) followOrUnfollowAccount(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, f.accountName, f.topLevelFlags.ConfigDir)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}
if c.unfollow {
return c.unfollowAccount(gtsClient, accountID)
switch f.action {
case CommandFollow:
return f.followAccount(gtsClient, accountID)
case CommandUnfollow:
return f.unfollowAccount(gtsClient, accountID)
default:
return nil
}
}
func (f *FollowOrUnfollowExecutor) followAccount(gtsClient *client.Client, accountID string) error {
form := client.FollowAccountForm{
AccountID: accountID,
ShowReposts: c.showReposts,
Notify: c.notify,
ShowReposts: f.showReposts,
Notify: f.notify,
}
if err := gtsClient.FollowAccount(form); err != nil {
@ -82,7 +90,7 @@ func (c *FollowExecutor) followAccount(gtsClient *client.Client) error {
return nil
}
func (c *FollowExecutor) unfollowAccount(gtsClient *client.Client, accountID string) error {
func (f *FollowOrUnfollowExecutor) unfollowAccount(gtsClient *client.Client, accountID string) error {
if err := gtsClient.UnfollowAccount(accountID); err != nil {
return fmt.Errorf("unable to unfollow the account: %w", err)
}

View file

@ -0,0 +1,24 @@
// SPDX-FileCopyrightText: 2024 Dan Anglin <d.n.i.anglin@gmail.com>
//
// SPDX-License-Identifier: GPL-3.0-or-later
package executor
const(
resourceAccount = "account"
resourceBlocked = "blocked"
resourceBookmarks = "bookmarks"
resourceBoost = "boost"
resourceFollowers = "followers"
resourceFollowing = "following"
resourceFollowRequest = "follow-request"
resourceInstance = "instance"
resourceLike = "like"
resourceLiked = "liked"
resourceList = "list"
resourceNote = "note"
resourceStatus = "status"
resourceStar = "star"
resourceStarred = "starred"
resourceTimeline = "timeline"
)

View file

@ -30,7 +30,7 @@ func NewVersionExecutor(name, summary, binaryVersion, buildTime, goVersion, gitC
showFullVersion: false,
}
command.BoolVar(&command.showFullVersion, "Full", false, "prints the full build information")
command.BoolVar(&command.showFullVersion, flagFull, false, "prints the full build information")
command.Usage = commandUsageFunc(name, summary, command.FlagSet)