diff --git a/cmd/enbas-cli-generators/main.go b/cmd/enbas-cli-generators/main.go index 3ac170e..d58c32d 100644 --- a/cmd/enbas-cli-generators/main.go +++ b/cmd/enbas-cli-generators/main.go @@ -114,6 +114,7 @@ func customFlagValueType(flagType string) bool { "MultiStringFlagValue": {}, "MultiIntFlagValue": {}, "TimeDurationFlagValue": {}, + "BoolPtrFlagValue": {}, } _, exists := customFlagValueTypes[flagType] diff --git a/cmd/enbas/main.go b/cmd/enbas/main.go index 1c8e4d3..d2c679a 100644 --- a/cmd/enbas/main.go +++ b/cmd/enbas/main.go @@ -106,8 +106,6 @@ func run() error { executor.CommandCreate: executor.NewCreateExecutor( enbasPrinter, enbasConfig, - executor.CommandCreate, - executor.CommandSummaryLookup(executor.CommandCreate), ), executor.CommandDelete: executor.NewDeleteExecutor( enbasPrinter, diff --git a/internal/executor/create.go b/internal/executor/create.go index 0360ab0..5e0e409 100644 --- a/internal/executor/create.go +++ b/internal/executor/create.go @@ -1,94 +1,13 @@ package executor import ( - "flag" "fmt" - "strconv" "codeflow.dananglin.me.uk/apollo/enbas/internal/client" - "codeflow.dananglin.me.uk/apollo/enbas/internal/config" "codeflow.dananglin.me.uk/apollo/enbas/internal/model" - "codeflow.dananglin.me.uk/apollo/enbas/internal/printer" "codeflow.dananglin.me.uk/apollo/enbas/internal/utilities" ) -type CreateExecutor struct { - *flag.FlagSet - - printer *printer.Printer - config *config.Config - addPoll bool - boostable bool - federated bool - likeable bool - pollAllowsMultipleChoices bool - pollHidesVoteCounts bool - replyable bool - sensitive *bool - content string - contentType string - fromFile string - inReplyTo string - language string - resourceType string - listTitle string - listRepliesPolicy string - spoilerText string - visibility string - pollExpiresIn TimeDurationFlagValue - pollOptions MultiStringFlagValue -} - -func NewCreateExecutor(printer *printer.Printer, config *config.Config, name, summary string) *CreateExecutor { - createExe := CreateExecutor{ - FlagSet: flag.NewFlagSet(name, flag.ExitOnError), - - printer: printer, - config: config, - } - - createExe.StringVar(&createExe.resourceType, flagType, "", "Specify the type of resource to create") - - // Flags for statuses - createExe.BoolVar(&createExe.boostable, flagEnableReposts, true, "Specify if the status can be reposted/boosted by others") - createExe.StringVar(&createExe.content, flagContent, "", "The content of the status to create") - createExe.StringVar(&createExe.contentType, flagContentType, "plain", "The type that the contents should be parsed from (valid values are plain and markdown)") - createExe.BoolVar(&createExe.federated, flagEnableFederation, true, "Specify if the status can be federated beyond the local timelines") - createExe.StringVar(&createExe.fromFile, flagFromFile, "", "The file path where to read the contents from") - createExe.StringVar(&createExe.inReplyTo, flagInReplyTo, "", "The ID of the status that you want to reply to") - createExe.StringVar(&createExe.language, flagLanguage, "", "The ISO 639 language code for this status") - createExe.BoolVar(&createExe.likeable, flagEnableLikes, true, "Specify if the status can be liked/favourited") - createExe.BoolVar(&createExe.replyable, flagEnableReplies, true, "Specify if the status can be replied to") - createExe.StringVar(&createExe.spoilerText, flagSpoilerText, "", "The text to display as the status' warning or subject") - createExe.StringVar(&createExe.visibility, flagVisibility, "", "The visibility of the posted status") - createExe.BoolFunc(flagSensitive, "Specify if the status should be marked as sensitive", func(value string) error { - boolVal, err := strconv.ParseBool(value) - if err != nil { - return fmt.Errorf("unable to parse %q as a boolean value: %w", value, err) - } - - createExe.sensitive = new(bool) - *createExe.sensitive = boolVal - - return nil - }) - - // Flags specifically for polls - createExe.BoolVar(&createExe.addPoll, flagAddPoll, false, "Add a poll to the status") - createExe.BoolVar(&createExe.pollAllowsMultipleChoices, flagPollAllowsMultipleChoices, false, "The poll allows viewers to make multiple choices in the poll") - createExe.BoolVar(&createExe.pollHidesVoteCounts, flagPollHidesVoteCounts, false, "The poll will hide the vote count until it is closed") - createExe.Var(&createExe.pollOptions, flagPollOption, "A poll option. Use this multiple times to set multiple options") - createExe.Var(&createExe.pollExpiresIn, flagPollExpiresIn, "The duration in which the poll is open for") - - // Flags for lists - createExe.StringVar(&createExe.listTitle, flagListTitle, "", "Specify the title of the list") - createExe.StringVar(&createExe.listRepliesPolicy, flagListRepliesPolicy, "list", "Specify the policy of the replies for this list (valid values are followed, list and none)") - - createExe.Usage = commandUsageFunc(name, summary, createExe.FlagSet) - - return &createExe -} - func (c *CreateExecutor) Execute() error { if c.resourceType == "" { return FlagNotSetError{flagText: flagType} @@ -179,8 +98,8 @@ func (c *CreateExecutor) createStatus(gtsClient *client.Client) error { visibility = preferences.PostingDefaultVisibility } - if c.sensitive != nil { - sensitive = *c.sensitive + if c.sensitive.Value != nil { + sensitive = *c.sensitive.Value } else { sensitive = preferences.PostingDefaultSensitive } diff --git a/internal/executor/executors.go b/internal/executor/executors.go index 9c314f9..8c683b0 100644 --- a/internal/executor/executors.go +++ b/internal/executor/executors.go @@ -64,6 +64,71 @@ func NewBlockExecutor( return &exe } +// CreateExecutor is the executor for the create command. +type CreateExecutor struct { + *flag.FlagSet + printer *printer.Printer + config *config.Config + addPoll bool + content string + contentType string + federated bool + likeable bool + replyable bool + boostable bool + fromFile string + inReplyTo string + language string + listRepliesPolicy string + listTitle string + pollAllowsMultipleChoices bool + pollExpiresIn TimeDurationFlagValue + pollHidesVoteCounts bool + pollOptions MultiStringFlagValue + sensitive BoolPtrFlagValue + spoilerText string + resourceType string + visibility string +} + +func NewCreateExecutor( + printer *printer.Printer, + config *config.Config, +) *CreateExecutor { + exe := CreateExecutor{ + FlagSet: flag.NewFlagSet("create", flag.ExitOnError), + printer: printer, + config: config, + pollExpiresIn: NewTimeDurationFlagValue(), + pollOptions: NewMultiStringFlagValue(), + sensitive: NewBoolPtrFlagValue(), + } + + exe.Usage = commandUsageFunc("create", "Creates a specific resource", exe.FlagSet) + + exe.BoolVar(&exe.addPoll, "add-poll", false, "Set to true to add a poll when creating a status") + exe.StringVar(&exe.content, "content", "", "The content of the created resource") + exe.StringVar(&exe.contentType, "content-type", "plain", "The type that the contents should be parsed from (valid values are plain and markdown)") + exe.BoolVar(&exe.federated, "enable-federation", true, "Set to true to federate the status beyond the local timelines") + exe.BoolVar(&exe.likeable, "enable-likes", true, "Set to true to allow the status to be liked (favourited)") + exe.BoolVar(&exe.replyable, "enable-replies", true, "Set to true to allow viewers to reply to the status") + exe.BoolVar(&exe.boostable, "enable-reposts", true, "Set to true to allow the status to be reposted (boosted) by others") + exe.StringVar(&exe.fromFile, "from-file", "", "The file path where to read the contents from") + exe.StringVar(&exe.inReplyTo, "in-reply-to", "", "The ID of the status that you want to reply to") + exe.StringVar(&exe.language, "language", "", "The ISO 639 language code for this status") + exe.StringVar(&exe.listRepliesPolicy, "list-replies-policy", "list", "The replies policy of the list") + exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list") + exe.BoolVar(&exe.pollAllowsMultipleChoices, "poll-allows-multiple-choices", false, "Set to true to allow viewers to make multiple choices in the poll") + exe.Var(&exe.pollExpiresIn, "poll-expires-in", "The duration in which the poll is open for") + exe.BoolVar(&exe.pollHidesVoteCounts, "poll-hides-vote-counts", false, "Set to true to hide the vote count until the poll is closed") + exe.Var(&exe.pollOptions, "poll-option", "A poll option. Use this multiple times to set multiple options") + exe.Var(&exe.sensitive, "sensitive", "Set to true if the status should be marked as sensitive") + exe.StringVar(&exe.spoilerText, "spoiler-text", "", "The text to display as the status' warning or subject") + exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)") + exe.StringVar(&exe.visibility, "visibility", "", "The visibility of the posted status") + return &exe +} + // DeleteExecutor is the executor for the delete command. type DeleteExecutor struct { *flag.FlagSet @@ -83,7 +148,7 @@ func NewDeleteExecutor( config: config, } - exe.Usage = commandUsageFunc("delete", "Delete a specific resource", exe.FlagSet) + exe.Usage = commandUsageFunc("delete", "Deletes a specific resource", exe.FlagSet) exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question") exe.StringVar(&exe.resourceType, "type", "", "The type of resource you want to action on (e.g. account, status)") diff --git a/internal/executor/flags.go b/internal/executor/flags.go index e27d974..baa17e0 100644 --- a/internal/executor/flags.go +++ b/internal/executor/flags.go @@ -8,58 +8,47 @@ import ( ) const ( - flagAddPoll = "add-poll" - flagAccountName = "account-name" - flagAllImages = "all-images" - flagAllVideos = "all-videos" - flagAttachmentID = "attachment-id" - flagBrowser = "browser" - flagContentType = "content-type" - flagContent = "content" - flagEnableFederation = "enable-federation" - flagEnableLikes = "enable-likes" - flagEnableReplies = "enable-replies" - flagEnableReposts = "enable-reposts" - flagExcludeBoosts = "exclude-boosts" - flagExcludeReplies = "exclude-replies" - flagFrom = "from" - flagFromFile = "from-file" - flagInReplyTo = "in-reply-to" - flagInstance = "instance" - flagLanguage = "language" - flagLimit = "limit" - flagListID = "list-id" - flagListTitle = "list-title" - flagListRepliesPolicy = "list-replies-policy" - flagMyAccount = "my-account" - flagMuteDuration = "mute-duration" - flagMuteNotifications = "mute-notifications" - flagOnlyMedia = "only-media" - flagOnlyPinned = "only-pinned" - flagOnlyPublic = "only-public" - flagPollAllowsMultipleChoices = "poll-allows-multiple-choices" - flagPollExpiresIn = "poll-expires-in" - flagPollHidesVoteCounts = "poll-hides-vote-counts" - flagPollID = "poll-id" - flagPollOption = "poll-option" - flagSensitive = "sensitive" - flagSkipRelationship = "skip-relationship" - flagShowPreferences = "show-preferences" - flagShowStatuses = "show-statuses" - flagSpoilerText = "spoiler-text" - flagStatusID = "status-id" - flagTag = "tag" - flagTimelineCategory = "timeline-category" - flagTo = "to" - flagType = "type" - flagVisibility = "visibility" - flagVote = "vote" + flagAccountName = "account-name" + flagAllImages = "all-images" + flagAllVideos = "all-videos" + flagAttachmentID = "attachment-id" + flagBrowser = "browser" + flagContent = "content" + flagExcludeBoosts = "exclude-boosts" + flagExcludeReplies = "exclude-replies" + flagFrom = "from" + flagFromFile = "from-file" + flagInstance = "instance" + flagLimit = "limit" + flagListID = "list-id" + flagListTitle = "list-title" + flagMyAccount = "my-account" + flagOnlyMedia = "only-media" + flagOnlyPinned = "only-pinned" + flagOnlyPublic = "only-public" + flagPollID = "poll-id" + flagPollOption = "poll-option" + flagSkipRelationship = "skip-relationship" + flagShowPreferences = "show-preferences" + flagShowStatuses = "show-statuses" + flagStatusID = "status-id" + flagTag = "tag" + flagTimelineCategory = "timeline-category" + flagTo = "to" + flagType = "type" + flagVote = "vote" ) type MultiStringFlagValue []string -func (v *MultiStringFlagValue) String() string { - return strings.Join(*v, ", ") +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 { @@ -72,11 +61,17 @@ func (v *MultiStringFlagValue) Set(value string) error { type MultiIntFlagValue []int -func (v *MultiIntFlagValue) String() string { +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 { + for ind, vote := range v { + if ind == len(v)-1 { value += strconv.Itoa(vote) } else { value += strconv.Itoa(vote) + ", " @@ -121,3 +116,33 @@ func (v *TimeDurationFlagValue) Set(text string) error { 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 +} diff --git a/schema/enbas_cli_schema.json b/schema/enbas_cli_schema.json index 33081eb..5cb60b6 100644 --- a/schema/enbas_cli_schema.json +++ b/schema/enbas_cli_schema.json @@ -4,14 +4,54 @@ "type": "string", "description": "The name of the account" }, + "add-poll": { + "type": "bool", + "description": "Set to true to add a poll when creating a status" + }, + "content": { + "type": "string", + "description": "The content of the created resource" + }, + "content-type": { + "type": "string", + "description": "The type that the contents should be parsed from (valid values are plain and markdown)" + }, + "enable-federation": { + "type": "bool", + "description": "Set to true to federate the status beyond the local timelines" + }, + "enable-likes": { + "type": "bool", + "description": "Set to true to allow the status to be liked (favourited)" + }, + "enable-replies": { + "type": "bool", + "description": "Set to true to allow viewers to reply to the status" + }, + "enable-reposts": { + "type": "bool", + "description": "Set to true to allow the status to be reposted (boosted) by others" + }, + "from-file": { + "type": "string", + "description": "The file path where to read the contents from" + }, "full": { "type": "bool", "description": "Set to true to print the build information in full" }, + "in-reply-to": { + "type": "string", + "description": "The ID of the status that you want to reply to" + }, "instance": { "type": "string", "description": "The instance that you want to log into" }, + "language": { + "type": "string", + "description": "The ISO 639 language code for this status" + }, "list-id": { "type": "string", "description": "The ID of the list in question" @@ -36,10 +76,34 @@ "type": "bool", "description": "Get notifications from statuses from the account you want to follow" }, + "poll-allows-multiple-choices": { + "type": "bool", + "description": "Set to true to allow viewers to make multiple choices in the poll" + }, + "poll-expires-in": { + "type": "TimeDurationFlagValue", + "description": "The duration in which the poll is open for" + }, + "poll-hides-vote-counts": { + "type": "bool", + "description": "Set to true to hide the vote count until the poll is closed" + }, + "poll-option": { + "type": "MultiStringFlagValue", + "description": "A poll option. Use this multiple times to set multiple options" + }, + "sensitive": { + "type": "BoolPtrFlagValue", + "description": "Set to true if the status should be marked as sensitive" + }, "show-reposts": { "type": "bool", "description": "Show reposts from the account you want to follow" }, + "spoiler-text": { + "type": "string", + "description": "The text to display as the status' warning or subject" + }, "type": { "type": "string", "description": "The type of resource you want to action on (e.g. account, status)" @@ -47,6 +111,10 @@ "to": { "type": "string", "description": "TBC" + }, + "visibility": { + "type": "string", + "description": "The visibility of the posted status" } }, @@ -71,13 +139,41 @@ "useConfig": true, "usePrinter": true }, + "create": { + "additionalFields": [], + "flags": [ + { "flag": "add-poll", "default": "false" }, + { "flag": "content", "default": "" }, + { "flag": "content-type", "default": "plain" }, + { "flag": "enable-federation", "fieldName": "federated", "default": "true" }, + { "flag": "enable-likes", "fieldName": "likeable", "default": "true" }, + { "flag": "enable-replies", "fieldName": "replyable", "default": "true" }, + { "flag": "enable-reposts", "fieldName": "boostable", "default": "true" }, + { "flag": "from-file", "default": "" }, + { "flag": "in-reply-to", "default": "" }, + { "flag": "language", "default": "" }, + { "flag": "list-replies-policy", "default": "list" }, + { "flag": "list-title", "default": "" }, + { "flag": "poll-allows-multiple-choices", "default": "false" }, + { "flag": "poll-expires-in" }, + { "flag": "poll-hides-vote-counts", "default": "false" }, + { "flag": "poll-option", "fieldName": "pollOptions" }, + { "flag": "sensitive" }, + { "flag": "spoiler-text", "default": "" }, + { "flag": "type", "fieldName": "resourceType", "default": "" }, + { "flag": "visibility", "default": "" } + ], + "summary": "Creates a specific resource", + "useConfig": true, + "usePrinter": true + }, "delete": { "additionalFields": [], "flags": [ { "flag": "list-id", "fieldName": "listID", "default": ""}, { "flag": "type", "fieldName": "resourceType", "default": "" } ], - "summary": "Delete a specific resource", + "summary": "Deletes a specific resource", "useConfig": true, "usePrinter": true },