Skip to content

Commit

Permalink
✨ Add glaze structured command output
Browse files Browse the repository at this point in the history
  • Loading branch information
wesen committed Aug 6, 2023
1 parent 057192c commit 3d8fb67
Show file tree
Hide file tree
Showing 5 changed files with 204 additions and 71 deletions.
82 changes: 82 additions & 0 deletions cmd/mastoid/cmds/render.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package cmds

import (
"context"
"encoding/json"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg/render"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg/render/html"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg/render/plaintext"
"github.com/mattn/go-mastodon"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"os"
)

var RenderCmd = &cobra.Command{
Use: "render",
Short: "Retrieves a thread from a Mastodon instance and renders it",
Run: func(cmd *cobra.Command, args []string) {
statusID, _ := cmd.Flags().GetString("status-id")
verbose, _ := cmd.Flags().GetBool("verbose")
output, _ := cmd.Flags().GetString("output")
withHeader, _ := cmd.Flags().GetBool("with-header")

statusID = pkg.ExtractID(statusID)
if statusID == "" {
log.Error().Msg("No status ID provided")
os.Exit(1)
}

ctx := context.Background()

credentials, err := pkg.LoadCredentials()
cobra.CheckErr(err)

client, err := pkg.CreateClientAndAuthenticate(ctx, credentials)
cobra.CheckErr(err)

status, err := client.GetStatus(ctx, mastodon.ID(statusID))
if err != nil {
log.Error().Err(err).Str("statusId", statusID).Msg("Could not get status")
}
cobra.CheckErr(err)

context, err := client.GetStatusContext(ctx, status.ID)
cobra.CheckErr(err)

var renderer render.Renderer

switch output {
case "json":
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
err = encoder.Encode(context)
cobra.CheckErr(err)
return
case "html":

renderer = html.NewRenderer(
html.WithVerbose(verbose),
html.WithHeader(withHeader),
)
case "text":
renderer = plaintext.NewRenderer(
plaintext.WithVerbose(verbose),
plaintext.WithHeader(withHeader),
)

case "markdown":
renderer = plaintext.NewRenderer(
plaintext.WithVerbose(verbose),
plaintext.WithHeader(withHeader),
plaintext.WithMarkdown(),
)
default:
cobra.CheckErr("Unknown output format")
}

err = renderer.RenderThread(os.Stdout, status, context)
cobra.CheckErr(err)
},
}
164 changes: 103 additions & 61 deletions cmd/mastoid/cmds/thread.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,81 +2,123 @@ package cmds

import (
"context"
"encoding/json"
"github.com/go-go-golems/glazed/pkg/cmds"
"github.com/go-go-golems/glazed/pkg/cmds/layers"
"github.com/go-go-golems/glazed/pkg/cmds/parameters"
"github.com/go-go-golems/glazed/pkg/middlewares"
"github.com/go-go-golems/glazed/pkg/settings"
"github.com/go-go-golems/glazed/pkg/types"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg/render"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg/render/html"
"github.com/go-go-golems/go-go-labs/cmd/mastoid/pkg/render/plaintext"
"github.com/mattn/go-mastodon"
"github.com/rs/zerolog/log"
"github.com/spf13/cobra"
"os"
"strings"
"github.com/pkg/errors"
)

var ThreadCmd = &cobra.Command{
Use: "thread",
Short: "Retrieves a thread from a Mastodon instance",
Run: func(cmd *cobra.Command, args []string) {
statusID, _ := cmd.Flags().GetString("status-id")
verbose, _ := cmd.Flags().GetBool("verbose")
output, _ := cmd.Flags().GetString("output")
withHeader, _ := cmd.Flags().GetBool("with-header")

// extract statusID from URL if we have a URL
if strings.Contains(statusID, "http") {
statusID = strings.Split(statusID, "/")[4]
}
type ThreadCmd struct {
description *cmds.CommandDescription
}

ctx := context.Background()
func NewThreadCommand() (*ThreadCmd, error) {
glazedParameterLayer, err := settings.NewGlazedParameterLayers()
if err != nil {
return nil, errors.Wrap(err, "could not create Glazed parameter layer")
}

credentials, err := pkg.LoadCredentials()
cobra.CheckErr(err)
return &ThreadCmd{
description: cmds.NewCommandDescription(
"thread",
cmds.WithShort("Output thread as structured data"),
cmds.WithFlags(
parameters.NewParameterDefinition(
"status",
parameters.ParameterTypeString,
parameters.WithHelp("Thread status id"),
parameters.WithShortFlag("s"),
),
parameters.NewParameterDefinition(
"verbose",
parameters.ParameterTypeBool,
parameters.WithHelp("Verbose output"),
parameters.WithShortFlag("v"),
parameters.WithDefault(false),
),
),
cmds.WithLayers(glazedParameterLayer),
),
}, nil
}

client, err := pkg.CreateClientAndAuthenticate(ctx, credentials)
cobra.CheckErr(err)
func (c *ThreadCmd) Description() *cmds.CommandDescription {
return c.description
}

status, err := client.GetStatus(ctx, mastodon.ID(statusID))
if err != nil {
log.Error().Err(err).Str("statusId", statusID).Msg("Could not get status")
}
cobra.CheckErr(err)
func (c *ThreadCmd) Run(
ctx context.Context,
parsedLayers map[string]*layers.ParsedParameterLayer,
ps map[string]interface{},
gp middlewares.Processor,
) error {
status := ps["status"].(string)

context, err := client.GetStatusContext(ctx, status.ID)
cobra.CheckErr(err)
statusID := pkg.ExtractID(status)
if statusID == "" {
return errors.New("no status ID provided")
}

var renderer render.Renderer
credentials, err := pkg.LoadCredentials()
if err != nil {
return errors.Wrap(err, "could not load credentials")
}

switch output {
case "json":
encoder := json.NewEncoder(os.Stdout)
encoder.SetIndent("", " ")
err = encoder.Encode(context)
cobra.CheckErr(err)
return
case "html":
client, err := pkg.CreateClientAndAuthenticate(ctx, credentials)
if err != nil {
return errors.Wrap(err, "could not create client")
}

renderer = html.NewRenderer(
html.WithVerbose(verbose),
html.WithHeader(withHeader),
)
case "text":
renderer = plaintext.NewRenderer(
plaintext.WithVerbose(verbose),
plaintext.WithHeader(withHeader),
)
status_, err := client.GetStatus(ctx, mastodon.ID(statusID))
if err != nil {
return err
}

case "markdown":
renderer = plaintext.NewRenderer(
plaintext.WithVerbose(verbose),
plaintext.WithHeader(withHeader),
plaintext.WithMarkdown(),
context, err := client.GetStatusContext(ctx, status_.ID)
if err != nil {
return err
}

thread := &pkg.Thread{
Nodes: map[mastodon.ID]*pkg.Node{},
}

thread.AddStatus(status_)
thread.AddContextAndGetMissingIDs(status_.ID, context)

verbose := ps["verbose"].(bool)

printNode := func(node *pkg.Node, depth int, siblingIdx int) error {
var row types.Row
if verbose {
row = types.NewRowFromStruct(node.Status, true)
} else {
row = types.NewRow(
types.MRP("ID", node.Status.ID),
types.MRP("CreatedAt", node.Status.CreatedAt),
types.MRP("Author", node.Status.Account.Acct),
types.MRP("Content", node.Status.Content),
types.MRP("InReplyToID", node.Status.InReplyToID),
)
default:
cobra.CheckErr("Unknown output format")
}
row.Set("Depth", depth)
row.Set("SiblingIndex", siblingIdx)
err = gp.AddRow(ctx, row)
if err != nil {
return err
}
return nil
}

err = thread.WalkDepthFirst(printNode)
if err != nil {
return err
}

err = renderer.RenderThread(os.Stdout, status, context)
cobra.CheckErr(err)
},
return nil
}
17 changes: 12 additions & 5 deletions cmd/mastoid/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package main
import (
"fmt"
clay "github.com/go-go-golems/clay/pkg"
"github.com/go-go-golems/glazed/pkg/cli"
"github.com/go-go-golems/glazed/pkg/help"
cmds "github.com/go-go-golems/go-go-labs/cmd/mastoid/cmds"
"github.com/spf13/cobra"
Expand Down Expand Up @@ -39,11 +40,11 @@ func main() {
_, err := initRootCmd()
cobra.CheckErr(err)

cmds.ThreadCmd.Flags().StringP("status-id", "s", "", "Status ID")
cmds.ThreadCmd.Flags().BoolP("verbose", "v", false, "Verbose output")
cmds.ThreadCmd.Flags().String("output", "markdown", "Output format (html, text, markdown, json)")
cmds.ThreadCmd.Flags().Bool("with-header", true, "Print header")
rootCmd.AddCommand(cmds.ThreadCmd)
cmds.RenderCmd.Flags().StringP("status-id", "s", "", "Status ID")
cmds.RenderCmd.Flags().BoolP("verbose", "v", false, "Verbose output")
cmds.RenderCmd.Flags().String("output", "markdown", "Output format (html, text, markdown, json)")
cmds.RenderCmd.Flags().Bool("with-header", true, "Print header")
rootCmd.AddCommand(cmds.RenderCmd)

cmds.RegisterCmd.Flags().StringP("client-name", "n", "mastoid", "Client name")
cmds.RegisterCmd.Flags().StringP("redirect-uris", "r", "urn:ietf:wg:oauth:2.0:oob", "Redirect URIs")
Expand All @@ -57,6 +58,12 @@ func main() {

rootCmd.AddCommand(cmds.VerifyCmd)

threadCmd, err := cmds.NewThreadCommand()
cobra.CheckErr(err)
command, err := cli.BuildCobraCommandFromGlazeCommand(threadCmd)
cobra.CheckErr(err)
rootCmd.AddCommand(command)

if err := rootCmd.Execute(); err != nil {
fmt.Println(err)
}
Expand Down
7 changes: 7 additions & 0 deletions cmd/mastoid/pkg/helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"github.com/mattn/go-mastodon"
"github.com/rs/zerolog/log"
"regexp"
)

func CreateClient(credentials *Credentials) (*mastodon.Client, error) {
Expand Down Expand Up @@ -52,3 +53,9 @@ func CreateClientAndAuthenticate(ctx context.Context, credentials *Credentials)

return client, nil
}

// ExtractID matches [0-9]+ in a string and returns the matched value
func ExtractID(status string) string {
reg := regexp.MustCompile(`[0-9]+`)
return reg.FindString(status)
}
5 changes: 0 additions & 5 deletions cmd/mastoid/pkg/render/plaintext/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,6 @@ func (r *Renderer) RenderStatus(w io.Writer, status *mastodon.Status) error {
}

func (r *Renderer) RenderThread(w io.Writer, status *mastodon.Status, context *mastodon.Context) error {

thread := &pkg.Thread{
Nodes: map[mastodon.ID]*pkg.Node{},
}
Expand Down Expand Up @@ -223,10 +222,6 @@ func (r *Renderer) RenderThread(w io.Writer, status *mastodon.Status, context *m
Int("siblingIdx", siblingIdx).
Msg("rendering")

indentDepth := depth - 1
if indentDepth < 0 {
indentDepth = 0
}
prefix = r.prefix + strings.Repeat(r.indent, siblingIdx)
prevDepth = depth

Expand Down

0 comments on commit 3d8fb67

Please sign in to comment.