...

Source file src/oss.terrastruct.com/d2/d2cli/help.go

Documentation: oss.terrastruct.com/d2/d2cli

     1  package d2cli
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"os"
     8  	"os/exec"
     9  	"path/filepath"
    10  	"strings"
    11  
    12  	"oss.terrastruct.com/util-go/xmain"
    13  
    14  	"oss.terrastruct.com/d2/d2plugin"
    15  	"oss.terrastruct.com/d2/d2themes/d2themescatalog"
    16  	"oss.terrastruct.com/d2/lib/version"
    17  )
    18  
    19  func help(ms *xmain.State) {
    20  	fmt.Fprintf(ms.Stdout, `%[1]s %[2]s
    21  Usage:
    22    %[1]s [--watch=false] [--theme=0] file.d2 [file.svg | file.png]
    23    %[1]s layout [name]
    24    %[1]s fmt file.d2 ...
    25  
    26  %[1]s compiles and renders file.d2 to file.svg | file.png
    27  It defaults to file.svg if an output path is not provided.
    28  
    29  Use - to have d2 read from stdin or write to stdout.
    30  
    31  See man d2 for more detailed docs.
    32  
    33  Flags:
    34  %[3]s
    35  
    36  Subcommands:
    37    %[1]s layout - Lists available layout engine options with short help
    38    %[1]s layout [name] - Display long help for a particular layout engine, including its configuration options
    39    %[1]s themes - Lists available themes
    40    %[1]s fmt file.d2 ... - Format passed files
    41  
    42  See more docs and the source code at https://oss.terrastruct.com/d2.
    43  Hosted icons at https://icons.terrastruct.com.
    44  Playground runner at https://play.d2lang.com.
    45  `, filepath.Base(ms.Name), version.Version, ms.Opts.Defaults())
    46  }
    47  
    48  func layoutCmd(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
    49  	if len(ms.Opts.Flags.Args()) == 1 {
    50  		return shortLayoutHelp(ctx, ms, ps)
    51  	} else if len(ms.Opts.Flags.Args()) == 2 {
    52  		return longLayoutHelp(ctx, ms, ps)
    53  	} else {
    54  		return pluginSubcommand(ctx, ms, ps)
    55  	}
    56  }
    57  
    58  func themesCmd(ctx context.Context, ms *xmain.State) {
    59  	fmt.Fprintf(ms.Stdout, "Available themes:\n%s", d2themescatalog.CLIString())
    60  }
    61  
    62  func shortLayoutHelp(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
    63  	var pluginLines []string
    64  	pinfos, err := d2plugin.ListPluginInfos(ctx, ps)
    65  	if err != nil {
    66  		return err
    67  	}
    68  	for _, p := range pinfos {
    69  		var l string
    70  		if p.Type == "bundled" {
    71  			l = fmt.Sprintf("%s (bundled) - %s", p.Name, p.ShortHelp)
    72  		} else {
    73  			l = fmt.Sprintf("%s (%s) - %s", p.Name, humanPath(p.Path), p.ShortHelp)
    74  		}
    75  		pluginLines = append(pluginLines, l)
    76  	}
    77  	fmt.Fprintf(ms.Stdout, `Available layout engines found:
    78  
    79  %s
    80  
    81  Usage:
    82    To use a particular layout engine, set the environment variable D2_LAYOUT=[name] or flag --layout=[name].
    83  
    84  Example:
    85    D2_LAYOUT=dagre d2 in.d2 out.svg
    86  
    87  Subcommands:
    88    %s layout [layout name] - Display long help for a particular layout engine, including its configuration options
    89  
    90  See more docs at https://d2lang.com/tour/layouts
    91  `, strings.Join(pluginLines, "\n"), ms.Name)
    92  	return nil
    93  }
    94  
    95  func longLayoutHelp(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
    96  	layout := ms.Opts.Flags.Arg(1)
    97  	plugin, err := d2plugin.FindPlugin(ctx, ps, layout)
    98  	if err != nil {
    99  		if errors.Is(err, exec.ErrNotFound) {
   100  			return layoutNotFound(ctx, ps, layout)
   101  		}
   102  		return err
   103  	}
   104  
   105  	pinfo, err := plugin.Info(ctx)
   106  	if err != nil {
   107  		return err
   108  	}
   109  
   110  	plocation := pinfo.Type
   111  	if pinfo.Type == "binary" {
   112  		plocation = fmt.Sprintf("executable plugin at %s", humanPath(pinfo.Path))
   113  	}
   114  
   115  	if !strings.HasSuffix(pinfo.LongHelp, "\n") {
   116  		pinfo.LongHelp += "\n"
   117  	}
   118  	fmt.Fprintf(ms.Stdout, `%s (%s):
   119  
   120  %s`, pinfo.Name, plocation, pinfo.LongHelp)
   121  
   122  	return nil
   123  }
   124  
   125  func layoutNotFound(ctx context.Context, ps []d2plugin.Plugin, layout string) error {
   126  	pinfos, err := d2plugin.ListPluginInfos(ctx, ps)
   127  	if err != nil {
   128  		return err
   129  	}
   130  	var names []string
   131  	for _, p := range pinfos {
   132  		names = append(names, p.Name)
   133  	}
   134  
   135  	return xmain.UsageErrorf(`D2_LAYOUT "%s" is not bundled and could not be found in your $PATH.
   136  The available options are: %s. For details on each option, run "d2 layout".
   137  
   138  For more information on setup, please visit https://github.com/terrastruct/d2.`,
   139  		layout, strings.Join(names, ", "))
   140  }
   141  
   142  func pluginSubcommand(ctx context.Context, ms *xmain.State, ps []d2plugin.Plugin) error {
   143  	layout := ms.Opts.Flags.Arg(1)
   144  	plugin, err := d2plugin.FindPlugin(ctx, ps, layout)
   145  	if err != nil {
   146  		if errors.Is(err, exec.ErrNotFound) {
   147  			return layoutNotFound(ctx, ps, layout)
   148  		}
   149  		return err
   150  	}
   151  
   152  	ms.Opts.Args = ms.Opts.Flags.Args()[2:]
   153  	return d2plugin.Serve(plugin)(ctx, ms)
   154  }
   155  
   156  func humanPath(fp string) string {
   157  	if strings.HasPrefix(fp, os.Getenv("HOME")) {
   158  		return filepath.Join("~", strings.TrimPrefix(fp, os.Getenv("HOME")))
   159  	}
   160  	return fp
   161  }
   162  

View as plain text