parent
296b6407e9
commit
5b4a20a6c6
4 changed files with 106 additions and 86 deletions
|
|
@ -40,9 +40,10 @@ import (
|
|||
type execPlugin struct {
|
||||
path string
|
||||
opts map[string]string
|
||||
info *PluginInfo
|
||||
}
|
||||
|
||||
func (p execPlugin) Flags(ctx context.Context) (_ []PluginSpecificFlag, err error) {
|
||||
func (p *execPlugin) Flags(ctx context.Context) (_ []PluginSpecificFlag, err error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, p.path, "flags")
|
||||
|
|
@ -92,7 +93,11 @@ func (p *execPlugin) HydrateOpts(opts []byte) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) {
|
||||
func (p *execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) {
|
||||
if p.info != nil {
|
||||
return p.info, nil
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Second*10)
|
||||
defer cancel()
|
||||
cmd := exec.CommandContext(ctx, p.path, "info")
|
||||
|
|
@ -114,10 +119,11 @@ func (p execPlugin) Info(ctx context.Context) (_ *PluginInfo, err error) {
|
|||
return nil, fmt.Errorf("failed to unmarshal json: %w", err)
|
||||
}
|
||||
|
||||
p.info = &info
|
||||
return &info, nil
|
||||
}
|
||||
|
||||
func (p execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||
func (p *execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
|
||||
|
|
@ -152,7 +158,7 @@ func (p execPlugin) Layout(ctx context.Context, g *d2graph.Graph) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (p execPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
||||
func (p *execPlugin) PostProcess(ctx context.Context, in []byte) ([]byte, error) {
|
||||
ctx, cancel := context.WithTimeout(ctx, time.Minute)
|
||||
defer cancel()
|
||||
|
||||
|
|
|
|||
|
|
@ -80,36 +80,54 @@ type PluginInfo struct {
|
|||
|
||||
const binaryPrefix = "d2plugin-"
|
||||
|
||||
func ListPlugins(ctx context.Context) ([]*PluginInfo, error) {
|
||||
func ListPlugins(ctx context.Context) ([]Plugin, error) {
|
||||
// 1. Run Info on all bundled plugins in the global plugins array.
|
||||
// - set Type for each bundled plugin to "bundled".
|
||||
// 2. Iterate through directories in $PATH and look for executables within these
|
||||
// directories with the prefix d2plugin-*
|
||||
// 3. Run each plugin binary with the argument info. e.g. d2plugin-dagre info
|
||||
|
||||
var infoSlice []*PluginInfo
|
||||
|
||||
for _, p := range plugins {
|
||||
info, err := p.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info.Type = "bundled"
|
||||
infoSlice = append(infoSlice, info)
|
||||
}
|
||||
var ps []Plugin
|
||||
ps = append(ps, plugins...)
|
||||
|
||||
matches, err := xexec.SearchPath(binaryPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
BINARY_PLUGINS_LOOP:
|
||||
for _, path := range matches {
|
||||
p := &execPlugin{path: path}
|
||||
info, err := p.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
info.Type = "binary"
|
||||
info.Path = path
|
||||
for _, p2 := range ps {
|
||||
info2, err := p2.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if info.Name == info2.Name {
|
||||
continue BINARY_PLUGINS_LOOP
|
||||
}
|
||||
}
|
||||
ps = append(ps, p)
|
||||
}
|
||||
return ps, nil
|
||||
}
|
||||
|
||||
func ListPluginInfos(ctx context.Context, ps []Plugin) ([]*PluginInfo, error) {
|
||||
var infoSlice []*PluginInfo
|
||||
for _, p := range ps {
|
||||
info, err := p.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ep, ok := p.(*execPlugin); ok {
|
||||
info.Type = "binary"
|
||||
info.Path = ep.path
|
||||
} else {
|
||||
info.Type = "bundled"
|
||||
}
|
||||
infoSlice = append(infoSlice, info)
|
||||
}
|
||||
|
||||
|
|
@ -123,41 +141,22 @@ func ListPlugins(ctx context.Context) ([]*PluginInfo, error) {
|
|||
// **NOTE** When D2 upgrades to go 1.19, remember to ignore exec.ErrDot
|
||||
// 3. If such a binary is found, it builds an execPlugin in exec.go
|
||||
// to get a plugin implementation around the binary and returns it.
|
||||
func FindPlugin(ctx context.Context, name string) (Plugin, string, error) {
|
||||
for _, p := range plugins {
|
||||
func FindPlugin(ctx context.Context, ps []Plugin, name string) (Plugin, error) {
|
||||
for _, p := range ps {
|
||||
info, err := p.Info(ctx)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if info.Name == name {
|
||||
return p, "", nil
|
||||
}
|
||||
}
|
||||
|
||||
path, err := exec.LookPath(binaryPrefix + name)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
return &execPlugin{path: path}, path, nil
|
||||
}
|
||||
|
||||
func ListPluginFlags(ctx context.Context) ([]PluginSpecificFlag, error) {
|
||||
var out []PluginSpecificFlag
|
||||
for _, p := range plugins {
|
||||
flags, err := p.Flags(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out = append(out, flags...)
|
||||
if info.Name == name {
|
||||
return p, nil
|
||||
}
|
||||
}
|
||||
return nil, exec.ErrNotFound
|
||||
}
|
||||
|
||||
matches, err := xexec.SearchPath(binaryPrefix)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, path := range matches {
|
||||
p := &execPlugin{path: path}
|
||||
func ListPluginFlags(ctx context.Context, ps []Plugin) ([]PluginSpecificFlag, error) {
|
||||
var out []PluginSpecificFlag
|
||||
for _, p := range ps {
|
||||
flags, err := p.Flags(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
|
|
|||
62
help.go
62
help.go
|
|
@ -39,23 +39,23 @@ See more docs and the source code at https://oss.terrastruct.com/d2
|
|||
`, filepath.Base(ms.Name), ms.Opts.Defaults())
|
||||
}
|
||||
|
||||
func layoutCmd(ctx context.Context, ms *xmain.State) error {
|
||||
func layoutCmd(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
|
||||
if len(ms.Opts.Flags.Args()) == 1 {
|
||||
return shortLayoutHelp(ctx, ms)
|
||||
return shortLayoutHelp(ctx, ms, ps)
|
||||
} else if len(ms.Opts.Flags.Args()) == 2 {
|
||||
return longLayoutHelp(ctx, ms)
|
||||
return longLayoutHelp(ctx, ms, ps)
|
||||
} else {
|
||||
return pluginSubcommand(ctx, ms)
|
||||
return pluginSubcommand(ctx, ms, ps)
|
||||
}
|
||||
}
|
||||
|
||||
func shortLayoutHelp(ctx context.Context, ms *xmain.State) error {
|
||||
func shortLayoutHelp(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
|
||||
var pluginLines []string
|
||||
plugins, err := d2plugin.ListPlugins(ctx)
|
||||
pinfos, err := d2plugin.ListPluginInfos(ctx, ps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range plugins {
|
||||
for _, p := range pinfos {
|
||||
var l string
|
||||
if p.Type == "bundled" {
|
||||
l = fmt.Sprintf("%s (bundled) - %s", p.Name, p.ShortHelp)
|
||||
|
|
@ -82,40 +82,43 @@ See more docs at https://oss.terrastruct.com/d2
|
|||
return nil
|
||||
}
|
||||
|
||||
func longLayoutHelp(ctx context.Context, ms *xmain.State) error {
|
||||
func longLayoutHelp(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
|
||||
layout := ms.Opts.Flags.Arg(1)
|
||||
plugin, path, err := d2plugin.FindPlugin(ctx, layout)
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, layout)
|
||||
plugin, err := d2plugin.FindPlugin(ctx, ps, layout)
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, ps, layout)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
pluginLocation := "bundled"
|
||||
if path != "" {
|
||||
pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path))
|
||||
}
|
||||
|
||||
pluginInfo, err := plugin.Info(ctx)
|
||||
pinfo, err := plugin.Info(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(pluginInfo.LongHelp, "\n") {
|
||||
pluginInfo.LongHelp += "\n"
|
||||
plocation := pinfo.Type
|
||||
if pinfo.Type == "binary" {
|
||||
plocation = fmt.Sprintf("executable plugin at %s", humanPath(pinfo.Path))
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(pinfo.LongHelp, "\n") {
|
||||
pinfo.LongHelp += "\n"
|
||||
}
|
||||
fmt.Fprintf(ms.Stdout, `%s (%s):
|
||||
|
||||
%s`, pluginInfo.Name, pluginLocation, pluginInfo.LongHelp)
|
||||
%s`, pinfo.Name, plocation, pinfo.LongHelp)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func layoutNotFound(ctx context.Context, layout string) error {
|
||||
var names []string
|
||||
plugins, err := d2plugin.ListPlugins(ctx)
|
||||
func layoutNotFound(ctx context.Context, ps []d2plugin.Plugin, layout string) error {
|
||||
pinfos, err := d2plugin.ListPluginInfos(ctx, ps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, p := range plugins {
|
||||
var names []string
|
||||
for _, p := range pinfos {
|
||||
names = append(names, p.Name)
|
||||
}
|
||||
|
||||
|
|
@ -126,11 +129,14 @@ For more information on setup, please visit https://github.com/terrastruct/d2.`,
|
|||
layout, strings.Join(names, ", "))
|
||||
}
|
||||
|
||||
func pluginSubcommand(ctx context.Context, ms *xmain.State) error {
|
||||
func pluginSubcommand(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
|
||||
layout := ms.Opts.Flags.Arg(1)
|
||||
plugin, _, err := d2plugin.FindPlugin(ctx, layout)
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, layout)
|
||||
plugin, err := d2plugin.FindPlugin(ctx, ps, layout)
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, ps, layout)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
ms.Opts.Args = ms.Opts.Flags.Args()[2:]
|
||||
|
|
|
|||
33
main.go
33
main.go
|
|
@ -75,7 +75,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
err = populateLayoutOpts(ctx, ms)
|
||||
ps, err := d2plugin.ListPlugins(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = populateLayoutOpts(ctx, ms, ps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -93,7 +97,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
if len(ms.Opts.Flags.Args()) > 0 {
|
||||
switch ms.Opts.Flags.Arg(0) {
|
||||
case "layout":
|
||||
return layoutCmd(ctx, ms)
|
||||
return layoutCmd(ctx, ms, ps)
|
||||
case "fmt":
|
||||
return fmtCmd(ctx, ms)
|
||||
case "version":
|
||||
|
|
@ -142,10 +146,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
}
|
||||
ms.Log.Debug.Printf("using theme %s (ID: %d)", match.Name, *themeFlag)
|
||||
|
||||
plugin, path, err := d2plugin.FindPlugin(ctx, *layoutFlag)
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, *layoutFlag)
|
||||
} else if err != nil {
|
||||
plugin, err := d2plugin.FindPlugin(ctx, ps, *layoutFlag)
|
||||
if err != nil {
|
||||
if errors.Is(err, exec.ErrNotFound) {
|
||||
return layoutNotFound(ctx, ps, *layoutFlag)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
@ -154,11 +159,15 @@ func run(ctx context.Context, ms *xmain.State) (err error) {
|
|||
return err
|
||||
}
|
||||
|
||||
pluginLocation := "bundled"
|
||||
if path != "" {
|
||||
pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path))
|
||||
pinfo, err := plugin.Info(ctx)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ms.Log.Debug.Printf("using layout plugin %s (%s)", *layoutFlag, pluginLocation)
|
||||
plocation := pinfo.Type
|
||||
if pinfo.Type == "binary" {
|
||||
plocation = fmt.Sprintf("executable plugin at %s", humanPath(pinfo.Path))
|
||||
}
|
||||
ms.Log.Debug.Printf("using layout plugin %s (%s)", *layoutFlag, plocation)
|
||||
|
||||
var pw png.Playwright
|
||||
if filepath.Ext(outputPath) == ".png" {
|
||||
|
|
@ -299,8 +308,8 @@ func DiscardSlog(ctx context.Context) context.Context {
|
|||
return ctxlog.With(ctx, slog.Make(sloghuman.Sink(io.Discard)))
|
||||
}
|
||||
|
||||
func populateLayoutOpts(ctx context.Context, ms *xmain.State) error {
|
||||
pluginFlags, err := d2plugin.ListPluginFlags(ctx)
|
||||
func populateLayoutOpts(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
|
||||
pluginFlags, err := d2plugin.ListPluginFlags(ctx, ps)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue