From dd6c21afe88b1a9d692d4ed5781f7351c46f4895 Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Sat, 17 Aug 2024 11:23:21 +0100 Subject: [PATCH] refactor: clean up errors - Created generic Error types to remove the need to import the errors package. - Used the generic Error types in place of the single use custom Error types that have no fields. - Created new Error types where necessary. PR: https://codeflow.dananglin.me.uk/apollo/enbas/pulls/50 --- internal/client/client.go | 7 +- internal/client/errors.go | 19 ++++++ internal/client/token.go | 5 +- internal/executor/add.go | 31 +++++---- internal/executor/create.go | 62 +++++++++++------- internal/executor/delete.go | 3 +- internal/executor/edit.go | 30 +++++---- internal/executor/errors.go | 111 ++++++++++++++++---------------- internal/executor/execute.go | 2 +- internal/executor/flags.go | 1 - internal/executor/mute.go | 4 +- internal/executor/remove.go | 16 ++--- internal/executor/show.go | 12 ++-- internal/executor/unmute.go | 4 +- internal/printer/printer.go | 4 +- internal/utilities/utilities.go | 2 +- 16 files changed, 174 insertions(+), 139 deletions(-) diff --git a/internal/client/client.go b/internal/client/client.go index b4ea7ff..b47bac6 100644 --- a/internal/client/client.go +++ b/internal/client/client.go @@ -79,10 +79,9 @@ func (g *Client) DownloadMedia(url, path string) error { defer response.Body.Close() if response.StatusCode != http.StatusOK { - return fmt.Errorf( - "did not receive an OK response from the GoToSocial server: got %d", - response.StatusCode, - ) + return BadStatusCodeError{ + statusCode: response.StatusCode, + } } file, err := os.Create(path) diff --git a/internal/client/errors.go b/internal/client/errors.go index b2f2617..24c729c 100644 --- a/internal/client/errors.go +++ b/internal/client/errors.go @@ -30,3 +30,22 @@ func (e ResponseError) Error() string { e.Message, ) } + +type BadStatusCodeError struct { + statusCode int +} + +func (e BadStatusCodeError) Error() string { + return fmt.Sprintf( + "did not receive an OK response from the GoToSocial server: got %d", + e.statusCode, + ) +} + +type Error struct { + message string +} + +func (e Error) Error() string { + return e.message +} diff --git a/internal/client/token.go b/internal/client/token.go index b2902f4..f23a242 100644 --- a/internal/client/token.go +++ b/internal/client/token.go @@ -3,15 +3,12 @@ package client import ( "bytes" "encoding/json" - "errors" "fmt" "net/http" "codeflow.dananglin.me.uk/apollo/enbas/internal" ) -var errEmptyAccessToken = errors.New("received an empty access token") - type tokenRequest struct { RedirectURI string `json:"redirect_uri"` ClientID string `json:"client_id"` @@ -59,7 +56,7 @@ func (g *Client) UpdateToken(code string) error { } if response.AccessToken == "" { - return errEmptyAccessToken + return Error{"received an empty access token"} } g.Authentication.AccessToken = response.AccessToken diff --git a/internal/executor/add.go b/internal/executor/add.go index f18a733..cb87992 100644 --- a/internal/executor/add.go +++ b/internal/executor/add.go @@ -1,7 +1,6 @@ package executor import ( - "errors" "fmt" "codeflow.dananglin.me.uk/apollo/enbas/internal/client" @@ -40,8 +39,8 @@ func (a *AddExecutor) addToList(gtsClient *client.Client) error { doFunc, ok := funcMap[a.resourceType] if !ok { return UnsupportedAddOperationError{ - ResourceType: a.resourceType, - AddToResourceType: a.toResourceType, + resourceType: a.resourceType, + addToResourceType: a.toResourceType, } } @@ -71,7 +70,7 @@ func (a *AddExecutor) addAccountsToList(gtsClient *client.Client) error { } if !relationship.Following { - return NotFollowingError{Account: accounts[ind].Acct} + return NotFollowingError{account: accounts[ind].Acct} } accountIDs[ind] = accounts[ind].ID @@ -94,8 +93,8 @@ func (a *AddExecutor) addToAccount(gtsClient *client.Client) error { doFunc, ok := funcMap[a.resourceType] if !ok { return UnsupportedAddOperationError{ - ResourceType: a.resourceType, - AddToResourceType: a.toResourceType, + resourceType: a.resourceType, + addToResourceType: a.toResourceType, } } @@ -109,7 +108,7 @@ func (a *AddExecutor) addNoteToAccount(gtsClient *client.Client) error { } if a.content == "" { - return errors.New("please add content to the status that you want to create") + return Error{"please add content to the note you want to add"} } if err := gtsClient.SetPrivateNote(accountID, a.content); err != nil { @@ -129,8 +128,8 @@ func (a *AddExecutor) addToBookmarks(gtsClient *client.Client) error { doFunc, ok := funcMap[a.resourceType] if !ok { return UnsupportedAddOperationError{ - ResourceType: a.resourceType, - AddToResourceType: a.toResourceType, + resourceType: a.resourceType, + addToResourceType: a.toResourceType, } } @@ -166,8 +165,8 @@ func (a *AddExecutor) addToStatus(gtsClient *client.Client) error { doFunc, ok := funcMap[a.resourceType] if !ok { return UnsupportedAddOperationError{ - ResourceType: a.resourceType, - AddToResourceType: a.toResourceType, + resourceType: a.resourceType, + addToResourceType: a.toResourceType, } } @@ -196,7 +195,7 @@ func (a *AddExecutor) addBoostToStatus(gtsClient *client.Client) error { func (a *AddExecutor) addVoteToStatus(gtsClient *client.Client) error { if a.votes.Empty() { - return NoVotesError{} + return Error{"please add your vote(s) to the poll"} } status, err := gtsClient.GetStatus(a.statusID) @@ -205,15 +204,15 @@ func (a *AddExecutor) addVoteToStatus(gtsClient *client.Client) error { } if status.Poll == nil { - return NoPollInStatusError{} + return Error{"this status does not have a poll"} } if status.Poll.Expired { - return PollClosedError{} + return Error{"this poll is closed"} } if !status.Poll.Multiple && !a.votes.ExpectedLength(1) { - return MultipleChoiceError{} + return Error{"this poll does not allow multiple choices"} } myAccountID, err := getAccountID(gtsClient, true, nil) @@ -222,7 +221,7 @@ func (a *AddExecutor) addVoteToStatus(gtsClient *client.Client) error { } if status.Account.ID == myAccountID { - return PollOwnerVoteError{} + return Error{"you cannot vote in your own poll"} } pollID := status.Poll.ID diff --git a/internal/executor/create.go b/internal/executor/create.go index a49bb09..10d8367 100644 --- a/internal/executor/create.go +++ b/internal/executor/create.go @@ -1,7 +1,6 @@ package executor import ( - "errors" "fmt" "codeflow.dananglin.me.uk/apollo/enbas/internal/client" @@ -72,27 +71,35 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { if !c.mediaFiles.Empty() { descriptionsExists := false focusValuesExists := false - expectedLength := len(c.mediaFiles) - mediaDescriptions := make([]string, expectedLength) + numMediaFiles := len(c.mediaFiles) + mediaDescriptions := make([]string, numMediaFiles) if !c.mediaDescriptions.Empty() { descriptionsExists = true - if !c.mediaDescriptions.ExpectedLength(expectedLength) { - return errors.New("the number of media descriptions does not match the number of media files") + if !c.mediaDescriptions.ExpectedLength(numMediaFiles) { + return MismatchedNumMediaValuesError{ + valueType: "media descriptions", + numValues: len(c.mediaDescriptions), + numMediaFiles: numMediaFiles, + } } } if !c.mediaFocusValues.Empty() { focusValuesExists = true - if !c.mediaFocusValues.ExpectedLength(expectedLength) { - return errors.New("the number of media focus values does not match the number of media files") + if !c.mediaFocusValues.ExpectedLength(numMediaFiles) { + return MismatchedNumMediaValuesError{ + valueType: "media focus values", + numValues: len(c.mediaFocusValues), + numMediaFiles: numMediaFiles, + } } } if descriptionsExists { - for ind := 0; ind < expectedLength; ind++ { + for ind := 0; ind < numMediaFiles; ind++ { mediaDesc, err := utilities.ReadContents(c.mediaDescriptions[ind]) if err != nil { return fmt.Errorf("unable to read the contents from %s: %w", c.mediaDescriptions[ind], err) @@ -102,7 +109,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { } } - for ind := 0; ind < expectedLength; ind++ { + for ind := 0; ind < numMediaFiles; ind++ { var ( mediaFile string description string @@ -133,7 +140,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { } if c.content == "" && len(attachmentIDs) == 0 { - return errors.New("please add content to the status that you want to create") + return Error{"please add content to the status that you want to create"} } content, err := utilities.ReadContents(c.content) @@ -144,7 +151,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { numAttachmentIDs := len(attachmentIDs) if c.addPoll && numAttachmentIDs > 0 { - return fmt.Errorf("attaching media to a poll is not allowed") + return Error{"attaching media to a poll is not allowed"} } preferences, err := gtsClient.GetUserPreferences() @@ -202,7 +209,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { if c.addPoll { if len(c.pollOptions) == 0 { - return NoPollOptionError{} + return Error{"no options were provided for this poll"} } poll := client.CreateStatusPollForm{ @@ -227,28 +234,33 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { func (c *CreateExecutor) createMediaAttachment(gtsClient *client.Client) error { expectedNumValues := 1 + if !c.mediaFiles.ExpectedLength(expectedNumValues) { - return fmt.Errorf( - "received an unexpected number of media files: want %d", - expectedNumValues, - ) + return UnexpectedNumValuesError{ + name: "media files", + expected: expectedNumValues, + actual: len(c.mediaFiles), + } } description := "" if !c.mediaDescriptions.Empty() { if !c.mediaDescriptions.ExpectedLength(expectedNumValues) { - return fmt.Errorf( - "received an unexpected number of media descriptions: want %d", - expectedNumValues, - ) + return UnexpectedNumValuesError{ + name: "media descriptions", + expected: expectedNumValues, + actual: len(c.mediaDescriptions), + } } var err error + description, err = utilities.ReadContents(c.mediaDescriptions[0]) if err != nil { return fmt.Errorf( "unable to read the contents from %s: %w", c.mediaDescriptions[0], + err, ) } } @@ -256,11 +268,13 @@ func (c *CreateExecutor) createMediaAttachment(gtsClient *client.Client) error { focus := "" if !c.mediaFocusValues.Empty() { if !c.mediaFocusValues.ExpectedLength(expectedNumValues) { - return fmt.Errorf( - "received an unexpected number of media focus values: want %d", - expectedNumValues, - ) + return UnexpectedNumValuesError{ + name: "media focus values", + expected: expectedNumValues, + actual: len(c.mediaFocusValues), + } } + focus = c.mediaFocusValues[0] } diff --git a/internal/executor/delete.go b/internal/executor/delete.go index 8bfe8a9..5ddd00f 100644 --- a/internal/executor/delete.go +++ b/internal/executor/delete.go @@ -1,7 +1,6 @@ package executor import ( - "errors" "fmt" "path/filepath" @@ -62,7 +61,7 @@ func (d *DeleteExecutor) deleteStatus(gtsClient *client.Client) error { } if status.Account.ID != myAccountID { - return errors.New("unable to delete the status because the status does not belong to you") + return Error{"unable to delete the status because the status does not belong to you"} } text, err := gtsClient.DeleteStatus(d.statusID) diff --git a/internal/executor/edit.go b/internal/executor/edit.go index b6097de..f2247bf 100644 --- a/internal/executor/edit.go +++ b/internal/executor/edit.go @@ -69,10 +69,11 @@ func (e *EditExecutor) editMediaAttachment(gtsClient *client.Client) error { expectedNumValues := 1 if !e.attachmentIDs.ExpectedLength(expectedNumValues) { - return fmt.Errorf( - "received an unexpected number of media attachment IDs: want %d", - expectedNumValues, - ) + return UnexpectedNumValuesError{ + name: "media attachment IDs", + expected: expectedNumValues, + actual: len(e.attachmentIDs), + } } attachment, err := gtsClient.GetMediaAttachment(e.attachmentIDs[0]) @@ -83,18 +84,21 @@ func (e *EditExecutor) editMediaAttachment(gtsClient *client.Client) error { description := attachment.Description if !e.mediaDescriptions.Empty() { if !e.mediaDescriptions.ExpectedLength(expectedNumValues) { - return fmt.Errorf( - "received an unexpected number of media descriptions: want %d", - expectedNumValues, - ) + return UnexpectedNumValuesError{ + name: "media description", + expected: expectedNumValues, + actual: len(e.mediaDescriptions), + } } var err error + description, err = utilities.ReadContents(e.mediaDescriptions[0]) if err != nil { return fmt.Errorf( "unable to read the contents from %s: %w", e.mediaDescriptions[0], + err, ) } } @@ -102,11 +106,13 @@ func (e *EditExecutor) editMediaAttachment(gtsClient *client.Client) error { focus := fmt.Sprintf("%f,%f", attachment.Meta.Focus.X, attachment.Meta.Focus.Y) if !e.mediaFocusValues.Empty() { if !e.mediaFocusValues.ExpectedLength(expectedNumValues) { - return fmt.Errorf( - "received an unexpected number of media focus values: want %d", - expectedNumValues, - ) + return UnexpectedNumValuesError{ + name: "media focus values", + expected: expectedNumValues, + actual: len(e.mediaFocusValues), + } } + focus = e.mediaFocusValues[0] } diff --git a/internal/executor/errors.go b/internal/executor/errors.go index 45edce8..46a53de 100644 --- a/internal/executor/errors.go +++ b/internal/executor/errors.go @@ -1,5 +1,15 @@ package executor +import "fmt" + +type Error struct { + message string +} + +func (e Error) Error() string { + return e.message +} + type FlagNotSetError struct { flagText string } @@ -23,94 +33,87 @@ func (e NoAccountSpecifiedError) Error() string { } type UnsupportedAddOperationError struct { - ResourceType string - AddToResourceType string + resourceType string + addToResourceType string } func (e UnsupportedAddOperationError) Error() string { return "adding '" + - e.ResourceType + + e.resourceType + "' to '" + - e.AddToResourceType + + e.addToResourceType + "' is not supported" } type UnsupportedRemoveOperationError struct { - ResourceType string - RemoveFromResourceType string + resourceType string + removeFromResourceType string } func (e UnsupportedRemoveOperationError) Error() string { return "removing '" + - e.ResourceType + + e.resourceType + "' from '" + - e.RemoveFromResourceType + + e.removeFromResourceType + "' is not supported" } type UnsupportedShowOperationError struct { - ResourceType string - ShowFromResourceType string + resourceType string + showFromResourceType string } func (e UnsupportedShowOperationError) Error() string { return "showing '" + - e.ResourceType + + e.resourceType + "' from '" + - e.ShowFromResourceType + + e.showFromResourceType + "' is not supported" } type UnknownCommandError struct { - Command string + command string } func (e UnknownCommandError) Error() string { - return "unknown command '" + e.Command + "'" -} - -type PollClosedError struct{} - -func (e PollClosedError) Error() string { - return "this poll is closed" -} - -type MultipleChoiceError struct{} - -func (e MultipleChoiceError) Error() string { - return "this poll does not allow multiple choices" -} - -type NoPollOptionError struct{} - -func (e NoPollOptionError) Error() string { - return "no options were provided for this poll, please use the --" + - flagPollOption + - " flag to add options to the poll" -} - -type NoVotesError struct{} - -func (e NoVotesError) Error() string { - return "no votes were made, please add your vote(s) using the --vote flag" -} - -type NoPollInStatusError struct{} - -func (e NoPollInStatusError) Error() string { - return "this status does not have a poll" -} - -type PollOwnerVoteError struct{} - -func (e PollOwnerVoteError) Error() string { - return "you cannot vote in your own poll" + return "unknown command '" + e.command + "'" } type NotFollowingError struct { - Account string + account string } func (e NotFollowingError) Error() string { - return "you are not following " + e.Account + return "you are not following " + e.account +} + +type MismatchedNumMediaValuesError struct { + valueType string + numValues int + numMediaFiles int +} + +func (e MismatchedNumMediaValuesError) Error() string { + return fmt.Sprintf( + "unexpected number of %s: received %d media files but got %d %s", + e.valueType, + e.numMediaFiles, + e.numValues, + e.valueType, + ) +} + +type UnexpectedNumValuesError struct { + name string + actual int + expected int +} + +func (e UnexpectedNumValuesError) Error() string { + return fmt.Sprintf( + "received an unexpected number of %s: received %d, expected %d", + e.name, + e.actual, + e.expected, + ) } diff --git a/internal/executor/execute.go b/internal/executor/execute.go index 8ffa917..d42d3c2 100644 --- a/internal/executor/execute.go +++ b/internal/executor/execute.go @@ -145,7 +145,7 @@ func execute( exe, ok := executorMap[command] if !ok { - return UnknownCommandError{Command: command} + return UnknownCommandError{command: command} } if err := exe.Parse(args); err != nil { diff --git a/internal/executor/flags.go b/internal/executor/flags.go index efd07a0..8f22c75 100644 --- a/internal/executor/flags.go +++ b/internal/executor/flags.go @@ -7,7 +7,6 @@ const ( flagInstance = "instance" flagListID = "list-id" flagListTitle = "list-title" - flagPollOption = "poll-option" flagStatusID = "status-id" flagTag = "tag" flagTo = "to" diff --git a/internal/executor/mute.go b/internal/executor/mute.go index f8cd30d..1c69131 100644 --- a/internal/executor/mute.go +++ b/internal/executor/mute.go @@ -1,7 +1,6 @@ package executor import ( - "errors" "fmt" "codeflow.dananglin.me.uk/apollo/enbas/internal/client" @@ -69,13 +68,14 @@ func (m *MuteExecutor) muteStatus(gtsClient *client.Client) error { for _, mention := range status.Mentions { if mention.ID == myAccountID { canMute = true + break } } } if !canMute { - return errors.New("unable to mute the status because you are not the owner and you are not mentioned in it") + return Error{"unable to mute the status because the status does not belong to you nor are you mentioned in it"} } if err := gtsClient.MuteStatus(m.statusID); err != nil { diff --git a/internal/executor/remove.go b/internal/executor/remove.go index 69aa841..bd1bd74 100644 --- a/internal/executor/remove.go +++ b/internal/executor/remove.go @@ -39,8 +39,8 @@ func (r *RemoveExecutor) removeFromList(gtsClient *client.Client) error { doFunc, ok := funcMap[r.resourceType] if !ok { return UnsupportedRemoveOperationError{ - ResourceType: r.resourceType, - RemoveFromResourceType: r.fromResourceType, + resourceType: r.resourceType, + removeFromResourceType: r.fromResourceType, } } @@ -84,8 +84,8 @@ func (r *RemoveExecutor) removeFromAccount(gtsClient *client.Client) error { doFunc, ok := funcMap[r.resourceType] if !ok { return UnsupportedRemoveOperationError{ - ResourceType: r.resourceType, - RemoveFromResourceType: r.fromResourceType, + resourceType: r.resourceType, + removeFromResourceType: r.fromResourceType, } } @@ -115,8 +115,8 @@ func (r *RemoveExecutor) removeFromBookmarks(gtsClient *client.Client) error { doFunc, ok := funcMap[r.resourceType] if !ok { return UnsupportedRemoveOperationError{ - ResourceType: r.resourceType, - RemoveFromResourceType: r.fromResourceType, + resourceType: r.resourceType, + removeFromResourceType: r.fromResourceType, } } @@ -151,8 +151,8 @@ func (r *RemoveExecutor) removeFromStatus(gtsClient *client.Client) error { doFunc, ok := funcMap[r.resourceType] if !ok { return UnsupportedRemoveOperationError{ - ResourceType: r.resourceType, - RemoveFromResourceType: r.fromResourceType, + resourceType: r.resourceType, + removeFromResourceType: r.fromResourceType, } } diff --git a/internal/executor/show.go b/internal/executor/show.go index 665386a..2c86c6b 100644 --- a/internal/executor/show.go +++ b/internal/executor/show.go @@ -257,8 +257,8 @@ func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error { doFunc, ok := funcMap[s.fromResourceType] if !ok { return UnsupportedShowOperationError{ - ResourceType: s.resourceType, - ShowFromResourceType: s.fromResourceType, + resourceType: s.resourceType, + showFromResourceType: s.fromResourceType, } } @@ -297,8 +297,8 @@ func (s *ShowExecutor) showFollowing(gtsClient *client.Client) error { doFunc, ok := funcMap[s.fromResourceType] if !ok { return UnsupportedShowOperationError{ - ResourceType: s.resourceType, - ShowFromResourceType: s.fromResourceType, + resourceType: s.resourceType, + showFromResourceType: s.fromResourceType, } } @@ -444,8 +444,8 @@ func (s *ShowExecutor) showMedia(gtsClient *client.Client) error { doFunc, ok := funcMap[s.fromResourceType] if !ok { return UnsupportedShowOperationError{ - ResourceType: s.resourceType, - ShowFromResourceType: s.fromResourceType, + resourceType: s.resourceType, + showFromResourceType: s.fromResourceType, } } diff --git a/internal/executor/unmute.go b/internal/executor/unmute.go index d643e46..ffb7693 100644 --- a/internal/executor/unmute.go +++ b/internal/executor/unmute.go @@ -1,7 +1,6 @@ package executor import ( - "errors" "fmt" "codeflow.dananglin.me.uk/apollo/enbas/internal/client" @@ -64,13 +63,14 @@ func (m *UnmuteExecutor) unmuteStatus(gtsClient *client.Client) error { for _, mention := range status.Mentions { if mention.ID == myAccountID { canUnmute = true + break } } } if !canUnmute { - return errors.New("unable to unmute the status because you are not the owner and you are not mentioned in it") + return Error{"unable to unmute the status because the status does not belong to you nor are you mentioned in it"} } if err := gtsClient.UnmuteStatus(m.statusID); err != nil { diff --git a/internal/printer/printer.go b/internal/printer/printer.go index c2eadc0..9c15ae2 100644 --- a/internal/printer/printer.go +++ b/internal/printer/printer.go @@ -143,11 +143,11 @@ func (p Printer) fullDisplayNameFormat(displayName, acct string) string { } func (p Printer) formatDate(date time.Time) string { - return date.Local().Format(dateFormat) + return date.Local().Format(dateFormat) //nolint:gosmopolitan } func (p Printer) formatDateTime(date time.Time) string { - return date.Local().Format(dateTimeFormat) + return date.Local().Format(dateTimeFormat) //nolint:gosmopolitan } func (p Printer) print(text string) { diff --git a/internal/utilities/utilities.go b/internal/utilities/utilities.go index 3c66121..600b3f2 100644 --- a/internal/utilities/utilities.go +++ b/internal/utilities/utilities.go @@ -50,7 +50,7 @@ func OpenLink(browser, url string) error { cmd := strings.Split(browser, " ") cmd = append(cmd, url) - command := exec.Command(cmd[0], cmd[1:]...) + command := exec.Command(cmd[0], cmd[1:]...) //nolint:gosec if err := command.Start(); err != nil { return fmt.Errorf("received an error after starting the program to view the link: %w", err)