diff --git a/cmd/enbas-codegen/main.go b/cmd/enbas-codegen/main.go index ef66aea..b16e60b 100644 --- a/cmd/enbas-codegen/main.go +++ b/cmd/enbas-codegen/main.go @@ -2,6 +2,7 @@ package main import ( "embed" + "errors" "flag" "fmt" "os" @@ -12,9 +13,13 @@ import ( ) func main() { - var enbasCLISchemaFilepath string + var ( + enbasCLISchemaFilepath string + packageName string + ) flag.StringVar(&enbasCLISchemaFilepath, "path-to-enbas-cli-schema", "", "The path to the Enbas CLI schema file") + flag.StringVar(&packageName, "package", "", "The name of the internal package") flag.Parse() schema, err := newEnbasCLISchemaFromFile(enbasCLISchemaFilepath) @@ -22,16 +27,24 @@ func main() { fmt.Printf("ERROR: Unable to read the schema file: %v.\n", err) } - if err := generateExecutors(schema); err != nil { + if err := generateExecutors(schema, packageName); err != nil { fmt.Printf("ERROR: Unable to generate the executors: %v.\n", err) } } -//go:embed templates/executor/* +//go:embed templates/* var executorTemplates embed.FS -func generateExecutors(schema enbasCLISchema) error { - fsDir, err := executorTemplates.ReadDir("templates/executor") +var errNoPackageFlag = errors.New("the --package flag must be used") + +func generateExecutors(schema enbasCLISchema, packageName string) error { + if packageName == "" { + return errNoPackageFlag + } + + dirName := "templates/" + packageName + + fsDir, err := executorTemplates.ReadDir(dirName) if err != nil { return fmt.Errorf("unable to read the template directory in the file system (FS): %w", err) } @@ -54,7 +67,7 @@ func generateExecutors(schema enbasCLISchema) error { if err := func() error { tmpl := template.Must(template.New(templateFilename). Funcs(funcMap). - ParseFS(executorTemplates, "templates/executor/"+templateFilename), + ParseFS(executorTemplates, dirName+"/"+templateFilename), ) output := strings.TrimSuffix(templateFilename, ".gotmpl") diff --git a/cmd/enbas-codegen/templates/executor/executors.go.gotmpl b/cmd/enbas-codegen/templates/executor/executors.go.gotmpl index 9af5f1c..f95b146 100644 --- a/cmd/enbas-codegen/templates/executor/executors.go.gotmpl +++ b/cmd/enbas-codegen/templates/executor/executors.go.gotmpl @@ -11,6 +11,7 @@ package executor {{ print "" }} {{ print "" }} import internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag" +import "codeflow.dananglin.me.uk/apollo/enbas/internal/usage" {{ print "" }} {{ print "" }} type Executor interface { @@ -83,7 +84,7 @@ func {{ $new_executor_function_name }}( {{ print "" }} } {{ print "" }} - exe.Usage = commandUsageFunc({{ printf "%q" $name }}, {{ printf "%q" $command.Summary }}, exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc({{ printf "%q" $name }}, {{ printf "%q" $command.Summary }}, exe.FlagSet) {{ print "" }} {{- range $flag := $command.Flags -}} {{- $flag_type := getFlagType $flag.Flag -}} diff --git a/cmd/enbas-codegen/templates/usage/summaries.go.gotmpl b/cmd/enbas-codegen/templates/usage/summaries.go.gotmpl new file mode 100644 index 0000000..eb88287 --- /dev/null +++ b/cmd/enbas-codegen/templates/usage/summaries.go.gotmpl @@ -0,0 +1,18 @@ +{{- /* vim: set noexpandtab : */ -}} +{{- /* vim: set tabstop=8 : */ -}} +{{- /* vim: set shiftwidth=8 : */ -}} +{{- /* vim: set softtabstop=8 : */ -}} +/* + This file is generated by the enbas-codegen + DO NOT EDIT. +*/ +{{ print "" }} +package usage +{{ print "" }} +var summaries = map[string]string { + {{- range $name, $command := . -}} + {{ print "" }} + {{ printf "%q" $name }}: {{ printf "%q" $command.Summary }}, + {{- end -}} +{{ print "" }} +} diff --git a/cmd/enbas/main.go b/cmd/enbas/main.go index 456ffd4..cd1ddd5 100644 --- a/cmd/enbas/main.go +++ b/cmd/enbas/main.go @@ -6,6 +6,7 @@ import ( "codeflow.dananglin.me.uk/apollo/enbas/internal/executor" internalFlag "codeflow.dananglin.me.uk/apollo/enbas/internal/flag" + "codeflow.dananglin.me.uk/apollo/enbas/internal/usage" ) func main() { @@ -23,7 +24,7 @@ func run() error { flag.StringVar(&configDir, "config-dir", "", "Specify your config directory") flag.Var(&noColorFlag, "no-color", "Disable ANSI colour output when displaying text on screen") - flag.Usage = usageFunc(executor.CommandSummaryMap()) + flag.Usage = usage.AppUsageFunc() flag.Parse() diff --git a/cmd/enbas/usage.go b/cmd/enbas/usage.go index d5deadf..c9ecbf5 100644 --- a/cmd/enbas/usage.go +++ b/cmd/enbas/usage.go @@ -1,53 +1,2 @@ package main -import ( - "flag" - "fmt" - "slices" - "strings" - "text/tabwriter" - - "codeflow.dananglin.me.uk/apollo/enbas/internal/version" -) - -func usageFunc(summaries map[string]string) func() { - cmds := make([]string, len(summaries)) - ind := 0 - - for k := range summaries { - cmds[ind] = k - ind++ - } - - slices.Sort(cmds) - - return func() { - var builder strings.Builder - - builder.WriteString("SUMMARY:\n enbas - A GoToSocial client for the terminal.\n\n") - - if version.BinaryVersion != "" { - builder.WriteString("VERSION:\n " + version.BinaryVersion + "\n\n") - } - - builder.WriteString("USAGE:\n enbas [flags]\n enbas [flags] [command]\n\nCOMMANDS:") - - tableWriter := tabwriter.NewWriter(&builder, 0, 8, 0, '\t', 0) - - for _, cmd := range cmds { - fmt.Fprintf(tableWriter, "\n %s\t%s", cmd, summaries[cmd]) - } - - tableWriter.Flush() - - builder.WriteString("\n\nFLAGS:\n --help\n print the help message") - flag.VisitAll(func(f *flag.Flag) { - fmt.Fprintf(&builder, "\n --%s\n %s", f.Name, f.Usage) - }) - - builder.WriteString("\n\nUse \"enbas [command] --help\" for more information about a command.\n") - - w := flag.CommandLine.Output() - fmt.Fprint(w, builder.String()) - } -} diff --git a/internal/executor/codegen.go b/internal/executor/codegen.go index c6ac0a9..a16ca0b 100644 --- a/internal/executor/codegen.go +++ b/internal/executor/codegen.go @@ -1,3 +1,3 @@ package executor -//go:generate go run ../../cmd/enbas-codegen --path-to-enbas-cli-schema ../../schema/enbas_cli_schema.json +//go:generate go run ../../cmd/enbas-codegen --package executor --path-to-enbas-cli-schema ../../schema/enbas_cli_schema.json diff --git a/internal/executor/commands.go b/internal/executor/commands.go deleted file mode 100644 index 6123c72..0000000 --- a/internal/executor/commands.go +++ /dev/null @@ -1,78 +0,0 @@ -package executor - -const ( - commandAccept string = "accept" - commandAdd string = "add" - commandBlock string = "block" - commandCreate string = "create" - commandDelete string = "delete" - commandEdit string = "edit" - commandFollow string = "follow" - commandInit string = "init" - commandLogin string = "login" - commandMute string = "mute" - commandReject string = "reject" - commandRemove string = "remove" - commandShow string = "show" - commandSwitch string = "switch" - commandUnblock string = "unblock" - commandUnfollow string = "unfollow" - commandUnmute string = "unmute" - commandVersion string = "version" - commandWhoami string = "whoami" - - commandAcceptSummary string = "Accept a request (e.g. a follow request)" - commandAddSummary string = "Add a resource to another resource" - commandBlockSummary string = "Block a resource (e.g. an account)" - commandCreateSummary string = "Create a specific resource" - commandDeleteSummary string = "Delete a specific resource" - commandEditSummary string = "Edit a specific resource" - commandFollowSummary string = "Follow a resource (e.g. an account)" - commandInitSummary string = "Create a new configuration file in the specified configuration directory" - commandLoginSummary string = "Login to an account on GoToSocial" - commandMuteSummary string = "Mute a resource (e.g. an account)" - commandRejectSummary string = "Reject a request (e.g. a follow request)" - commandRemoveSummary string = "Remove a resource from another resource" - commandShowSummary string = "Print details about a specified resource" - commandSwitchSummary string = "Perform a switch operation (e.g. switch logged in accounts)" - commandUnblockSummary string = "Unblock a resource (e.g. an account)" - commandUnfollowSummary string = "Unfollow a resource (e.g. an account)" - commandUnmuteSummary string = "Unmute a resource (e.g. an account)" - commandVersionSummary string = "Print the application's version and build information" - commandWhoamiSummary string = "Print the account that you are currently logged in to" -) - -func CommandSummaryMap() map[string]string { - return map[string]string{ - commandAccept: commandAcceptSummary, - commandAdd: commandAddSummary, - commandBlock: commandBlockSummary, - commandCreate: commandCreateSummary, - commandDelete: commandDeleteSummary, - commandEdit: commandEditSummary, - commandFollow: commandFollowSummary, - commandInit: commandInitSummary, - commandLogin: commandLoginSummary, - commandMute: commandMuteSummary, - commandReject: commandRejectSummary, - commandRemove: commandRemoveSummary, - commandShow: commandShowSummary, - commandSwitch: commandSwitchSummary, - commandUnblock: commandUnblockSummary, - commandUnfollow: commandUnfollowSummary, - commandUnmute: commandUnmuteSummary, - commandVersion: commandVersionSummary, - commandWhoami: commandWhoamiSummary, - } -} - -func CommandSummaryLookup(command string) string { - commandMap := CommandSummaryMap() - - summary, ok := commandMap[command] - if !ok { - return "This command does not have a summary" - } - - return summary -} diff --git a/internal/executor/executors.go b/internal/executor/executors.go index 43cca40..4e7fd14 100644 --- a/internal/executor/executors.go +++ b/internal/executor/executors.go @@ -11,6 +11,7 @@ import ( "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" + "codeflow.dananglin.me.uk/apollo/enbas/internal/usage" ) type Executor interface { @@ -39,7 +40,7 @@ func NewAcceptExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("accept", "Accepts a request (e.g. a follow request)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("accept", "Accepts a request (e.g. a follow request)", exe.FlagSet) 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)") @@ -74,7 +75,7 @@ func NewAddExecutor( votes: internalFlag.NewIntSliceValue(), } - exe.Usage = commandUsageFunc("add", "Add a resource to another resource", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("add", "Adds 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") @@ -108,7 +109,7 @@ func NewBlockExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("block", "Blocks a resource (e.g. an account)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("block", "Blocks a resource (e.g. an account)", exe.FlagSet) 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)") @@ -156,7 +157,7 @@ func NewCreateExecutor( sensitive: internalFlag.NewBoolPtrValue(), } - exe.Usage = commandUsageFunc("create", "Creates a specific resource", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("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") @@ -201,7 +202,7 @@ func NewDeleteExecutor( config: config, } - exe.Usage = commandUsageFunc("delete", "Deletes a specific resource", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("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)") @@ -230,7 +231,7 @@ func NewEditExecutor( config: config, } - exe.Usage = commandUsageFunc("edit", "Edit a specific resource", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("edit", "Edit a specific resource", exe.FlagSet) exe.StringVar(&exe.listID, "list-id", "", "The ID of the list in question") exe.StringVar(&exe.listTitle, "list-title", "", "The title of the list") @@ -262,7 +263,7 @@ func NewFollowExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("follow", "Follow a resource (e.g. an account)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("follow", "Follow a resource (e.g. an account)", exe.FlagSet) 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") @@ -289,7 +290,7 @@ func NewInitExecutor( configDir: configDir, } - exe.Usage = commandUsageFunc("init", "Creates a new configuration file in the specified configuration directory", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("init", "Creates a new configuration file in the specified configuration directory", exe.FlagSet) return &exe } @@ -312,7 +313,7 @@ func NewLoginExecutor( config: config, } - exe.Usage = commandUsageFunc("login", "Logs into an account on GoToSocial", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("login", "Logs into an account on GoToSocial", exe.FlagSet) exe.StringVar(&exe.instance, "instance", "", "The instance that you want to log into") @@ -342,7 +343,7 @@ func NewMuteExecutor( muteDuration: internalFlag.NewTimeDurationValue(), } - exe.Usage = commandUsageFunc("mute", "Mutes a specific resource (e.g. an account)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("mute", "Mutes a specific resource (e.g. an account)", exe.FlagSet) 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") @@ -372,7 +373,7 @@ func NewRejectExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("reject", "Rejects a request (e.g. a follow request)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("reject", "Rejects a request (e.g. a follow request)", exe.FlagSet) 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)") @@ -403,7 +404,7 @@ func NewRemoveExecutor( accountNames: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("remove", "", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("remove", "Removes a resource from another resource", 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") @@ -455,7 +456,7 @@ func NewShowExecutor( attachmentIDs: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("show", "Shows details about a specified resource", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("show", "Shows details about a specified resource", exe.FlagSet) 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") @@ -503,7 +504,7 @@ func NewSwitchExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("switch", "Performs a switch operation (e.g. switching between logged in accounts)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("switch", "Performs a switch operation (e.g. switching between logged in accounts)", exe.FlagSet) exe.Var(&exe.accountName, "account-name", "The name of the account") exe.StringVar(&exe.to, "to", "", "TBC") @@ -531,7 +532,7 @@ func NewUnblockExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("unblock", "Unblocks a resource (e.g. an account)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("unblock", "Unblocks a resource (e.g. an account)", exe.FlagSet) 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)") @@ -559,7 +560,7 @@ func NewUnfollowExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("unfollow", "Unfollow a resource (e.g. an account)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("unfollow", "Unfollows a resource (e.g. an account)", exe.FlagSet) 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)") @@ -587,7 +588,7 @@ func NewUnmuteExecutor( accountName: internalFlag.NewStringSliceValue(), } - exe.Usage = commandUsageFunc("unmute", "Umutes a specific resource (e.g. an account)", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("unmute", "Umutes a specific resource (e.g. an account)", exe.FlagSet) 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)") @@ -610,7 +611,7 @@ func NewVersionExecutor( printer: printer, } - exe.Usage = commandUsageFunc("version", "Prints the application's version and build information", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("version", "Prints the application's version and build information", exe.FlagSet) exe.BoolVar(&exe.full, "full", false, "Set to true to print the build information in full") @@ -634,7 +635,7 @@ func NewWhoamiExecutor( config: config, } - exe.Usage = commandUsageFunc("whoami", "Prints the account that you are currently logged into", exe.FlagSet) + exe.Usage = usage.ExecutorUsageFunc("whoami", "Prints the account that you are currently logged into", exe.FlagSet) return &exe } diff --git a/internal/usage/app.go b/internal/usage/app.go new file mode 100644 index 0000000..8ab4d09 --- /dev/null +++ b/internal/usage/app.go @@ -0,0 +1,53 @@ +package usage + +import ( + "flag" + "fmt" + "slices" + "strings" + "text/tabwriter" + + "codeflow.dananglin.me.uk/apollo/enbas/internal/version" +) + +func AppUsageFunc() func() { + cmds := make([]string, len(summaries)) + ind := 0 + + for k := range summaries { + cmds[ind] = k + ind++ + } + + slices.Sort(cmds) + + return func() { + var builder strings.Builder + + builder.WriteString("SUMMARY:\n enbas - A GoToSocial client for the terminal.\n\n") + + if version.BinaryVersion != "" { + builder.WriteString("VERSION:\n " + version.BinaryVersion + "\n\n") + } + + builder.WriteString("USAGE:\n enbas [flags]\n enbas [flags] [command]\n\nCOMMANDS:") + + tableWriter := tabwriter.NewWriter(&builder, 0, 8, 0, '\t', 0) + + for _, cmd := range cmds { + fmt.Fprintf(tableWriter, "\n %s\t%s", cmd, summaries[cmd]) + } + + tableWriter.Flush() + + builder.WriteString("\n\nFLAGS:\n --help\n print the help message") + flag.VisitAll(func(f *flag.Flag) { + fmt.Fprintf(&builder, "\n --%s\n %s", f.Name, f.Usage) + }) + + builder.WriteString("\n\nUse \"enbas [command] --help\" for more information about a command.\n") + + w := flag.CommandLine.Output() + fmt.Fprint(w, builder.String()) + } +} diff --git a/internal/usage/codegen.go b/internal/usage/codegen.go new file mode 100644 index 0000000..96bef50 --- /dev/null +++ b/internal/usage/codegen.go @@ -0,0 +1,3 @@ +package usage + +//go:generate go run ../../cmd/enbas-codegen --package usage --path-to-enbas-cli-schema ../../schema/enbas_cli_schema.json diff --git a/internal/executor/usage.go b/internal/usage/executor.go similarity index 83% rename from internal/executor/usage.go rename to internal/usage/executor.go index 67d2718..63bf890 100644 --- a/internal/executor/usage.go +++ b/internal/usage/executor.go @@ -1,4 +1,4 @@ -package executor +package usage import ( "flag" @@ -6,8 +6,8 @@ import ( "strings" ) -// commandUsageFunc returns the function used to print a command's help page. -func commandUsageFunc(name, summary string, flagset *flag.FlagSet) func() { +// ExecutorUsageFunc returns the function used to print a command's help page. +func ExecutorUsageFunc(name, summary string, flagset *flag.FlagSet) func() { return func() { var builder strings.Builder diff --git a/internal/usage/summaries.go b/internal/usage/summaries.go new file mode 100644 index 0000000..837f764 --- /dev/null +++ b/internal/usage/summaries.go @@ -0,0 +1,28 @@ +/* + This file is generated by the enbas-codegen + DO NOT EDIT. +*/ + +package usage + +var summaries = map[string]string{ + "accept": "Accepts a request (e.g. a follow request)", + "add": "Adds a resource to another resource", + "block": "Blocks a resource (e.g. an account)", + "create": "Creates a specific resource", + "delete": "Deletes a specific resource", + "edit": "Edit a specific resource", + "follow": "Follow a resource (e.g. an account)", + "init": "Creates a new configuration file in the specified configuration directory", + "login": "Logs into an account on GoToSocial", + "mute": "Mutes a specific resource (e.g. an account)", + "reject": "Rejects a request (e.g. a follow request)", + "remove": "Removes a resource from another resource", + "show": "Shows details about a specified resource", + "switch": "Performs a switch operation (e.g. switching between logged in accounts)", + "unblock": "Unblocks a resource (e.g. an account)", + "unfollow": "Unfollows a resource (e.g. an account)", + "unmute": "Umutes a specific resource (e.g. an account)", + "version": "Prints the application's version and build information", + "whoami": "Prints the account that you are currently logged into", +} diff --git a/schema/enbas_cli_schema.json b/schema/enbas_cli_schema.json index c1eb52f..68cd55f 100644 --- a/schema/enbas_cli_schema.json +++ b/schema/enbas_cli_schema.json @@ -221,7 +221,7 @@ { "flag": "type", "fieldName": "resourceType", "default": "" }, { "flag": "vote", "fieldName": "votes" } ], - "summary": "Add a resource to another resource", + "summary": "Adds a resource to another resource", "useConfig": true, "usePrinter": true }, @@ -346,7 +346,7 @@ { "flag": "status-id", "fieldName": "statusID", "default": "" }, { "flag": "type", "fieldName": "resourceType", "default": "" } ], - "summary": "", + "summary": "Removes a resource from another resource", "useConfig": true, "usePrinter": true }, @@ -406,7 +406,7 @@ { "flag": "account-name" }, { "flag": "type", "fieldName": "resourceType", "default": "" } ], - "summary": "Unfollow a resource (e.g. an account)", + "summary": "Unfollows a resource (e.g. an account)", "useConfig": true, "usePrinter": true },