diff --git a/cmd/enbas/main.go b/cmd/enbas/main.go index d4aa348..0e27f0e 100644 --- a/cmd/enbas/main.go +++ b/cmd/enbas/main.go @@ -4,6 +4,7 @@ import ( "flag" "fmt" "os" + "strconv" "codeflow.dananglin.me.uk/apollo/enbas/internal/executor" ) @@ -57,9 +58,23 @@ func run() error { commandUnblock: "unblock a resource (e.g. an account)", } - topLevelFlags := executor.TopLevelFlags{} + topLevelFlags := executor.TopLevelFlags{ + ConfigDir: "", + NoColor: nil, + } 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) + } + + topLevelFlags.NoColor = new(bool) + *topLevelFlags.NoColor = boolVal + + return nil + }) flag.Usage = usageFunc(commandSummaries) @@ -71,6 +86,16 @@ func run() error { return nil } + // If NoColor is still unspecified, check to see if the NO_COLOR environment variable is set + if topLevelFlags.NoColor == nil { + topLevelFlags.NoColor = new(bool) + if os.Getenv("NO_COLOR") != "" { + *topLevelFlags.NoColor = true + } else { + *topLevelFlags.NoColor = false + } + } + command := flag.Arg(0) args := flag.Args()[1:] diff --git a/internal/executor/create.go b/internal/executor/create.go index c847a0c..e7f1ac5 100644 --- a/internal/executor/create.go +++ b/internal/executor/create.go @@ -112,7 +112,7 @@ func (c *CreateExecutor) createList(gtsClient *client.Client) error { } fmt.Println("Successfully created the following list:") - fmt.Printf("\n%s\n", list) + utilities.Display(list, *c.topLevelFlags.NoColor) return nil } @@ -193,7 +193,7 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { } fmt.Println("Successfully created the following status:") - fmt.Println(status) + utilities.Display(status, *c.topLevelFlags.NoColor) return nil } diff --git a/internal/executor/update.go b/internal/executor/edit.go similarity index 91% rename from internal/executor/update.go rename to internal/executor/edit.go index a3517c0..9d92bd3 100644 --- a/internal/executor/update.go +++ b/internal/executor/edit.go @@ -6,6 +6,7 @@ import ( "codeflow.dananglin.me.uk/apollo/enbas/internal/client" "codeflow.dananglin.me.uk/apollo/enbas/internal/model" + "codeflow.dananglin.me.uk/apollo/enbas/internal/utilities" ) type EditExecutor struct { @@ -40,7 +41,7 @@ func (e *EditExecutor) Execute() error { } funcMap := map[string]func(*client.Client) error{ - resourceList: e.updateList, + resourceList: e.editList, } doFunc, ok := funcMap[e.resourceType] @@ -56,7 +57,7 @@ func (e *EditExecutor) Execute() error { return doFunc(gtsClient) } -func (e *EditExecutor) updateList(gtsClient *client.Client) error { +func (e *EditExecutor) editList(gtsClient *client.Client) error { if e.listID == "" { return FlagNotSetError{flagText: flagListID} } @@ -85,7 +86,7 @@ func (e *EditExecutor) updateList(gtsClient *client.Client) error { } fmt.Println("Successfully updated the list.") - fmt.Println(updatedList) + utilities.Display(updatedList, *e.topLevelFlags.NoColor) return nil } diff --git a/internal/executor/flags.go b/internal/executor/flags.go index 9c64dc3..c11eecd 100644 --- a/internal/executor/flags.go +++ b/internal/executor/flags.go @@ -18,4 +18,5 @@ func (a *AccountNames) Set(value string) error { type TopLevelFlags struct { ConfigDir string + NoColor *bool } diff --git a/internal/executor/show.go b/internal/executor/show.go index b69ab7e..2661d0e 100644 --- a/internal/executor/show.go +++ b/internal/executor/show.go @@ -83,7 +83,7 @@ func (c *ShowExecutor) showInstance(gtsClient *client.Client) error { return fmt.Errorf("unable to retrieve the instance details; %w", err) } - fmt.Println(instance) + utilities.Display(instance, *c.topLevelFlags.NoColor) return nil } @@ -116,7 +116,7 @@ func (c *ShowExecutor) showAccount(gtsClient *client.Client) error { return nil } - fmt.Println(account) + utilities.Display(account, *c.topLevelFlags.NoColor) if !c.myAccount && !c.skipAccountRelationship { relationship, err := gtsClient.GetAccountRelationship(account.ID) @@ -124,7 +124,7 @@ func (c *ShowExecutor) showAccount(gtsClient *client.Client) error { return fmt.Errorf("unable to retrieve the relationship to this account; %w", err) } - fmt.Println(relationship) + utilities.Display(relationship, *c.topLevelFlags.NoColor) } if c.myAccount && c.showUserPreferences { @@ -133,7 +133,7 @@ func (c *ShowExecutor) showAccount(gtsClient *client.Client) error { return fmt.Errorf("unable to retrieve the user preferences; %w", err) } - fmt.Println(preferences) + utilities.Display(preferences, *c.topLevelFlags.NoColor) } return nil @@ -155,7 +155,7 @@ func (c *ShowExecutor) showStatus(gtsClient *client.Client) error { return nil } - fmt.Println(status) + utilities.Display(status, *c.topLevelFlags.NoColor) return nil } @@ -197,7 +197,7 @@ func (c *ShowExecutor) showTimeline(gtsClient *client.Client) error { return nil } - fmt.Println(timeline) + utilities.Display(timeline, *c.topLevelFlags.NoColor) return nil } @@ -226,7 +226,7 @@ func (c *ShowExecutor) showList(gtsClient *client.Client) error { list.Accounts = accountMap } - fmt.Println(list) + utilities.Display(list, *c.topLevelFlags.NoColor) return nil } @@ -243,8 +243,7 @@ func (c *ShowExecutor) showLists(gtsClient *client.Client) error { return nil } - fmt.Println(utilities.HeaderFormat("LISTS")) - fmt.Println(lists) + utilities.Display(lists, *c.topLevelFlags.NoColor) return nil } @@ -261,7 +260,7 @@ func (c *ShowExecutor) showFollowers(gtsClient *client.Client) error { } if len(followers.Accounts) > 0 { - fmt.Println(followers) + utilities.Display(followers, *c.topLevelFlags.NoColor) } else { fmt.Println("There are no followers for this account or the list is hidden.") } @@ -281,7 +280,7 @@ func (c *ShowExecutor) showFollowing(gtsClient *client.Client) error { } if len(following.Accounts) > 0 { - fmt.Println(following) + utilities.Display(following, *c.topLevelFlags.NoColor) } else { fmt.Println("This account is not following anyone or the list is hidden.") } @@ -296,7 +295,7 @@ func (c *ShowExecutor) showBlocked(gtsClient *client.Client) error { } if len(blocked.Accounts) > 0 { - fmt.Println(blocked) + utilities.Display(blocked, *c.topLevelFlags.NoColor) } else { fmt.Println("You have no blocked accounts.") } diff --git a/internal/model/account.go b/internal/model/account.go index 6264e39..083642c 100644 --- a/internal/model/account.go +++ b/internal/model/account.go @@ -59,7 +59,7 @@ type Field struct { VerifiedAt string `json:"verified_at"` } -func (a Account) String() string { +func (a Account) Display(noColor bool) string { format := ` %s (@%s) @@ -87,28 +87,28 @@ func (a Account) String() string { for _, field := range a.Fields { metadata += fmt.Sprintf( "\n %s: %s", - utilities.FieldFormat(field.Name), + utilities.FieldFormat(noColor, field.Name), utilities.StripHTMLTags(field.Value), ) } return fmt.Sprintf( format, - utilities.DisplayNameFormat(a.DisplayName), + utilities.DisplayNameFormat(noColor, a.DisplayName), a.Username, - utilities.HeaderFormat("ACCOUNT ID:"), + utilities.HeaderFormat(noColor, "ACCOUNT ID:"), a.ID, - utilities.HeaderFormat("JOINED ON:"), + utilities.HeaderFormat(noColor, "JOINED ON:"), utilities.FormatDate(a.CreatedAt), - utilities.HeaderFormat("STATS:"), - utilities.FieldFormat("Followers:"), a.FollowersCount, - utilities.FieldFormat("Following:"), a.FollowingCount, - utilities.FieldFormat("Statuses:"), a.StatusCount, - utilities.HeaderFormat("BIOGRAPHY:"), + utilities.HeaderFormat(noColor, "STATS:"), + utilities.FieldFormat(noColor, "Followers:"), a.FollowersCount, + utilities.FieldFormat(noColor, "Following:"), a.FollowingCount, + utilities.FieldFormat(noColor, "Statuses:"), a.StatusCount, + utilities.HeaderFormat(noColor, "BIOGRAPHY:"), utilities.WrapLines(utilities.StripHTMLTags(a.Note), "\n ", 80), - utilities.HeaderFormat("METADATA:"), + utilities.HeaderFormat(noColor, "METADATA:"), metadata, - utilities.HeaderFormat("ACCOUNT URL:"), + utilities.HeaderFormat(noColor, "ACCOUNT URL:"), a.URL, ) } @@ -130,7 +130,7 @@ type AccountRelationship struct { ShowingReblogs bool `json:"showing_reblogs"` } -func (a AccountRelationship) String() string { +func (a AccountRelationship) Display(noColor bool) string { format := ` %s %s: %t @@ -151,25 +151,25 @@ func (a AccountRelationship) String() string { output := fmt.Sprintf( format, - utilities.HeaderFormat("YOUR RELATIONSHIP WITH THIS ACCOUNT:"), - utilities.FieldFormat("Following"), a.Following, - utilities.FieldFormat("Is following you"), a.FollowedBy, - utilities.FieldFormat("A follow request was sent and is pending"), a.FollowRequested, - utilities.FieldFormat("Received a pending follow request"), a.FollowRequestedBy, - utilities.FieldFormat("Endorsed"), a.Endorsed, - utilities.FieldFormat("Showing Reposts (boosts)"), a.ShowingReblogs, - utilities.FieldFormat("Muted"), a.Muting, - utilities.FieldFormat("Notifications muted"), a.MutingNotifications, - utilities.FieldFormat("Blocking"), a.Blocking, - utilities.FieldFormat("Is blocking you"), a.BlockedBy, - utilities.FieldFormat("Blocking account's domain"), a.DomainBlocking, + utilities.HeaderFormat(noColor, "YOUR RELATIONSHIP WITH THIS ACCOUNT:"), + utilities.FieldFormat(noColor, "Following"), a.Following, + utilities.FieldFormat(noColor, "Is following you"), a.FollowedBy, + utilities.FieldFormat(noColor, "A follow request was sent and is pending"), a.FollowRequested, + utilities.FieldFormat(noColor, "Received a pending follow request"), a.FollowRequestedBy, + utilities.FieldFormat(noColor, "Endorsed"), a.Endorsed, + utilities.FieldFormat(noColor, "Showing Reposts (boosts)"), a.ShowingReblogs, + utilities.FieldFormat(noColor, "Muted"), a.Muting, + utilities.FieldFormat(noColor, "Notifications muted"), a.MutingNotifications, + utilities.FieldFormat(noColor, "Blocking"), a.Blocking, + utilities.FieldFormat(noColor, "Is blocking you"), a.BlockedBy, + utilities.FieldFormat(noColor, "Blocking account's domain"), a.DomainBlocking, ) if a.PrivateNote != "" { output += "\n" output += fmt.Sprintf( privateNoteFormat, - utilities.HeaderFormat("YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"), + utilities.HeaderFormat(noColor, "YOUR PRIVATE NOTE ABOUT THIS ACCOUNT:"), utilities.WrapLines(a.PrivateNote, "\n ", 80), ) } @@ -190,18 +190,18 @@ type AccountList struct { Accounts []Account } -func (a AccountList) String() string { +func (a AccountList) Display(noColor bool) string { output := "\n" switch a.Type { case AccountListFollowers: - output += utilities.HeaderFormat("FOLLOWED BY:") + output += utilities.HeaderFormat(noColor, "FOLLOWED BY:") case AccountListFollowing: - output += utilities.HeaderFormat("FOLLOWING:") + output += utilities.HeaderFormat(noColor, "FOLLOWING:") case AccountListBlockedAccount: - output += utilities.HeaderFormat("BLOCKED ACCOUNTS:") + output += utilities.HeaderFormat(noColor, "BLOCKED ACCOUNTS:") default: - output += utilities.HeaderFormat("ACCOUNTS:") + output += utilities.HeaderFormat(noColor, "ACCOUNTS:") } if a.Type == AccountListBlockedAccount { @@ -216,7 +216,7 @@ func (a AccountList) String() string { for i := range a.Accounts { output += fmt.Sprintf( "\n • %s (%s)", - utilities.DisplayNameFormat(a.Accounts[i].DisplayName), + utilities.DisplayNameFormat(noColor, a.Accounts[i].DisplayName), a.Accounts[i].Acct, ) } diff --git a/internal/model/instance_v2.go b/internal/model/instance_v2.go index 2b6f36b..fdf5467 100644 --- a/internal/model/instance_v2.go +++ b/internal/model/instance_v2.go @@ -113,7 +113,7 @@ type InstanceV2Users struct { ActiveMonth int `json:"active_month"` } -func (i InstanceV2) String() string { +func (i InstanceV2) Display(noColor bool) string { format := ` %s %s @@ -138,22 +138,22 @@ func (i InstanceV2) String() string { return fmt.Sprintf( format, - utilities.HeaderFormat("INSTANCE TITLE:"), + utilities.HeaderFormat(noColor, "INSTANCE TITLE:"), i.Title, - utilities.HeaderFormat("INSTANCE DESCRIPTION:"), + utilities.HeaderFormat(noColor, "INSTANCE DESCRIPTION:"), utilities.WrapLines(i.DescriptionText, "\n ", 80), - utilities.HeaderFormat("DOMAIN:"), + utilities.HeaderFormat(noColor, "DOMAIN:"), i.Domain, - utilities.HeaderFormat("TERMS AND CONDITIONS:"), + utilities.HeaderFormat(noColor, "TERMS AND CONDITIONS:"), utilities.WrapLines(i.TermsText, "\n ", 80), - utilities.HeaderFormat("VERSION:"), + utilities.HeaderFormat(noColor, "VERSION:"), i.Version, - utilities.HeaderFormat("CONTACT:"), - utilities.FieldFormat("Name:"), - utilities.DisplayNameFormat(i.Contact.Account.DisplayName), - utilities.FieldFormat("Username:"), + utilities.HeaderFormat(noColor, "CONTACT:"), + utilities.FieldFormat(noColor, "Name:"), + utilities.DisplayNameFormat(noColor, i.Contact.Account.DisplayName), + utilities.FieldFormat(noColor, "Username:"), i.Contact.Account.Username, - utilities.FieldFormat("Email:"), + utilities.FieldFormat(noColor, "Email:"), i.Contact.Email, ) } diff --git a/internal/model/list.go b/internal/model/list.go index c45c40b..c29d235 100644 --- a/internal/model/list.go +++ b/internal/model/list.go @@ -83,7 +83,7 @@ type List struct { Accounts map[string]string } -func (l List) String() string { +func (l List) Display(noColor bool) string { format := ` %s %s @@ -98,17 +98,17 @@ func (l List) String() string { output := fmt.Sprintf( format, - utilities.HeaderFormat("LIST TITLE:"), l.Title, - utilities.HeaderFormat("LIST ID:"), l.ID, - utilities.HeaderFormat("REPLIES POLICY:"), l.RepliesPolicy, - utilities.HeaderFormat("ADDED ACCOUNTS:"), + utilities.HeaderFormat(noColor, "LIST TITLE:"), l.Title, + utilities.HeaderFormat(noColor, "LIST ID:"), l.ID, + utilities.HeaderFormat(noColor, "REPLIES POLICY:"), l.RepliesPolicy, + utilities.HeaderFormat(noColor, "ADDED ACCOUNTS:"), ) if len(l.Accounts) > 0 { for id, name := range l.Accounts { output += fmt.Sprintf( "\n • %s (%s)", - utilities.DisplayNameFormat(name), + utilities.DisplayNameFormat(noColor, name), id, ) } @@ -121,12 +121,12 @@ func (l List) String() string { type Lists []List -func (l Lists) String() string { - output := "" +func (l Lists) Display(noColor bool) string { + output := "\n" + utilities.HeaderFormat(noColor, "LISTS") for i := range l { output += fmt.Sprintf( - "\n%s (%s)", + "\n • %s (%s)", l[i].Title, l[i].ID, ) diff --git a/internal/model/preferences.go b/internal/model/preferences.go index 692c064..58547bd 100644 --- a/internal/model/preferences.go +++ b/internal/model/preferences.go @@ -15,7 +15,7 @@ type Preferences struct { ReadingAutoplayGifs bool `json:"reading:autoplay:gifs"` } -func (p Preferences) String() string { +func (p Preferences) Display(noColor bool) string { format := ` %s %s: %s @@ -24,9 +24,9 @@ func (p Preferences) String() string { return fmt.Sprintf( format, - utilities.HeaderFormat("YOUR PREFERENCES:"), - utilities.FieldFormat("Default post language"), p.PostingDefaultLanguage, - utilities.FieldFormat("Default post visibility"), p.PostingDefaultVisibility, - utilities.FieldFormat("Mark posts as sensitive by default"), p.PostingDefaultSensitive, + utilities.HeaderFormat(noColor, "YOUR PREFERENCES:"), + utilities.FieldFormat(noColor, "Default post language"), p.PostingDefaultLanguage, + utilities.FieldFormat(noColor, "Default post visibility"), p.PostingDefaultVisibility, + utilities.FieldFormat(noColor, "Mark posts as sensitive by default"), p.PostingDefaultSensitive, ) } diff --git a/internal/model/status.go b/internal/model/status.go index 011dca8..3d68744 100644 --- a/internal/model/status.go +++ b/internal/model/status.go @@ -152,7 +152,7 @@ type MediaDimensions struct { Width int `json:"width"` } -func (s Status) String() string { +func (s Status) Display(noColor bool) string { format := ` %s (@%s) @@ -178,20 +178,20 @@ func (s Status) String() string { return fmt.Sprintf( format, - utilities.DisplayNameFormat(s.Account.DisplayName), s.Account.Username, - utilities.HeaderFormat("CONTENT:"), + utilities.DisplayNameFormat(noColor, s.Account.DisplayName), s.Account.Username, + utilities.HeaderFormat(noColor, "CONTENT:"), utilities.WrapLines(utilities.StripHTMLTags(s.Content), "\n ", 80), - utilities.HeaderFormat("STATUS ID:"), + utilities.HeaderFormat(noColor, "STATUS ID:"), s.ID, - utilities.HeaderFormat("CREATED AT:"), + utilities.HeaderFormat(noColor, "CREATED AT:"), utilities.FormatTime(s.CreatedAt), - utilities.HeaderFormat("STATS:"), + utilities.HeaderFormat(noColor, "STATS:"), s.ReblogsCount, s.FavouritesCount, s.RepliesCount, - utilities.HeaderFormat("VISIBILITY:"), + utilities.HeaderFormat(noColor, "VISIBILITY:"), s.Visibility, - utilities.HeaderFormat("URL:"), + utilities.HeaderFormat(noColor, "URL:"), s.URL, ) } diff --git a/internal/model/timeline.go b/internal/model/timeline.go index 58f03ad..9bc35fc 100644 --- a/internal/model/timeline.go +++ b/internal/model/timeline.go @@ -11,27 +11,27 @@ type Timeline struct { Statuses []Status } -func (t Timeline) String() string { +func (t Timeline) Display(noColor bool) string { var builder strings.Builder separator := "────────────────────────────────────────────────────────────────────────────────" - builder.WriteString(utilities.HeaderFormat(t.Name) + "\n\n") + builder.WriteString(utilities.HeaderFormat(noColor, t.Name) + "\n") for _, status := range t.Statuses { - builder.WriteString(utilities.DisplayNameFormat(status.Account.DisplayName) + " (@" + status.Account.Acct + ")\n") + builder.WriteString("\n" + utilities.DisplayNameFormat(noColor, status.Account.DisplayName) + " (@" + status.Account.Acct + ")\n") statusID := status.ID createdAt := status.CreatedAt if status.Reblog != nil { - builder.WriteString("reposted this status from " + utilities.DisplayNameFormat(status.Reblog.Account.DisplayName) + " (@" + status.Reblog.Account.Acct + ")\n") + builder.WriteString("reposted this status from " + utilities.DisplayNameFormat(noColor, status.Reblog.Account.DisplayName) + " (@" + status.Reblog.Account.Acct + ")\n") statusID = status.Reblog.ID createdAt = status.Reblog.CreatedAt } builder.WriteString(utilities.WrapLines(utilities.StripHTMLTags(status.Content), "\n", 80) + "\n\n") - builder.WriteString(utilities.FieldFormat("ID:") + " " + statusID + "\t" + utilities.FieldFormat("Created at:") + " " + utilities.FormatTime(createdAt) + "\n") + builder.WriteString(utilities.FieldFormat(noColor, "ID:") + " " + statusID + "\t" + utilities.FieldFormat(noColor, "Created at:") + " " + utilities.FormatTime(createdAt) + "\n") builder.WriteString(separator + "\n") } diff --git a/internal/utilities/displayer.go b/internal/utilities/displayer.go new file mode 100644 index 0000000..75ad4d8 --- /dev/null +++ b/internal/utilities/displayer.go @@ -0,0 +1,11 @@ +package utilities + +import "os" + +type Displayer interface { + Display(noColor bool) string +} + +func Display(d Displayer, noColor bool) { + os.Stdout.WriteString(d.Display(noColor) + "\n") +} diff --git a/internal/utilities/format.go b/internal/utilities/format.go index 51dc026..d130923 100644 --- a/internal/utilities/format.go +++ b/internal/utilities/format.go @@ -12,18 +12,30 @@ const ( green = "\033[32m" ) -func HeaderFormat(text string) string { +func HeaderFormat(noColor bool, text string) string { + if noColor { + return text + } + return boldblue + text + reset } -func FieldFormat(text string) string { +func FieldFormat(noColor bool, text string) string { + if noColor { + return text + } + return green + text + reset } -func DisplayNameFormat(text string) string { +func DisplayNameFormat(noColor bool, text string) string { // use this pattern to remove all emoji strings pattern := regexp.MustCompile(`\s:[A-Za-z0-9]*:`) + if noColor { + return pattern.ReplaceAllString(text, "") + } + return boldmagenta + pattern.ReplaceAllString(text, "") + reset }