Compare commits

..

4 commits

22 changed files with 500 additions and 360 deletions

View file

@ -40,7 +40,7 @@ func generateExecutors(schema enbasCLISchema, output string) error {
"flagFieldName": flagFieldName,
"getFlagType": schema.Flags.getType,
"getFlagDescription": schema.Flags.getDescription,
"customFlagValueType": customFlagValueType,
"internalFlagValue": internalFlagValue,
}
tmpl := template.Must(template.New("executor-template").Funcs(funcMap).Parse(executorsFileTemplate))
@ -109,15 +109,15 @@ func convertFlagToMixedCaps(value string) string {
return builder.String()
}
func customFlagValueType(flagType string) bool {
customFlagValueTypes := map[string]struct{}{
"MultiStringFlagValue": {},
"MultiIntFlagValue": {},
"TimeDurationFlagValue": {},
"BoolPtrFlagValue": {},
func internalFlagValue(flagType string) bool {
internalFlagValues := map[string]struct{}{
"StringSliceValue": {},
"IntSliceValue": {},
"TimeDurationValue": {},
"BoolPtrValue": {},
}
_, exists := customFlagValueTypes[flagType]
_, exists := internalFlagValues[flagType]
return exists
}

View file

@ -1,11 +1,14 @@
package main
var executorsFileTemplate = `package executor
{{ print "" }}
/*
var executorsFileTemplate = `/*
This file is generated by ./cmd/enbas-cli-generators
DO NOT EDIT.
*/
{{ print "" }}
package executor
{{ print "" }}
{{ print "" }}
import internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
{{ range $name, $command := . }}
{{- $struct_name := capitalise $name | printf "%sExecutor" -}}
{{- $new_executor_function_name := capitalise $name | printf "New%sExecutor" -}}
@ -20,8 +23,14 @@ type {{ $struct_name }} struct {
config *config.Config
{{- end }}
{{- range $flag := $command.Flags -}}
{{- $flag_type := getFlagType $flag.Flag -}}
{{- if internalFlagValue $flag_type -}}
{{ print "" }}
{{ flagFieldName $flag }} {{ getFlagType $flag.Flag }}
{{ flagFieldName $flag }} internalFlag.{{ $flag_type }}
{{- else -}}
{{ print "" }}
{{ flagFieldName $flag }} {{ $flag_type }}
{{- end -}}
{{- end -}}
{{- range $field := $command.AdditionalFields -}}
{{ print "" }}
@ -53,9 +62,9 @@ func {{ $new_executor_function_name }}(
{{- end }}
{{- range $flag := $command.Flags -}}
{{- $flag_type := getFlagType $flag.Flag -}}
{{- if customFlagValueType $flag_type -}}
{{- if internalFlagValue $flag_type -}}
{{ print "" }}
{{ flagFieldName $flag }}: New{{ $flag_type }}(),
{{ flagFieldName $flag }}: internalFlag.New{{ $flag_type }}(),
{{- end -}}
{{- end -}}
{{- range $field := $command.AdditionalFields -}}
@ -78,7 +87,7 @@ func {{ $new_executor_function_name }}(
{{- else if eq $flag_type "int" -}}
{{ print "" }}
exe.IntVar(&exe.{{ flagFieldName $flag }}, {{ printf "%q" $flag.Flag }}, {{ $flag.Default }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
{{- else if customFlagValueType $flag_type -}}
{{- else if internalFlagValue $flag_type -}}
{{ print "" }}
exe.Var(&exe.{{ flagFieldName $flag }}, {{ printf "%q" $flag.Flag }}, {{ getFlagDescription $flag.Flag | printf "%q" }})
{{- end -}}

View file

@ -2,12 +2,11 @@ package main
import (
"flag"
"fmt"
"os"
"strconv"
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
"codeflow.dananglin.me.uk/apollo/enbas/internal/executor"
internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
)
@ -27,21 +26,11 @@ func main() {
func run() error {
var (
configDir string
noColor *bool
noColorFlag internalFlag.BoolPtrValue
)
flag.StringVar(&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)
}
noColor = new(bool)
*noColor = boolVal
return nil
})
flag.Var(&noColorFlag, "no-color", "Disable ANSI colour output when displaying text on screen")
flag.Usage = usageFunc(executor.CommandSummaryMap())
@ -53,15 +42,12 @@ func run() error {
return nil
}
// If NoColor is still unspecified,
// check to see if the NO_COLOR environment variable is set
if noColor == nil {
noColor = new(bool)
if os.Getenv("NO_COLOR") != "" {
*noColor = true
} else {
*noColor = false
}
var noColor bool
if noColorFlag.Value != nil {
noColor = *noColorFlag.Value
} else if os.Getenv("NO_COLOR") != "" {
noColor = true
}
command := flag.Arg(0)
@ -75,17 +61,21 @@ func run() error {
switch command {
case executor.CommandInit, executor.CommandVersion:
enbasPrinter = printer.NewPrinter(*noColor, "", 0)
enbasPrinter = printer.NewPrinter(noColor, "", 0)
default:
enbasConfig, err = config.NewConfigFromFile(configDir)
if err != nil {
enbasPrinter = printer.NewPrinter(*noColor, "", 0)
enbasPrinter = printer.NewPrinter(noColor, "", 0)
enbasPrinter.PrintFailure("unable to load the configuration: " + err.Error() + ".")
return err
}
enbasPrinter = printer.NewPrinter(*noColor, enbasConfig.Integrations.Pager, enbasConfig.LineWrapMaxWidth)
enbasPrinter = printer.NewPrinter(
noColor,
enbasConfig.Integrations.Pager,
enbasConfig.LineWrapMaxWidth,
)
}
executorMap := map[string]executor.Executor{
@ -96,8 +86,6 @@ func run() error {
executor.CommandAdd: executor.NewAddExecutor(
enbasPrinter,
enbasConfig,
executor.CommandAdd,
executor.CommandSummaryLookup(executor.CommandAdd),
),
executor.CommandBlock: executor.NewBlockExecutor(
enbasPrinter,
@ -138,8 +126,6 @@ func run() error {
executor.CommandRemove: executor.NewRemoveExecutor(
enbasPrinter,
enbasConfig,
executor.CommandRemove,
executor.CommandSummaryLookup(executor.CommandRemove),
),
executor.CommandSwitch: executor.NewSwitchExecutor(
enbasPrinter,

View file

@ -25,7 +25,15 @@ func (a *AcceptExecutor) Execute() error {
}
func (a *AcceptExecutor) acceptFollowRequest(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, a.accountName, a.config.CredentialsFile)
expectedNumAccountNames := 1
if !a.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, a.accountName[0], a.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -2,54 +2,11 @@ package executor
import (
"errors"
"flag"
"fmt"
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
)
type AddExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
resourceType string
toResourceType string
listID string
statusID string
pollID string
choices MultiIntFlagValue
accountNames MultiStringFlagValue
content string
}
func NewAddExecutor(printer *printer.Printer, config *config.Config, name, summary string) *AddExecutor {
emptyArr := make([]string, 0, 3)
addExe := AddExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
printer: printer,
config: config,
accountNames: MultiStringFlagValue(emptyArr),
}
addExe.StringVar(&addExe.resourceType, flagType, "", "Specify the resource type to add (e.g. account, note)")
addExe.StringVar(&addExe.toResourceType, flagTo, "", "Specify the target resource type to add to (e.g. list, account, etc)")
addExe.StringVar(&addExe.listID, flagListID, "", "The ID of the list")
addExe.StringVar(&addExe.statusID, flagStatusID, "", "The ID of the status")
addExe.StringVar(&addExe.content, flagContent, "", "The content of the resource")
addExe.StringVar(&addExe.pollID, flagPollID, "", "The ID of the poll")
addExe.Var(&addExe.accountNames, flagAccountName, "The name of the account")
addExe.Var(&addExe.choices, flagVote, "Add a vote to an option in a poll")
addExe.Usage = commandUsageFunc(name, summary, addExe.FlagSet)
return &addExe
}
func (a *AddExecutor) Execute() error {
if a.toResourceType == "" {
return FlagNotSetError{flagText: flagTo}
@ -97,7 +54,7 @@ func (a *AddExecutor) addAccountsToList(gtsClient *client.Client) error {
return FlagNotSetError{flagText: flagListID}
}
if len(a.accountNames) == 0 {
if a.accountNames.Empty() {
return NoAccountSpecifiedError{}
}
@ -147,8 +104,12 @@ func (a *AddExecutor) addToAccount(gtsClient *client.Client) error {
}
func (a *AddExecutor) addNoteToAccount(gtsClient *client.Client) error {
if len(a.accountNames) != 1 {
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(a.accountNames))
expectedNumAccountNames := 1
if !a.accountNames.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, a.accountNames[0], a.config.CredentialsFile)
@ -265,7 +226,7 @@ func (a *AddExecutor) addToPoll(gtsClient *client.Client) error {
}
func (a *AddExecutor) addVoteToPoll(gtsClient *client.Client) error {
if len(a.choices) == 0 {
if a.votes.Empty() {
return errors.New("please use --" + flagVote + " to make a choice in this poll")
}
@ -278,11 +239,11 @@ func (a *AddExecutor) addVoteToPoll(gtsClient *client.Client) error {
return PollClosedError{}
}
if !poll.Multiple && len(a.choices) > 1 {
if !poll.Multiple && !a.votes.ExpectedLength(1) {
return MultipleChoiceError{}
}
if err := gtsClient.VoteInPoll(a.pollID, []int(a.choices)); err != nil {
if err := gtsClient.VoteInPoll(a.pollID, []int(a.votes)); err != nil {
return fmt.Errorf("unable to add your vote(s) to the poll: %w", err)
}

View file

@ -25,11 +25,15 @@ func (b *BlockExecutor) Execute() error {
}
func (b *BlockExecutor) blockAccount(gtsClient *client.Client) error {
if b.accountName == "" {
return FlagNotSetError{flagText: flagAccountName}
expectedNumAccountNames := 1
if !b.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, b.accountName, b.config.CredentialsFile)
accountID, err := getAccountID(gtsClient, false, b.accountName[0], b.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -1,23 +1,24 @@
/*
This file is generated by ./cmd/enbas-cli-generators
DO NOT EDIT.
*/
package executor
import (
"flag"
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag"
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
)
/*
This file is generated by ./cmd/enbas-cli-generators
DO NOT EDIT.
*/
// AcceptExecutor is the executor for the accept command.
type AcceptExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
resourceType string
}
@ -29,22 +30,64 @@ func NewAcceptExecutor(
FlagSet: flag.NewFlagSet("accept", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("accept", "Accepts a request (e.g. a follow request)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
return &exe
}
// AddExecutor is the executor for the add command.
type AddExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountNames internalFlag.StringSliceValue
content string
listID string
pollID string
statusID string
toResourceType string
resourceType string
votes internalFlag.IntSliceValue
}
func NewAddExecutor(
printer *printer.Printer,
config *config.Config,
) *AddExecutor {
exe := AddExecutor{
FlagSet: flag.NewFlagSet("add", flag.ExitOnError),
printer: printer,
config: config,
accountNames: internalFlag.NewStringSliceValue(),
votes: internalFlag.NewIntSliceValue(),
}
exe.Usage = commandUsageFunc("add", "Add a resource to another resource", exe.FlagSet)
exe.Var(&exe.accountNames, "account-name", "The name of the account")
exe.StringVar(&exe.content, "content", "", "The content of the created resource")
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
exe.StringVar(&exe.pollID, "poll-id", "", "The ID of the poll")
exe.StringVar(&exe.statusID, "status-id", "", "The ID of the status")
exe.StringVar(&exe.toResourceType, "to", "", "TBC")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
exe.Var(&exe.votes, "vote", "Add a vote to an option in a poll")
return &exe
}
// BlockExecutor is the executor for the block command.
type BlockExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
resourceType string
}
@ -56,11 +99,12 @@ func NewBlockExecutor(
FlagSet: flag.NewFlagSet("block", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("block", "Blocks a resource (e.g. an account)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
return &exe
@ -84,10 +128,10 @@ type CreateExecutor struct {
listRepliesPolicy string
listTitle string
pollAllowsMultipleChoices bool
pollExpiresIn TimeDurationFlagValue
pollExpiresIn internalFlag.TimeDurationValue
pollHidesVoteCounts bool
pollOptions MultiStringFlagValue
sensitive BoolPtrFlagValue
pollOptions internalFlag.StringSliceValue
sensitive internalFlag.BoolPtrValue
spoilerText string
resourceType string
visibility string
@ -101,9 +145,9 @@ func NewCreateExecutor(
FlagSet: flag.NewFlagSet("create", flag.ExitOnError),
printer: printer,
config: config,
pollExpiresIn: NewTimeDurationFlagValue(),
pollOptions: NewMultiStringFlagValue(),
sensitive: NewBoolPtrFlagValue(),
pollExpiresIn: internalFlag.NewTimeDurationValue(),
pollOptions: internalFlag.NewStringSliceValue(),
sensitive: internalFlag.NewBoolPtrValue(),
}
exe.Usage = commandUsageFunc("create", "Creates a specific resource", exe.FlagSet)
@ -195,7 +239,7 @@ type FollowExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
notify bool
showReposts bool
resourceType string
@ -209,11 +253,12 @@ func NewFollowExecutor(
FlagSet: flag.NewFlagSet("follow", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("follow", "Follow a resource (e.g. an account)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.BoolVar(&exe.notify, "notify", false, "Get notifications from statuses from the account you want to follow")
exe.BoolVar(&exe.showReposts, "show-reposts", true, "Show reposts from the account you want to follow")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
@ -273,8 +318,8 @@ type MuteExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
muteDuration TimeDurationFlagValue
accountName internalFlag.StringSliceValue
muteDuration internalFlag.TimeDurationValue
muteNotifications bool
resourceType string
}
@ -287,12 +332,13 @@ func NewMuteExecutor(
FlagSet: flag.NewFlagSet("mute", flag.ExitOnError),
printer: printer,
config: config,
muteDuration: NewTimeDurationFlagValue(),
accountName: internalFlag.NewStringSliceValue(),
muteDuration: internalFlag.NewTimeDurationValue(),
}
exe.Usage = commandUsageFunc("mute", "Mutes a specific resource (e.g. an account)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.Var(&exe.muteDuration, "mute-duration", "Specify how long the mute should last for. To mute indefinitely, set this to 0s")
exe.BoolVar(&exe.muteNotifications, "mute-notifications", false, "Set to true to mute notifications as well as posts")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
@ -305,7 +351,7 @@ type RejectExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
resourceType string
}
@ -317,11 +363,46 @@ func NewRejectExecutor(
FlagSet: flag.NewFlagSet("reject", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("reject", "Rejects a request (e.g. a follow request)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
return &exe
}
// RemoveExecutor is the executor for the remove command.
type RemoveExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountNames internalFlag.StringSliceValue
fromResourceType string
listID string
statusID string
resourceType string
}
func NewRemoveExecutor(
printer *printer.Printer,
config *config.Config,
) *RemoveExecutor {
exe := RemoveExecutor{
FlagSet: flag.NewFlagSet("remove", flag.ExitOnError),
printer: printer,
config: config,
accountNames: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("remove", "", exe.FlagSet)
exe.Var(&exe.accountNames, "account-name", "The name of the account")
exe.StringVar(&exe.fromResourceType, "from", "", "Specify the resource type to action the target resource from")
exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question")
exe.StringVar(&exe.statusID, "status-id", "", "The ID of the status")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
return &exe
@ -332,10 +413,10 @@ type ShowExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
getAllImages bool
getAllVideos bool
attachmentIDs MultiStringFlagValue
attachmentIDs internalFlag.StringSliceValue
showInBrowser bool
excludeBoosts bool
excludeReplies bool
@ -364,12 +445,13 @@ func NewShowExecutor(
FlagSet: flag.NewFlagSet("show", flag.ExitOnError),
printer: printer,
config: config,
attachmentIDs: NewMultiStringFlagValue(),
accountName: internalFlag.NewStringSliceValue(),
attachmentIDs: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("show", "Shows details about a specified resource", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.BoolVar(&exe.getAllImages, "all-images", false, "Set to true to show all images from a status")
exe.BoolVar(&exe.getAllVideos, "all-videos", false, "Set to true to show all videos from a status")
exe.Var(&exe.attachmentIDs, "attachment-id", "The ID of the media attachment")
@ -400,7 +482,7 @@ type SwitchExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
to string
}
@ -412,11 +494,12 @@ func NewSwitchExecutor(
FlagSet: flag.NewFlagSet("switch", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("switch", "Performs a switch operation (e.g. switching between logged in accounts)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.StringVar(&exe.to, "to", "", "TBC")
return &exe
@ -427,7 +510,7 @@ type UnblockExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
resourceType string
}
@ -439,11 +522,12 @@ func NewUnblockExecutor(
FlagSet: flag.NewFlagSet("unblock", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("unblock", "Unblocks a resource (e.g. an account)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
return &exe
@ -454,7 +538,7 @@ type UnfollowExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
resourceType string
}
@ -466,11 +550,12 @@ func NewUnfollowExecutor(
FlagSet: flag.NewFlagSet("unfollow", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("unfollow", "Unfollow a resource (e.g. an account)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
return &exe
@ -481,7 +566,7 @@ type UnmuteExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
accountName string
accountName internalFlag.StringSliceValue
resourceType string
}
@ -493,11 +578,12 @@ func NewUnmuteExecutor(
FlagSet: flag.NewFlagSet("unmute", flag.ExitOnError),
printer: printer,
config: config,
accountName: internalFlag.NewStringSliceValue(),
}
exe.Usage = commandUsageFunc("unmute", "Umutes a specific resource (e.g. an account)", exe.FlagSet)
exe.StringVar(&exe.accountName, "account-name", "", "The name of the account")
exe.Var(&exe.accountName, "account-name", "The name of the account")
exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)")
return &exe

View file

@ -1,14 +1,6 @@
package executor
import (
"fmt"
"strconv"
"strings"
"time"
)
const (
flagAccountName = "account-name"
flagAttachmentID = "attachment-id"
flagContent = "content"
flagFrom = "from"
@ -24,111 +16,3 @@ const (
flagType = "type"
flagVote = "vote"
)
type MultiStringFlagValue []string
func NewMultiStringFlagValue() MultiStringFlagValue {
arr := make([]string, 0, 3)
return MultiStringFlagValue(arr)
}
func (v MultiStringFlagValue) String() string {
return strings.Join(v, ", ")
}
func (v *MultiStringFlagValue) Set(value string) error {
if len(value) > 0 {
*v = append(*v, value)
}
return nil
}
type MultiIntFlagValue []int
func NewMultiIntFlagValue() MultiIntFlagValue {
arr := make([]int, 0, 3)
return MultiIntFlagValue(arr)
}
func (v MultiIntFlagValue) String() string {
value := "Choices: "
for ind, vote := range v {
if ind == len(v)-1 {
value += strconv.Itoa(vote)
} else {
value += strconv.Itoa(vote) + ", "
}
}
return value
}
func (v *MultiIntFlagValue) Set(text string) error {
value, err := strconv.Atoi(text)
if err != nil {
return fmt.Errorf("unable to parse the value to an integer: %w", err)
}
*v = append(*v, value)
return nil
}
type TimeDurationFlagValue struct {
Duration time.Duration
}
func NewTimeDurationFlagValue() TimeDurationFlagValue {
return TimeDurationFlagValue{
Duration: 0 * time.Second,
}
}
func (v TimeDurationFlagValue) String() string {
return "Time duration: " + v.Duration.String()
}
func (v *TimeDurationFlagValue) Set(text string) error {
duration, err := time.ParseDuration(text)
if err != nil {
return fmt.Errorf("unable to parse the value as time duration: %w", err)
}
v.Duration = duration
return nil
}
type BoolPtrFlagValue struct {
Value *bool
}
func NewBoolPtrFlagValue() BoolPtrFlagValue {
return BoolPtrFlagValue{
Value: nil,
}
}
func (b BoolPtrFlagValue) String() string {
if b.Value == nil {
return "NOT SET"
}
return strconv.FormatBool(*b.Value)
}
func (b *BoolPtrFlagValue) Set(value string) error {
boolVar, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("unable to parse %q as a boolean value: %w", value, err)
}
b.Value = new(bool)
*b.Value = boolVar
return nil
}

View file

@ -25,7 +25,11 @@ func (f *FollowExecutor) Execute() error {
}
func (f *FollowExecutor) followAccount(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, f.accountName, f.config.CredentialsFile)
if !f.accountName.ExpectedLength(1) {
return fmt.Errorf("found an unexpected number of %s flags: expected %d", "--account-name", 1)
}
accountID, err := getAccountID(gtsClient, false, f.accountName[0], f.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -25,11 +25,15 @@ func (m *MuteExecutor) Execute() error {
}
func (m *MuteExecutor) muteAccount(gtsClient *client.Client) error {
if m.accountName == "" {
return FlagNotSetError{flagText: flagAccountName}
expectedNumAccountNames := 1
if !m.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, m.accountName, m.config.CredentialsFile)
accountID, err := getAccountID(gtsClient, false, m.accountName[0], m.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -25,7 +25,15 @@ func (r *RejectExecutor) Execute() error {
}
func (r *RejectExecutor) rejectFollowRequest(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, r.accountName, r.config.CredentialsFile)
expectedNumAccountNames := 1
if !r.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, r.accountName[0], r.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -1,48 +1,11 @@
package executor
import (
"flag"
"fmt"
"codeflow.dananglin.me.uk/apollo/enbas/internal/client"
"codeflow.dananglin.me.uk/apollo/enbas/internal/config"
"codeflow.dananglin.me.uk/apollo/enbas/internal/printer"
)
type RemoveExecutor struct {
*flag.FlagSet
printer *printer.Printer
config *config.Config
resourceType string
fromResourceType string
listID string
statusID string
accountNames MultiStringFlagValue
}
func NewRemoveExecutor(printer *printer.Printer, config *config.Config, name, summary string) *RemoveExecutor {
emptyArr := make([]string, 0, 3)
removeExe := RemoveExecutor{
FlagSet: flag.NewFlagSet(name, flag.ExitOnError),
printer: printer,
config: config,
accountNames: MultiStringFlagValue(emptyArr),
}
removeExe.StringVar(&removeExe.resourceType, flagType, "", "Specify the resource type to remove (e.g. account, note)")
removeExe.StringVar(&removeExe.fromResourceType, flagFrom, "", "Specify the resource type to remove from (e.g. list, account, etc)")
removeExe.StringVar(&removeExe.listID, flagListID, "", "The ID of the list to remove from")
removeExe.StringVar(&removeExe.statusID, flagStatusID, "", "The ID of the status")
removeExe.Var(&removeExe.accountNames, flagAccountName, "The name of the account to remove from the resource")
removeExe.Usage = commandUsageFunc(name, summary, removeExe.FlagSet)
return &removeExe
}
func (r *RemoveExecutor) Execute() error {
if r.fromResourceType == "" {
return FlagNotSetError{flagText: flagFrom}
@ -89,7 +52,7 @@ func (r *RemoveExecutor) removeAccountsFromList(gtsClient *client.Client) error
return FlagNotSetError{flagText: flagListID}
}
if len(r.accountNames) == 0 {
if r.accountNames.Empty() {
return NoAccountSpecifiedError{}
}
@ -130,8 +93,12 @@ func (r *RemoveExecutor) removeFromAccount(gtsClient *client.Client) error {
}
func (r *RemoveExecutor) removeNoteFromAccount(gtsClient *client.Client) error {
if len(r.accountNames) != 1 {
return fmt.Errorf("unexpected number of accounts specified: want 1, got %d", len(r.accountNames))
expectedNumAccountNames := 1
if !r.accountNames.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, r.accountNames[0], r.config.CredentialsFile)

View file

@ -70,11 +70,14 @@ func (s *ShowExecutor) showAccount(gtsClient *client.Client) error {
return fmt.Errorf("received an error while getting the account details: %w", err)
}
} else {
if s.accountName == "" {
return FlagNotSetError{flagText: flagAccountName}
expectedNumAccountNames := 1
if !s.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
account, err = getAccount(gtsClient, s.accountName)
account, err = getAccount(gtsClient, s.accountName[0])
if err != nil {
return fmt.Errorf("received an error while getting the account details: %w", err)
}
@ -269,7 +272,15 @@ func (s *ShowExecutor) showFollowers(gtsClient *client.Client) error {
}
func (s *ShowExecutor) showFollowersFromAccount(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
expectedNumAccountNames := 1
if !s.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName[0], s.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}
@ -309,7 +320,15 @@ func (s *ShowExecutor) showFollowing(gtsClient *client.Client) error {
}
func (s *ShowExecutor) showFollowingFromAccount(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName, s.config.CredentialsFile)
expectedNumAccountNames := 1
if !s.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, s.myAccount, s.accountName[0], s.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -20,15 +20,19 @@ func (s *SwitchExecutor) Execute() error {
}
func (s *SwitchExecutor) switchToAccount() error {
if s.accountName == "" {
return NoAccountSpecifiedError{}
expectedNumAccountNames := 1
if !s.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
if err := config.UpdateCurrentAccount(s.accountName, s.config.CredentialsFile); err != nil {
if err := config.UpdateCurrentAccount(s.accountName[0], s.config.CredentialsFile); err != nil {
return fmt.Errorf("unable to switch account to the account: %w", err)
}
s.printer.PrintSuccess("The current account is now set to '" + s.accountName + "'.")
s.printer.PrintSuccess("The current account is now set to '" + s.accountName[0] + "'.")
return nil
}

View file

@ -25,11 +25,15 @@ func (b *UnblockExecutor) Execute() error {
}
func (b *UnblockExecutor) unblockAccount(gtsClient *client.Client) error {
if b.accountName == "" {
return FlagNotSetError{flagText: flagAccountName}
expectedNumAccountNames := 1
if !b.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, b.accountName, b.config.CredentialsFile)
accountID, err := getAccountID(gtsClient, false, b.accountName[0], b.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -25,7 +25,15 @@ func (f *UnfollowExecutor) Execute() error {
}
func (f *UnfollowExecutor) unfollowAccount(gtsClient *client.Client) error {
accountID, err := getAccountID(gtsClient, false, f.accountName, f.config.CredentialsFile)
expectedNumAccountNames := 1
if !f.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, f.accountName[0], f.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -25,11 +25,15 @@ func (m *UnmuteExecutor) Execute() error {
}
func (m *UnmuteExecutor) unmuteAccount(gtsClient *client.Client) error {
if m.accountName == "" {
return FlagNotSetError{flagText: flagAccountName}
expectedNumAccountNames := 1
if !m.accountName.ExpectedLength(expectedNumAccountNames) {
return fmt.Errorf(
"found an unexpected number of --account-name flags: expected %d",
expectedNumAccountNames,
)
}
accountID, err := getAccountID(gtsClient, false, m.accountName, m.config.CredentialsFile)
accountID, err := getAccountID(gtsClient, false, m.accountName[0], m.config.CredentialsFile)
if err != nil {
return fmt.Errorf("received an error while getting the account ID: %w", err)
}

View file

@ -0,0 +1,36 @@
package flag
import (
"fmt"
"strconv"
)
type BoolPtrValue struct {
Value *bool
}
func NewBoolPtrValue() BoolPtrValue {
return BoolPtrValue{
Value: nil,
}
}
func (b BoolPtrValue) String() string {
if b.Value == nil {
return "NOT SET"
}
return strconv.FormatBool(*b.Value)
}
func (b *BoolPtrValue) Set(value string) error {
boolVar, err := strconv.ParseBool(value)
if err != nil {
return fmt.Errorf("unable to parse %q as a boolean value: %w", value, err)
}
b.Value = new(bool)
*b.Value = boolVar
return nil
}

49
internal/flag/intslice.go Normal file
View file

@ -0,0 +1,49 @@
package flag
import (
"fmt"
"strconv"
"strings"
)
type IntSliceValue []int
func NewIntSliceValue() IntSliceValue {
arr := make([]int, 0, 3)
return IntSliceValue(arr)
}
func (v IntSliceValue) String() string {
var builder strings.Builder
for ind, value := range v {
if ind == len(v)-1 {
builder.WriteString(strconv.Itoa(value))
} else {
builder.WriteString(strconv.Itoa(value))
builder.WriteString(", ")
}
}
return builder.String()
}
func (v *IntSliceValue) Set(text string) error {
value, err := strconv.Atoi(text)
if err != nil {
return fmt.Errorf("unable to parse the value to an integer: %w", err)
}
*v = append(*v, value)
return nil
}
func (v IntSliceValue) Empty() bool {
return len(v) == 0
}
func (v IntSliceValue) ExpectedLength(expectedLength int) bool {
return len(v) == expectedLength
}

View file

@ -0,0 +1,31 @@
package flag
import "strings"
type StringSliceValue []string
func NewStringSliceValue() StringSliceValue {
arr := make([]string, 0, 3)
return StringSliceValue(arr)
}
func (v StringSliceValue) String() string {
return strings.Join(v, ", ")
}
func (v *StringSliceValue) Set(value string) error {
if len(value) > 0 {
*v = append(*v, value)
}
return nil
}
func (v StringSliceValue) Empty() bool {
return len(v) == 0
}
func (v StringSliceValue) ExpectedLength(expectedLength int) bool {
return len(v) == expectedLength
}

View file

@ -0,0 +1,31 @@
package flag
import (
"fmt"
"time"
)
type TimeDurationValue struct {
Duration time.Duration
}
func NewTimeDurationValue() TimeDurationValue {
return TimeDurationValue{
Duration: 0 * time.Second,
}
}
func (v TimeDurationValue) String() string {
return v.Duration.String()
}
func (v *TimeDurationValue) Set(text string) error {
duration, err := time.ParseDuration(text)
if err != nil {
return fmt.Errorf("unable to parse the value as time duration: %w", err)
}
v.Duration = duration
return nil
}

View file

@ -1,7 +1,7 @@
{
"flags": {
"account-name": {
"type": "string",
"type": "StringSliceValue",
"description": "The name of the account"
},
"all-images": {
@ -13,7 +13,7 @@
"description": "Set to true to show all videos from a status"
},
"attachment-id": {
"type": "MultiStringFlagValue",
"type": "StringSliceValue",
"description": "The ID of the media attachment"
},
"add-poll": {
@ -97,7 +97,7 @@
"description": "The replies policy of the list"
},
"mute-duration": {
"type": "TimeDurationFlagValue",
"type": "TimeDurationValue",
"description": "Specify how long the mute should last for. To mute indefinitely, set this to 0s"
},
"mute-notifications": {
@ -129,7 +129,7 @@
"description": "Set to true to allow viewers to make multiple choices in the poll"
},
"poll-expires-in": {
"type": "TimeDurationFlagValue",
"type": "TimeDurationValue",
"description": "The duration in which the poll is open for"
},
"poll-hides-vote-counts": {
@ -141,11 +141,11 @@
"description": "The ID of the poll"
},
"poll-option": {
"type": "MultiStringFlagValue",
"type": "StringSliceValue",
"description": "A poll option. Use this multiple times to set multiple options"
},
"sensitive": {
"type": "BoolPtrFlagValue",
"type": "BoolPtrValue",
"description": "Set to true if the status should be marked as sensitive"
},
"show-preferences": {
@ -180,17 +180,21 @@
"type": "string",
"description": "The timeline category"
},
"type": {
"type": "string",
"description": "The type of resource you want to action on (e.g. account, status)"
},
"to": {
"type": "string",
"description": "TBC"
},
"type": {
"type": "string",
"description": "The type of resource you want to action on (e.g. account, status)"
},
"visibility": {
"type": "string",
"description": "The visibility of the posted status"
},
"vote": {
"type": "IntSliceValue",
"description": "Add a vote to an option in a poll"
}
},
@ -198,17 +202,33 @@
"accept": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
],
"summary": "Accepts a request (e.g. a follow request)",
"useConfig": true,
"usePrinter": true
},
"add": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "fieldName": "accountNames" },
{ "flag": "content", "default": "" },
{ "flag": "list-id", "fieldName": "listID", "default": "" },
{ "flag": "poll-id", "fieldName": "pollID", "default": "" },
{ "flag": "status-id", "fieldName": "statusID", "default": "" },
{ "flag": "to", "fieldName": "toResourceType", "default": "" },
{ "flag": "type", "fieldName": "resourceType", "default": "" },
{ "flag": "vote", "fieldName": "votes" }
],
"summary": "Add a resource to another resource",
"useConfig": true,
"usePrinter": true
},
"block": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
],
"summary": "Blocks a resource (e.g. an account)",
@ -268,7 +288,7 @@
"follow": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "notify", "default": "false" },
{ "flag": "show-reposts", "default": "true" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
@ -298,7 +318,7 @@
"mute": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "mute-duration" },
{ "flag": "mute-notifications", "default": "false" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
@ -310,17 +330,30 @@
"reject": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
],
"summary": "Rejects a request (e.g. a follow request)",
"useConfig": true,
"usePrinter": true
},
"remove": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "fieldName": "accountNames" },
{ "flag": "from", "fieldName": "fromResourceType", "default": "" },
{ "flag": "list-id", "fieldName": "listID", "default": "" },
{ "flag": "status-id", "fieldName": "statusID", "default": "" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
],
"summary": "",
"useConfig": true,
"usePrinter": true
},
"show": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "all-images", "fieldName": "getAllImages", "default": "false" },
{ "flag": "all-videos", "fieldName": "getAllVideos", "default": "false" },
{ "flag": "attachment-id", "fieldName": "attachmentIDs" },
@ -350,7 +383,7 @@
"switch": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "to", "default": "" }
],
"summary": "Performs a switch operation (e.g. switching between logged in accounts)",
@ -360,7 +393,7 @@
"unblock": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
],
"summary": "Unblocks a resource (e.g. an account)",
@ -370,7 +403,7 @@
"unfollow": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
],
"summary": "Unfollow a resource (e.g. an account)",
@ -380,7 +413,7 @@
"unmute": {
"additionalFields": [],
"flags": [
{ "flag": "account-name", "default": "" },
{ "flag": "account-name" },
{ "flag": "type", "fieldName": "resourceType", "default": "" }
],
"summary": "Umutes a specific resource (e.g. an account)",