2022-11-03 13:54:49 +00:00
|
|
|
package d2plugin
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"encoding/json"
|
|
|
|
|
"errors"
|
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
|
2022-12-01 23:33:48 +00:00
|
|
|
"github.com/spf13/pflag"
|
2022-12-01 19:19:39 +00:00
|
|
|
|
2022-11-03 13:54:49 +00:00
|
|
|
"oss.terrastruct.com/d2/d2graph"
|
2022-12-08 07:40:13 +00:00
|
|
|
"oss.terrastruct.com/util-go/xmain"
|
2022-11-03 13:54:49 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// Serve returns a xmain.RunFunc that will invoke the plugin p as necessary to service the
|
|
|
|
|
// calling d2 CLI.
|
|
|
|
|
//
|
|
|
|
|
// See implementation of d2plugin-dagre in the ./cmd directory.
|
|
|
|
|
//
|
|
|
|
|
// Also see execPlugin in exec.go for the d2 binary plugin protocol.
|
2022-12-01 22:10:48 +00:00
|
|
|
func Serve(p Plugin) xmain.RunFunc {
|
2022-11-03 13:54:49 +00:00
|
|
|
return func(ctx context.Context, ms *xmain.State) (err error) {
|
2023-04-30 04:24:03 +00:00
|
|
|
if !ms.Opts.Flags.Parsed() {
|
|
|
|
|
fs, err := p.Flags(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
for _, f := range fs {
|
|
|
|
|
f.AddToOpts(ms.Opts)
|
|
|
|
|
}
|
|
|
|
|
err = ms.Opts.Flags.Parse(ms.Opts.Args)
|
|
|
|
|
if !errors.Is(err, pflag.ErrHelp) && err != nil {
|
|
|
|
|
return xmain.UsageErrorf("failed to parse flags: %v", err)
|
|
|
|
|
}
|
|
|
|
|
if errors.Is(err, pflag.ErrHelp) {
|
|
|
|
|
// At some point we want to write a friendly help.
|
|
|
|
|
return info(ctx, p, ms)
|
|
|
|
|
}
|
2022-12-01 23:33:48 +00:00
|
|
|
}
|
|
|
|
|
|
2022-11-17 00:48:46 +00:00
|
|
|
if len(ms.Opts.Flags.Args()) < 1 {
|
2022-12-01 23:33:48 +00:00
|
|
|
return xmain.UsageErrorf("expected first argument to be subcmd name")
|
2022-11-03 13:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
2022-12-31 01:30:25 +00:00
|
|
|
err = HydratePluginOpts(ctx, ms, p)
|
2022-12-31 01:28:21 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-01 23:33:48 +00:00
|
|
|
subcmd := ms.Opts.Flags.Arg(0)
|
|
|
|
|
switch subcmd {
|
2022-11-03 13:54:49 +00:00
|
|
|
case "info":
|
|
|
|
|
return info(ctx, p, ms)
|
2022-12-30 20:06:40 +00:00
|
|
|
case "flags":
|
|
|
|
|
return flags(ctx, p, ms)
|
2022-11-03 13:54:49 +00:00
|
|
|
case "layout":
|
|
|
|
|
return layout(ctx, p, ms)
|
|
|
|
|
case "postprocess":
|
|
|
|
|
return postProcess(ctx, p, ms)
|
2024-04-16 00:31:27 +00:00
|
|
|
case "routeedges":
|
|
|
|
|
routingPlugin, ok := p.(RoutingPlugin)
|
|
|
|
|
if !ok {
|
|
|
|
|
return fmt.Errorf("plugin has routing feature but does not implement RoutingPlugin")
|
|
|
|
|
}
|
|
|
|
|
return routeEdges(ctx, routingPlugin, ms)
|
2022-11-03 13:54:49 +00:00
|
|
|
default:
|
2022-12-01 23:33:48 +00:00
|
|
|
return xmain.UsageErrorf("unrecognized command: %s", subcmd)
|
2022-11-03 13:54:49 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func info(ctx context.Context, p Plugin, ms *xmain.State) error {
|
|
|
|
|
info, err := p.Info(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
b, err := json.Marshal(info)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, err = ms.Stdout.Write(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-12-30 20:06:40 +00:00
|
|
|
func flags(ctx context.Context, p Plugin, ms *xmain.State) error {
|
|
|
|
|
flags, err := p.Flags(ctx)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
b, err := json.Marshal(flags)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, err = ms.Stdout.Write(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-03 13:54:49 +00:00
|
|
|
func layout(ctx context.Context, p Plugin, ms *xmain.State) error {
|
|
|
|
|
in, err := io.ReadAll(ms.Stdin)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
var g d2graph.Graph
|
|
|
|
|
if err := d2graph.DeserializeGraph(in, &g); err != nil {
|
|
|
|
|
return fmt.Errorf("failed to unmarshal input to graph: %s", in)
|
|
|
|
|
}
|
|
|
|
|
err = p.Layout(ctx, &g)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
b, err := d2graph.SerializeGraph(&g)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, err = ms.Stdout.Write(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func postProcess(ctx context.Context, p Plugin, ms *xmain.State) error {
|
|
|
|
|
in, err := io.ReadAll(ms.Stdin)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
out, err := p.PostProcess(ctx, in)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_, err = ms.Stdout.Write(out)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2024-04-16 00:31:27 +00:00
|
|
|
|
|
|
|
|
func routeEdges(ctx context.Context, p RoutingPlugin, ms *xmain.State) error {
|
|
|
|
|
inRaw, err := io.ReadAll(ms.Stdin)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2024-04-16 00:46:45 +00:00
|
|
|
var in routeEdgesInput
|
2024-04-16 00:31:27 +00:00
|
|
|
err = json.Unmarshal(inRaw, &in)
|
2024-04-16 00:46:45 +00:00
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
2024-04-16 00:31:27 +00:00
|
|
|
|
|
|
|
|
var g d2graph.Graph
|
2024-04-16 05:10:50 +00:00
|
|
|
if err := d2graph.DeserializeGraph(in.G, &g); err != nil {
|
2024-04-16 00:31:27 +00:00
|
|
|
return fmt.Errorf("failed to unmarshal input graph to graph: %s", in)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var gedges d2graph.Graph
|
2024-04-16 05:10:50 +00:00
|
|
|
if err := d2graph.DeserializeGraph(in.GEdges, &gedges); err != nil {
|
2024-04-16 00:31:27 +00:00
|
|
|
return fmt.Errorf("failed to unmarshal input edges graph to graph: %s", in)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
err = p.RouteEdges(ctx, &g, gedges.Edges)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
b, err := d2graph.SerializeGraph(&g)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
_, err = ms.Stdout.Write(b)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|