From 4d4a00743d71904ad8a65276b3711d4550cf57b5 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Mon, 14 Nov 2022 16:35:22 -0800 Subject: [PATCH 1/9] 2022-11-14 04:35:22PM --- cmd/d2/help.go | 14 ++--- cmd/d2/main.go | 52 ++++++++---------- cmd/d2/watch.go | 6 ++- d2plugin/serve.go | 6 +-- lib/xmain/opts.go | 132 +++++++++++++++++++++++++++++++++++++++++++++ lib/xmain/xmain.go | 24 ++------- 6 files changed, 174 insertions(+), 60 deletions(-) create mode 100644 lib/xmain/opts.go diff --git a/cmd/d2/help.go b/cmd/d2/help.go index 961c9c275..3e18a56cf 100644 --- a/cmd/d2/help.go +++ b/cmd/d2/help.go @@ -28,13 +28,13 @@ Subcommands: %[1]s layout [layout name] - Display long help for a particular layout engine See more docs and the source code at https://oss.terrastruct.com/d2 -`, ms.Name, ms.FlagHelp()) +`, ms.Name, ms.Opts.Help()) } func layoutHelp(ctx context.Context, ms *xmain.State) error { - if len(ms.FlagSet.Args()) == 1 { + if len(ms.Opts.Args()) == 1 { return shortLayoutHelp(ctx, ms) - } else if len(ms.FlagSet.Args()) == 2 { + } else if len(ms.Opts.Args()) == 2 { return longLayoutHelp(ctx, ms) } else { return pluginSubcommand(ctx, ms) @@ -61,7 +61,7 @@ func shortLayoutHelp(ctx context.Context, ms *xmain.State) error { %s Usage: - To use a particular layout engine, set the environment variable D2_LAYOUT=[layout name]. + To use a particular layout engine, set the environment variable D2_LAYOUT=[name] or flag --layout=[name]. Example: D2_LAYOUT=dagre d2 in.d2 out.svg @@ -75,7 +75,7 @@ See more docs at https://oss.terrastruct.com/d2 } func longLayoutHelp(ctx context.Context, ms *xmain.State) error { - layout := ms.FlagSet.Arg(1) + layout := ms.Opts.Arg(1) plugin, path, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) @@ -119,13 +119,13 @@ For more information on setup, please visit https://github.com/terrastruct/d2.`, } func pluginSubcommand(ctx context.Context, ms *xmain.State) error { - layout := ms.FlagSet.Arg(1) + layout := ms.Opts.Arg(1) plugin, _, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) } - ms.Args = ms.FlagSet.Args()[2:] + ms.Opts.SetArgs(ms.Opts.Args()[2:]) return d2plugin.Serve(plugin)(ctx, ms) } diff --git a/cmd/d2/main.go b/cmd/d2/main.go index 6ce4d5459..d2f1ecdfd 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -6,7 +6,6 @@ import ( "fmt" "os/exec" "path/filepath" - "strconv" "strings" "time" @@ -32,19 +31,20 @@ func run(ctx context.Context, ms *xmain.State) (err error) { // :( ctx = xmain.DiscardSlog(ctx) - watchFlag := ms.FlagSet.BoolP("watch", "w", false, "watch for changes to input and live reload. Use $PORT and $HOST to specify the listening address.\n$D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0") - themeFlag := ms.FlagSet.Int64P("theme", "t", 0, "set the diagram theme. For a list of available options, see https://oss.terrastruct.com/d2") - bundleFlag := ms.FlagSet.BoolP("bundle", "b", true, "bundle all assets and layers into the output svg") - versionFlag := ms.FlagSet.BoolP("version", "v", false, "get the version and check for updates") - debugFlag := ms.FlagSet.BoolP("debug", "d", false, "print debug logs") - err = ms.FlagSet.Parse(ms.Args) + watchFlag := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") + bundleFlag := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") + debugFlag := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.") + layoutFlag := ms.Opts.String("D2_LAYOUT", "layout", "l", "dagre", `the layout engine used.`) + themeFlag := ms.Opts.Int64("D2_THEME", "theme", "t", 0, "the diagram theme ID. For a list of available options, see https://oss.terrastruct.com/d2") + versionFlag := ms.Opts.Bool("", "version", "v", false, "get the version and check for updates") + err = ms.Opts.Parse() if !errors.Is(err, pflag.ErrHelp) && err != nil { return xmain.UsageErrorf("failed to parse flags: %v", err) } - if len(ms.FlagSet.Args()) > 0 { - switch ms.FlagSet.Arg(0) { + if len(ms.Opts.Args()) > 0 { + switch ms.Opts.Arg(0) { case "layout": return layoutHelp(ctx, ms) } @@ -62,25 +62,25 @@ func run(ctx context.Context, ms *xmain.State) (err error) { var inputPath string var outputPath string - if len(ms.FlagSet.Args()) == 0 { + if len(ms.Opts.Args()) == 0 { if versionFlag != nil && *versionFlag { version.CheckVersion(ctx, ms.Log) return nil } help(ms) return nil - } else if len(ms.FlagSet.Args()) >= 3 { + } else if len(ms.Opts.Args()) >= 3 { return xmain.UsageErrorf("too many arguments passed") } - if len(ms.FlagSet.Args()) >= 1 { - if ms.FlagSet.Arg(0) == "version" { + if len(ms.Opts.Args()) >= 1 { + if ms.Opts.Arg(0) == "version" { version.CheckVersion(ctx, ms.Log) return nil } - inputPath = ms.FlagSet.Arg(0) + inputPath = ms.Opts.Arg(0) } - if len(ms.FlagSet.Args()) >= 2 { - outputPath = ms.FlagSet.Arg(1) + if len(ms.Opts.Args()) >= 2 { + outputPath = ms.Opts.Arg(1) } else { if inputPath == "-" { outputPath = "-" @@ -93,16 +93,11 @@ func run(ctx context.Context, ms *xmain.State) (err error) { if match == (d2themes.Theme{}) { return xmain.UsageErrorf("-t[heme] could not be found. The available options are:\n%s\nYou provided: %d", d2themescatalog.CLIString(), *themeFlag) } - ms.Env.Setenv("D2_THEME", fmt.Sprintf("%d", *themeFlag)) + ms.Log.Debug.Printf("using theme %s (ID: %d)", match.Name, *themeFlag) - envD2Layout := ms.Env.Getenv("D2_LAYOUT") - if envD2Layout == "" { - envD2Layout = "dagre" - } - - plugin, path, err := d2plugin.FindPlugin(ctx, envD2Layout) + plugin, path, err := d2plugin.FindPlugin(ctx, *layoutFlag) if errors.Is(err, exec.ErrNotFound) { - return layoutNotFound(ctx, envD2Layout) + return layoutNotFound(ctx, *layoutFlag) } else if err != nil { return err } @@ -111,14 +106,14 @@ func run(ctx context.Context, ms *xmain.State) (err error) { if path != "" { pluginLocation = fmt.Sprintf("executable plugin at %s", humanPath(path)) } - ms.Log.Debug.Printf("using layout plugin %s (%s)", envD2Layout, pluginLocation) + ms.Log.Debug.Printf("using layout plugin %s (%s)", *layoutFlag, pluginLocation) if *watchFlag { if inputPath == "-" { return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin") } ms.Env.Setenv("LOG_TIMESTAMPS", "1") - w, err := newWatcher(ctx, ms, plugin, inputPath, outputPath) + w, err := newWatcher(ctx, ms, plugin, *themeFlag, inputPath, outputPath) if err != nil { return err } @@ -132,7 +127,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { _ = 343 } - _, err = compile(ctx, ms, plugin, inputPath, outputPath) + _, err = compile(ctx, ms, plugin, *themeFlag, inputPath, outputPath) if err != nil { return err } @@ -140,7 +135,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return nil } -func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, inputPath, outputPath string) ([]byte, error) { +func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, themeID int64, inputPath, outputPath string) ([]byte, error) { input, err := ms.ReadPath(inputPath) if err != nil { return nil, err @@ -151,7 +146,6 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, input return nil, err } - themeID, _ := strconv.ParseInt(ms.Env.Getenv("D2_THEME"), 10, 64) d, err := d2.Compile(ctx, string(input), &d2.CompileOptions{ Layout: plugin.Layout, Ruler: ruler, diff --git a/cmd/d2/watch.go b/cmd/d2/watch.go index f9570da66..18dd9ea3c 100644 --- a/cmd/d2/watch.go +++ b/cmd/d2/watch.go @@ -42,6 +42,7 @@ type watcher struct { ms *xmain.State layoutPlugin d2plugin.Plugin + themeID int64 inputPath string outputPath string @@ -68,7 +69,7 @@ type compileResult struct { SVG string `json:"svg"` } -func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, inputPath, outputPath string) (*watcher, error) { +func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, inputPath, outputPath string) (*watcher, error) { ctx, cancel := context.WithCancel(ctx) w := &watcher{ @@ -78,6 +79,7 @@ func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plug ms: ms, layoutPlugin: layoutPlugin, + themeID: themeID, inputPath: inputPath, outputPath: outputPath, @@ -325,7 +327,7 @@ func (w *watcher) compileLoop(ctx context.Context) error { recompiledPrefix = "re" } - b, err := compile(ctx, w.ms, w.layoutPlugin, w.inputPath, w.outputPath) + b, err := compile(ctx, w.ms, w.layoutPlugin, w.themeID, w.inputPath, w.outputPath) if err != nil { err = fmt.Errorf("failed to %scompile: %w", recompiledPrefix, err) w.ms.Log.Error.Print(err) diff --git a/d2plugin/serve.go b/d2plugin/serve.go index 919eab48b..a51c48614 100644 --- a/d2plugin/serve.go +++ b/d2plugin/serve.go @@ -19,12 +19,12 @@ import ( // Also see execPlugin in exec.go for the d2 binary plugin protocol. func Serve(p Plugin) func(context.Context, *xmain.State) error { return func(ctx context.Context, ms *xmain.State) (err error) { - if len(ms.Args) < 1 { + if len(ms.Opts.Args()) < 1 { return errors.New("expected first argument to plugin binary to be function name") } - reqFunc := ms.Args[0] + reqFunc := ms.Opts.Arg(0) - switch ms.Args[0] { + switch ms.Opts.Arg(0) { case "info": return info(ctx, p, ms) case "layout": diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go new file mode 100644 index 000000000..c095d58cb --- /dev/null +++ b/lib/xmain/opts.go @@ -0,0 +1,132 @@ +package xmain + +import ( + "fmt" + "io" + "strconv" + "strings" + + "github.com/spf13/pflag" + "oss.terrastruct.com/cmdlog" + "oss.terrastruct.com/xos" +) + +type Opts struct { + args []string + flags *pflag.FlagSet + env *xos.Env + log *cmdlog.Logger + + registeredEnvs []string +} + +func NewOpts(env *xos.Env, args []string, log *cmdlog.Logger) *Opts { + flags := pflag.NewFlagSet("", pflag.ContinueOnError) + flags.SortFlags = false + flags.Usage = func() {} + flags.SetOutput(io.Discard) + return &Opts{ + args: args, + flags: flags, + env: env, + log: log, + } +} + +func (o *Opts) Help() string { + b := &strings.Builder{} + o.flags.SetOutput(b) + o.flags.PrintDefaults() + + if len(o.registeredEnvs) > 0 { + b.WriteString("\nYou may persistently set the following as environment variables (flags take precedent):\n") + for i, e := range o.registeredEnvs { + s := fmt.Sprintf("- $%s", e) + if i != len(o.registeredEnvs)-1 { + s += "\n" + } + b.WriteString(s) + } + } + + return b.String() +} + +func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) *int64 { + if envKey != "" { + if o.env.Getenv(envKey) != "" { + envVal, err := strconv.ParseInt(o.env.Getenv(envKey), 10, 64) + if err != nil { + o.log.Error.Printf(`ignoring invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) + } else if envVal != defaultVal { + defaultVal = envVal + } + } + o.registeredEnvs = append(o.registeredEnvs, envKey) + } + + return o.flags.Int64P(flag, shortFlag, defaultVal, usage) +} + +func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { + if envKey != "" { + if o.env.Getenv(envKey) != "" { + envVal := o.env.Getenv(envKey) + if envVal != defaultVal { + defaultVal = envVal + } + } + o.registeredEnvs = append(o.registeredEnvs, envKey) + } + + return o.flags.StringP(flag, shortFlag, defaultVal, usage) +} + +func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) *bool { + if envKey != "" { + if o.env.Getenv(envKey) != "" { + envVal := o.env.Getenv(envKey) + if !boolyEnv(envVal) { + o.log.Error.Printf(`ignoring invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) + } else if (defaultVal && falseyEnv(envVal)) || + (!defaultVal && truthyEnv(envVal)) { + defaultVal = !defaultVal + } + } + o.registeredEnvs = append(o.registeredEnvs, envKey) + } + + return o.flags.BoolP(flag, shortFlag, defaultVal, usage) +} + +func boolyEnv(s string) bool { + return falseyEnv(s) || truthyEnv(s) +} + +func falseyEnv(s string) bool { + return s == "0" || s == "false" || s == "f" +} + +func truthyEnv(s string) bool { + return s == "1" || s == "true" || s == "t" +} + +func (o *Opts) Parse() error { + err := o.flags.Parse(o.args) + if err != nil { + return err + } + return nil +} + +func (o *Opts) SetArgs(args []string) { + o.args = args +} + +func (o *Opts) Args() []string { + return o.flags.Args() +} + +func (o *Opts) Arg(i int) string { + return o.flags.Arg(i) +} diff --git a/lib/xmain/xmain.go b/lib/xmain/xmain.go index d71147776..3ef9b89aa 100644 --- a/lib/xmain/xmain.go +++ b/lib/xmain/xmain.go @@ -9,13 +9,11 @@ import ( "io" "os" "os/signal" - "strings" "syscall" "time" "cdr.dev/slog" "cdr.dev/slog/sloggers/sloghuman" - "github.com/spf13/pflag" "oss.terrastruct.com/xos" @@ -41,14 +39,10 @@ func Main(run RunFunc) { Stdout: os.Stdout, Stderr: os.Stderr, - Env: xos.NewEnv(os.Environ()), - FlagSet: pflag.NewFlagSet("", pflag.ContinueOnError), - Args: args, + Env: xos.NewEnv(os.Environ()), } ms.Log = cmdlog.Log(ms.Env, os.Stderr) - ms.FlagSet.SortFlags = false - ms.FlagSet.Usage = func() {} - ms.FlagSet.SetOutput(io.Discard) + ms.Opts = NewOpts(ms.Env, args, ms.Log) sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) @@ -88,10 +82,9 @@ type State struct { Stdout io.WriteCloser Stderr io.WriteCloser - Log *cmdlog.Logger - Env *xos.Env - Args []string - FlagSet *pflag.FlagSet + Log *cmdlog.Logger + Env *xos.Env + Opts *Opts } func (ms *State) Main(ctx context.Context, sigs <-chan os.Signal, run func(context.Context, *State) error) error { @@ -129,13 +122,6 @@ func (ms *State) Main(ctx context.Context, sigs <-chan os.Signal, run func(conte } } -func (ms *State) FlagHelp() string { - b := &strings.Builder{} - ms.FlagSet.SetOutput(b) - ms.FlagSet.PrintDefaults() - return b.String() -} - type ExitError struct { Code int `json:"code"` Message string `json:"message"` From 06fa7094617e024b7a04faca358c56376c7ea879 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:42:39 -0800 Subject: [PATCH 2/9] 2022-11-16 04:42:39PM --- cmd/d2/main.go | 25 ++++++++++++++++++++----- lib/xmain/opts.go | 39 +++++++++++++++++---------------------- lib/xmain/xmain.go | 2 +- 3 files changed, 38 insertions(+), 28 deletions(-) diff --git a/cmd/d2/main.go b/cmd/d2/main.go index d5976671a..e111d72f0 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -31,12 +31,27 @@ func run(ctx context.Context, ms *xmain.State) (err error) { // :( ctx = xmain.DiscardSlog(ctx) - watchFlag := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") - bundleFlag := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") - debugFlag := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.") + watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } + bundleFlag, err := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } + debugFlag, err := ms.Opts.Bool("DEBUG", "debug", "d", false, "print debug logs.") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } layoutFlag := ms.Opts.String("D2_LAYOUT", "layout", "l", "dagre", `the layout engine used.`) - themeFlag := ms.Opts.Int64("D2_THEME", "theme", "t", 0, "the diagram theme ID. For a list of available options, see https://oss.terrastruct.com/d2") - versionFlag := ms.Opts.Bool("", "version", "v", false, "get the version") + themeFlag, err := ms.Opts.Int64("D2_THEME", "theme", "t", 0, "the diagram theme ID. For a list of available options, see https://oss.terrastruct.com/d2") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } + versionFlag, err := ms.Opts.Bool("", "version", "v", false, "get the version") + if err != nil { + return xmain.UsageErrorf(err.Error()) + } err = ms.Opts.Parse() if !errors.Is(err, pflag.ErrHelp) && err != nil { diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index c095d58cb..15a0c71a1 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -20,7 +20,7 @@ type Opts struct { registeredEnvs []string } -func NewOpts(env *xos.Env, args []string, log *cmdlog.Logger) *Opts { +func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { flags := pflag.NewFlagSet("", pflag.ContinueOnError) flags.SortFlags = false flags.Usage = func() {} @@ -52,29 +52,26 @@ func (o *Opts) Help() string { return b.String() } -func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) *int64 { +func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) { if envKey != "" { if o.env.Getenv(envKey) != "" { envVal, err := strconv.ParseInt(o.env.Getenv(envKey), 10, 64) if err != nil { - o.log.Error.Printf(`ignoring invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) - } else if envVal != defaultVal { - defaultVal = envVal + return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) } + defaultVal = envVal } o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.Int64P(flag, shortFlag, defaultVal, usage) + return o.flags.Int64P(flag, shortFlag, defaultVal, usage), nil } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { if envKey != "" { if o.env.Getenv(envKey) != "" { envVal := o.env.Getenv(envKey) - if envVal != defaultVal { - defaultVal = envVal - } + defaultVal = envVal } o.registeredEnvs = append(o.registeredEnvs, envKey) } @@ -82,21 +79,23 @@ func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) return o.flags.StringP(flag, shortFlag, defaultVal, usage) } -func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) *bool { +func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { if envKey != "" { if o.env.Getenv(envKey) != "" { envVal := o.env.Getenv(envKey) if !boolyEnv(envVal) { - o.log.Error.Printf(`ignoring invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) - } else if (defaultVal && falseyEnv(envVal)) || - (!defaultVal && truthyEnv(envVal)) { - defaultVal = !defaultVal + return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) + } + if truthyEnv(envVal) { + defaultVal = true + } else { + defaultVal = false } } o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.BoolP(flag, shortFlag, defaultVal, usage) + return o.flags.BoolP(flag, shortFlag, defaultVal, usage), nil } func boolyEnv(s string) bool { @@ -104,19 +103,15 @@ func boolyEnv(s string) bool { } func falseyEnv(s string) bool { - return s == "0" || s == "false" || s == "f" + return s == "0" || s == "false" } func truthyEnv(s string) bool { - return s == "1" || s == "true" || s == "t" + return s == "1" || s == "true" } func (o *Opts) Parse() error { - err := o.flags.Parse(o.args) - if err != nil { - return err - } - return nil + return o.flags.Parse(o.args) } func (o *Opts) SetArgs(args []string) { diff --git a/lib/xmain/xmain.go b/lib/xmain/xmain.go index 3ef9b89aa..18eba1367 100644 --- a/lib/xmain/xmain.go +++ b/lib/xmain/xmain.go @@ -42,7 +42,7 @@ func Main(run RunFunc) { Env: xos.NewEnv(os.Environ()), } ms.Log = cmdlog.Log(ms.Env, os.Stderr) - ms.Opts = NewOpts(ms.Env, args, ms.Log) + ms.Opts = NewOpts(ms.Env, ms.Log, args) sigs := make(chan os.Signal, 1) signal.Notify(sigs, os.Interrupt, syscall.SIGTERM) From e1846b0e4a88cc24d3d5d0cc4a6eedb5ffcae92d Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:48:19 -0800 Subject: [PATCH 3/9] 2022-11-16 04:48:19PM --- cmd/d2/help.go | 10 +++++----- cmd/d2/main.go | 20 ++++++++++---------- lib/xmain/opts.go | 34 +++++++++------------------------- 3 files changed, 24 insertions(+), 40 deletions(-) diff --git a/cmd/d2/help.go b/cmd/d2/help.go index 3e18a56cf..9b304ee34 100644 --- a/cmd/d2/help.go +++ b/cmd/d2/help.go @@ -32,9 +32,9 @@ See more docs and the source code at https://oss.terrastruct.com/d2 } func layoutHelp(ctx context.Context, ms *xmain.State) error { - if len(ms.Opts.Args()) == 1 { + if len(ms.Opts.Flags.Args()) == 1 { return shortLayoutHelp(ctx, ms) - } else if len(ms.Opts.Args()) == 2 { + } else if len(ms.Opts.Flags.Args()) == 2 { return longLayoutHelp(ctx, ms) } else { return pluginSubcommand(ctx, ms) @@ -75,7 +75,7 @@ See more docs at https://oss.terrastruct.com/d2 } func longLayoutHelp(ctx context.Context, ms *xmain.State) error { - layout := ms.Opts.Arg(1) + layout := ms.Opts.Flags.Arg(1) plugin, path, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) @@ -119,13 +119,13 @@ For more information on setup, please visit https://github.com/terrastruct/d2.`, } func pluginSubcommand(ctx context.Context, ms *xmain.State) error { - layout := ms.Opts.Arg(1) + layout := ms.Opts.Flags.Arg(1) plugin, _, err := d2plugin.FindPlugin(ctx, layout) if errors.Is(err, exec.ErrNotFound) { return layoutNotFound(ctx, layout) } - ms.Opts.SetArgs(ms.Opts.Args()[2:]) + ms.Opts.Args = ms.Opts.Flags.Args()[2:] return d2plugin.Serve(plugin)(ctx, ms) } diff --git a/cmd/d2/main.go b/cmd/d2/main.go index e111d72f0..914ca295e 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -53,13 +53,13 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return xmain.UsageErrorf(err.Error()) } - err = ms.Opts.Parse() + 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 len(ms.Opts.Args()) > 0 { - switch ms.Opts.Arg(0) { + if len(ms.Opts.Flags.Args()) > 0 { + switch ms.Opts.Flags.Arg(0) { case "layout": return layoutHelp(ctx, ms) } @@ -77,26 +77,26 @@ func run(ctx context.Context, ms *xmain.State) (err error) { var inputPath string var outputPath string - if len(ms.Opts.Args()) == 0 { + if len(ms.Opts.Flags.Args()) == 0 { if versionFlag != nil && *versionFlag { fmt.Println(version.Version) return nil } help(ms) return nil - } else if len(ms.Opts.Args()) >= 3 { + } else if len(ms.Opts.Flags.Args()) >= 3 { return xmain.UsageErrorf("too many arguments passed") } - if len(ms.Opts.Args()) >= 1 { - if ms.Opts.Arg(0) == "version" { + if len(ms.Opts.Flags.Args()) >= 1 { + if ms.Opts.Flags.Arg(0) == "version" { fmt.Println(version.Version) return nil } - inputPath = ms.Opts.Arg(0) + inputPath = ms.Opts.Flags.Arg(0) } - if len(ms.Opts.Args()) >= 2 { - outputPath = ms.Opts.Arg(1) + if len(ms.Opts.Flags.Args()) >= 2 { + outputPath = ms.Opts.Flags.Arg(1) } else { if inputPath == "-" { outputPath = "-" diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index 15a0c71a1..498f0db7e 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -12,8 +12,8 @@ import ( ) type Opts struct { - args []string - flags *pflag.FlagSet + Args []string + Flags *pflag.FlagSet env *xos.Env log *cmdlog.Logger @@ -26,8 +26,8 @@ func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { flags.Usage = func() {} flags.SetOutput(io.Discard) return &Opts{ - args: args, - flags: flags, + Args: args, + Flags: flags, env: env, log: log, } @@ -35,8 +35,8 @@ func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { func (o *Opts) Help() string { b := &strings.Builder{} - o.flags.SetOutput(b) - o.flags.PrintDefaults() + o.Flags.SetOutput(b) + o.Flags.PrintDefaults() if len(o.registeredEnvs) > 0 { b.WriteString("\nYou may persistently set the following as environment variables (flags take precedent):\n") @@ -64,7 +64,7 @@ func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage str o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.Int64P(flag, shortFlag, defaultVal, usage), nil + return o.Flags.Int64P(flag, shortFlag, defaultVal, usage), nil } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { @@ -76,7 +76,7 @@ func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.StringP(flag, shortFlag, defaultVal, usage) + return o.Flags.StringP(flag, shortFlag, defaultVal, usage) } func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { @@ -95,7 +95,7 @@ func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage strin o.registeredEnvs = append(o.registeredEnvs, envKey) } - return o.flags.BoolP(flag, shortFlag, defaultVal, usage), nil + return o.Flags.BoolP(flag, shortFlag, defaultVal, usage), nil } func boolyEnv(s string) bool { @@ -109,19 +109,3 @@ func falseyEnv(s string) bool { func truthyEnv(s string) bool { return s == "1" || s == "true" } - -func (o *Opts) Parse() error { - return o.flags.Parse(o.args) -} - -func (o *Opts) SetArgs(args []string) { - o.args = args -} - -func (o *Opts) Args() []string { - return o.flags.Args() -} - -func (o *Opts) Arg(i int) string { - return o.flags.Arg(i) -} From 359a12b5f891239fef50540a6805e82446ed3509 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:48:46 -0800 Subject: [PATCH 4/9] 2022-11-16 04:48:46PM --- d2plugin/serve.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/d2plugin/serve.go b/d2plugin/serve.go index a51c48614..2db42d215 100644 --- a/d2plugin/serve.go +++ b/d2plugin/serve.go @@ -19,12 +19,12 @@ import ( // Also see execPlugin in exec.go for the d2 binary plugin protocol. func Serve(p Plugin) func(context.Context, *xmain.State) error { return func(ctx context.Context, ms *xmain.State) (err error) { - if len(ms.Opts.Args()) < 1 { + if len(ms.Opts.Flags.Args()) < 1 { return errors.New("expected first argument to plugin binary to be function name") } - reqFunc := ms.Opts.Arg(0) + reqFunc := ms.Opts.Flags.Arg(0) - switch ms.Opts.Arg(0) { + switch ms.Opts.Flags.Arg(0) { case "info": return info(ctx, p, ms) case "layout": From d90625dc0a91844c0f09861ec88a2fa30bae63e3 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 16:55:12 -0800 Subject: [PATCH 5/9] 2022-11-16 04:55:12PM --- ci/release/changelogs/next.md | 3 ++- lib/xmain/opts.go | 49 ++++++++++++++++------------------- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index ffd49ca9c..abd4b28f7 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -6,7 +6,8 @@ For v0.0.99 we focused on X, Y and Z. Enjoy! #### Improvements 🔧 -- Add table columns indices in edges between SQL Tables so that layout engines can route exactly between them +- Equivalency between flags and environment variables. You can set either one for all + options (flags take precedence). #### Bugfixes 🔴 diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index 498f0db7e..ed260537f 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -52,47 +52,44 @@ func (o *Opts) Help() string { return b.String() } +func (o *Opts) getEnv(k string) string { + if k != "" { + o.registeredEnvs = append(o.registeredEnvs, k) + return o.env.Getenv(k) + } + return "" +} + func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) { - if envKey != "" { - if o.env.Getenv(envKey) != "" { - envVal, err := strconv.ParseInt(o.env.Getenv(envKey), 10, 64) - if err != nil { - return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) - } - defaultVal = envVal + if env := o.getEnv(envKey); env != "" { + envVal, err := strconv.ParseInt(env, 10, 64) + if err != nil { + return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) } - o.registeredEnvs = append(o.registeredEnvs, envKey) + defaultVal = envVal } return o.Flags.Int64P(flag, shortFlag, defaultVal, usage), nil } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { - if envKey != "" { - if o.env.Getenv(envKey) != "" { - envVal := o.env.Getenv(envKey) - defaultVal = envVal - } - o.registeredEnvs = append(o.registeredEnvs, envKey) + if env := o.getEnv(envKey); env != "" { + defaultVal = env } return o.Flags.StringP(flag, shortFlag, defaultVal, usage) } func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { - if envKey != "" { - if o.env.Getenv(envKey) != "" { - envVal := o.env.Getenv(envKey) - if !boolyEnv(envVal) { - return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, envVal) - } - if truthyEnv(envVal) { - defaultVal = true - } else { - defaultVal = false - } + if env := o.getEnv(envKey); env != "" { + if !boolyEnv(env) { + return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env) + } + if truthyEnv(env) { + defaultVal = true + } else { + defaultVal = false } - o.registeredEnvs = append(o.registeredEnvs, envKey) } return o.Flags.BoolP(flag, shortFlag, defaultVal, usage), nil From 474d5f1655732f2506a19cab42ada2cc1e738819 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 17:13:38 -0800 Subject: [PATCH 6/9] 2022-11-16 05:13:38PM --- ci/release/template/man/d2.1 | 4 +++- cmd/d2/main.go | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index 6d187ea83..b9c680414 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -39,7 +39,9 @@ Watch for changes to input and live reload. Use .Ev $D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0 .It Fl t , -theme Ar 0 Set the diagram theme to the passed integer. For a list of available options, see -.Lk https://oss.terrastruct.com/d2 +.It Fl l , -layout Ar dagre +Set the diagram layout engine to the passed string. For a list of available options, run +.Ar layout .It Fl b , -bundle Ar true Bundle all assets and layers into the output svg. .It Fl d , -debug diff --git a/cmd/d2/main.go b/cmd/d2/main.go index 914ca295e..6392541f4 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -31,6 +31,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { // :( ctx = xmain.DiscardSlog(ctx) + // These should be kept up-to-date with the d2 man page watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") if err != nil { return xmain.UsageErrorf(err.Error()) From 052b461b031c28128cd598e73554961b2ac868e4 Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 22:33:57 -0800 Subject: [PATCH 7/9] 2022-11-16 10:33:57PM --- cmd/d2/help.go | 2 +- lib/xmain/flag_helpers.go | 45 ++++++++++++++++ lib/xmain/opts.go | 111 ++++++++++++++++++++++++++++++-------- 3 files changed, 134 insertions(+), 24 deletions(-) create mode 100644 lib/xmain/flag_helpers.go diff --git a/cmd/d2/help.go b/cmd/d2/help.go index 9b304ee34..ccc85332f 100644 --- a/cmd/d2/help.go +++ b/cmd/d2/help.go @@ -28,7 +28,7 @@ Subcommands: %[1]s layout [layout name] - Display long help for a particular layout engine See more docs and the source code at https://oss.terrastruct.com/d2 -`, ms.Name, ms.Opts.Help()) +`, ms.Name, ms.Opts.Defaults()) } func layoutHelp(ctx context.Context, ms *xmain.State) error { diff --git a/lib/xmain/flag_helpers.go b/lib/xmain/flag_helpers.go new file mode 100644 index 000000000..4d17066dc --- /dev/null +++ b/lib/xmain/flag_helpers.go @@ -0,0 +1,45 @@ +// flag_helpers.go are private functions from pflag/flag.go +package xmain + +import "strings" + +func wrap(i, w int, s string) string { + if w == 0 { + return strings.Replace(s, "\n", "\n"+strings.Repeat(" ", i), -1) + } + wrap := w - i + var r, l string + if wrap < 24 { + i = 16 + wrap = w - i + r += "\n" + strings.Repeat(" ", i) + } + if wrap < 24 { + return strings.Replace(s, "\n", r, -1) + } + slop := 5 + wrap = wrap - slop + l, s = wrapN(wrap, slop, s) + r = r + strings.Replace(l, "\n", "\n"+strings.Repeat(" ", i), -1) + for s != "" { + var t string + t, s = wrapN(wrap, slop, s) + r = r + "\n" + strings.Repeat(" ", i) + strings.Replace(t, "\n", "\n"+strings.Repeat(" ", i), -1) + } + return r +} + +func wrapN(i, slop int, s string) (string, string) { + if i+slop > len(s) { + return s, "" + } + w := strings.LastIndexAny(s[:i], " \t\n") + if w <= 0 { + return s, "" + } + nlPos := strings.LastIndex(s[:i], "\n") + if nlPos > 0 && nlPos < w { + return s[:nlPos], s[nlPos+1:] + } + return s[:w], s[w+1:] +} diff --git a/lib/xmain/opts.go b/lib/xmain/opts.go index ed260537f..98d9387ef 100644 --- a/lib/xmain/opts.go +++ b/lib/xmain/opts.go @@ -1,6 +1,7 @@ package xmain import ( + "bytes" "fmt" "io" "strconv" @@ -17,7 +18,7 @@ type Opts struct { env *xos.Env log *cmdlog.Logger - registeredEnvs []string + flagEnv map[string]string } func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { @@ -26,42 +27,106 @@ func NewOpts(env *xos.Env, log *cmdlog.Logger, args []string) *Opts { flags.Usage = func() {} flags.SetOutput(io.Discard) return &Opts{ - Args: args, - Flags: flags, - env: env, - log: log, + Args: args, + Flags: flags, + env: env, + log: log, + flagEnv: make(map[string]string), } } -func (o *Opts) Help() string { - b := &strings.Builder{} - o.Flags.SetOutput(b) - o.Flags.PrintDefaults() +// Mostly copy pasted pasted from pflag.FlagUsagesWrapped +// with modifications for env var +func (o *Opts) Defaults() string { + buf := new(bytes.Buffer) - if len(o.registeredEnvs) > 0 { - b.WriteString("\nYou may persistently set the following as environment variables (flags take precedent):\n") - for i, e := range o.registeredEnvs { - s := fmt.Sprintf("- $%s", e) - if i != len(o.registeredEnvs)-1 { - s += "\n" - } - b.WriteString(s) + var lines []string + + maxlen := 0 + maxEnvLen := 0 + o.Flags.VisitAll(func(flag *pflag.Flag) { + if flag.Hidden { + return } + + line := "" + if flag.Shorthand != "" && flag.ShorthandDeprecated == "" { + line = fmt.Sprintf(" -%s, --%s", flag.Shorthand, flag.Name) + } else { + line = fmt.Sprintf(" --%s", flag.Name) + } + + varname, usage := pflag.UnquoteUsage(flag) + if varname != "" { + line += " " + varname + } + if flag.NoOptDefVal != "" { + switch flag.Value.Type() { + case "string": + line += fmt.Sprintf("[=\"%s\"]", flag.NoOptDefVal) + case "bool": + if flag.NoOptDefVal != "true" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + case "count": + if flag.NoOptDefVal != "+1" { + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + default: + line += fmt.Sprintf("[=%s]", flag.NoOptDefVal) + } + } + + line += "\x00" + + if len(line) > maxlen { + maxlen = len(line) + } + + if e, ok := o.flagEnv[flag.Name]; ok { + line += fmt.Sprintf("$%s", e) + } + + line += "\x01" + + if len(line) > maxEnvLen { + maxEnvLen = len(line) + } + + line += usage + if flag.Value.Type() == "string" { + line += fmt.Sprintf(" (default %q)", flag.DefValue) + } else { + line += fmt.Sprintf(" (default %s)", flag.DefValue) + } + if len(flag.Deprecated) != 0 { + line += fmt.Sprintf(" (DEPRECATED: %s)", flag.Deprecated) + } + + lines = append(lines, line) + }) + + for _, line := range lines { + sidx1 := strings.Index(line, "\x00") + sidx2 := strings.Index(line, "\x01") + spacing1 := strings.Repeat(" ", maxlen-sidx1) + spacing2 := strings.Repeat(" ", (maxEnvLen-maxlen)-sidx2+sidx1) + fmt.Fprintln(buf, line[:sidx1], spacing1, line[sidx1+1:sidx2], spacing2, wrap(maxEnvLen+3, 0, line[sidx2+1:])) } - return b.String() + return buf.String() } -func (o *Opts) getEnv(k string) string { +func (o *Opts) getEnv(flag, k string) string { if k != "" { - o.registeredEnvs = append(o.registeredEnvs, k) + o.flagEnv[flag] = k return o.env.Getenv(k) } return "" } func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage string) (*int64, error) { - if env := o.getEnv(envKey); env != "" { + if env := o.getEnv(flag, envKey); env != "" { envVal, err := strconv.ParseInt(env, 10, 64) if err != nil { return nil, fmt.Errorf(`invalid environment variable %s. Expected int64. Found "%v".`, envKey, envVal) @@ -73,7 +138,7 @@ func (o *Opts) Int64(envKey, flag, shortFlag string, defaultVal int64, usage str } func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) *string { - if env := o.getEnv(envKey); env != "" { + if env := o.getEnv(flag, envKey); env != "" { defaultVal = env } @@ -81,7 +146,7 @@ func (o *Opts) String(envKey, flag, shortFlag string, defaultVal, usage string) } func (o *Opts) Bool(envKey, flag, shortFlag string, defaultVal bool, usage string) (*bool, error) { - if env := o.getEnv(envKey); env != "" { + if env := o.getEnv(flag, envKey); env != "" { if !boolyEnv(env) { return nil, fmt.Errorf(`invalid environment variable %s. Expected bool. Found "%s".`, envKey, env) } From e08a0fb8dce03061e04b568355acab741cc2544d Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 22:45:08 -0800 Subject: [PATCH 8/9] 2022-11-16 10:45:08PM --- ci/release/template/man/d2.1 | 12 +++++++++++- cmd/d2/main.go | 6 ++++-- cmd/d2/watch.go | 19 ++++++------------- 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/ci/release/template/man/d2.1 b/ci/release/template/man/d2.1 index b9c680414..0583e90c3 100644 --- a/ci/release/template/man/d2.1 +++ b/ci/release/template/man/d2.1 @@ -36,12 +36,22 @@ See more docs, the source code and license at .It Fl w , -watch Ar false Watch for changes to input and live reload. Use .Ev $PORT and Ev $HOST to specify the listening address. -.Ev $D2_PORT and $D2_HOST are also accepted and take priority. Default is localhost:0 +.It Fl h , -host Ar localhost +Host listening address when used with +.Ar watch +.Ns . +.It Fl p , -port Ar 0 +Port listening address when used with +.Ar watch +.Ns . .It Fl t , -theme Ar 0 Set the diagram theme to the passed integer. For a list of available options, see +.Lk https://oss.terrastruct.com/d2 +.Ns . .It Fl l , -layout Ar dagre Set the diagram layout engine to the passed string. For a list of available options, run .Ar layout +.Ns . .It Fl b , -bundle Ar true Bundle all assets and layers into the output svg. .It Fl d , -debug diff --git a/cmd/d2/main.go b/cmd/d2/main.go index 6392541f4..cfca96ddf 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -32,10 +32,12 @@ func run(ctx context.Context, ms *xmain.State) (err error) { ctx = xmain.DiscardSlog(ctx) // These should be kept up-to-date with the d2 man page - watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n$D2_HOST and $D2_PORT are also accepted and take priority (default localhost:0, which is will open on a randomly available local port).") + watchFlag, err := ms.Opts.Bool("D2_WATCH", "watch", "w", false, "watch for changes to input and live reload. Use $HOST and $PORT to specify the listening address.\n(default localhost:0, which is will open on a randomly available local port).") if err != nil { return xmain.UsageErrorf(err.Error()) } + hostFlag := ms.Opts.String("HOST", "host", "h", "localhost", "host listening address when used with watch") + portFlag := ms.Opts.String("PORT", "port", "p", "0", "port listening address when used with watch") bundleFlag, err := ms.Opts.Bool("D2_BUNDLE", "bundle", "b", true, "bundle all assets and layers into the output svg.") if err != nil { return xmain.UsageErrorf(err.Error()) @@ -130,7 +132,7 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin") } ms.Env.Setenv("LOG_TIMESTAMPS", "1") - w, err := newWatcher(ctx, ms, plugin, *themeFlag, inputPath, outputPath) + w, err := newWatcher(ctx, ms, plugin, *themeFlag, *hostFlag, *portFlag, inputPath, outputPath) if err != nil { return err } diff --git a/cmd/d2/watch.go b/cmd/d2/watch.go index 18dd9ea3c..a5d22c42c 100644 --- a/cmd/d2/watch.go +++ b/cmd/d2/watch.go @@ -43,6 +43,8 @@ type watcher struct { ms *xmain.State layoutPlugin d2plugin.Plugin themeID int64 + host string + port string inputPath string outputPath string @@ -69,7 +71,7 @@ type compileResult struct { SVG string `json:"svg"` } -func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, inputPath, outputPath string) (*watcher, error) { +func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, host, port, inputPath, outputPath string) (*watcher, error) { ctx, cancel := context.WithCancel(ctx) w := &watcher{ @@ -80,6 +82,8 @@ func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plug ms: ms, layoutPlugin: layoutPlugin, themeID: themeID, + host: host, + port: port, inputPath: inputPath, outputPath: outputPath, @@ -353,18 +357,7 @@ func (w *watcher) compileLoop(ctx context.Context) error { } func (w *watcher) listen() error { - host := "localhost" - port := "0" - hostEnv := w.ms.Env.Getenv("HOST") - if hostEnv != "" { - host = hostEnv - } - portEnv := w.ms.Env.Getenv("PORT") - if portEnv != "" { - port = portEnv - } - - l, err := net.Listen("tcp", net.JoinHostPort(host, port)) + l, err := net.Listen("tcp", net.JoinHostPort(w.host, w.port)) if err != nil { return err } From c8739d8147bbb31a0e705f553483d1b70e8ac77f Mon Sep 17 00:00:00 2001 From: Alexander Wang Date: Wed, 16 Nov 2022 23:49:45 -0800 Subject: [PATCH 9/9] 2022-11-16 11:49:45PM --- cmd/d2/main.go | 10 +++++++++- cmd/d2/watch.go | 29 ++++++++++++++--------------- 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/cmd/d2/main.go b/cmd/d2/main.go index cfca96ddf..61107d6ea 100644 --- a/cmd/d2/main.go +++ b/cmd/d2/main.go @@ -132,7 +132,15 @@ func run(ctx context.Context, ms *xmain.State) (err error) { return xmain.UsageErrorf("-w[atch] cannot be combined with reading input from stdin") } ms.Env.Setenv("LOG_TIMESTAMPS", "1") - w, err := newWatcher(ctx, ms, plugin, *themeFlag, *hostFlag, *portFlag, inputPath, outputPath) + + w, err := newWatcher(ctx, ms, watcherOpts{ + layoutPlugin: plugin, + themeID: *themeFlag, + host: *hostFlag, + port: *portFlag, + inputPath: inputPath, + outputPath: outputPath, + }) if err != nil { return err } diff --git a/cmd/d2/watch.go b/cmd/d2/watch.go index a5d22c42c..f4fef9a6d 100644 --- a/cmd/d2/watch.go +++ b/cmd/d2/watch.go @@ -34,19 +34,23 @@ var devMode = false //go:embed static var staticFS embed.FS -type watcher struct { - ctx context.Context - cancel context.CancelFunc - wg sync.WaitGroup - devMode bool - - ms *xmain.State +type watcherOpts struct { layoutPlugin d2plugin.Plugin themeID int64 host string port string inputPath string outputPath string +} + +type watcher struct { + ctx context.Context + cancel context.CancelFunc + wg sync.WaitGroup + devMode bool + + ms *xmain.State + watcherOpts compileCh chan struct{} @@ -71,7 +75,7 @@ type compileResult struct { SVG string `json:"svg"` } -func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plugin, themeID int64, host, port, inputPath, outputPath string) (*watcher, error) { +func newWatcher(ctx context.Context, ms *xmain.State, opts watcherOpts) (*watcher, error) { ctx, cancel := context.WithCancel(ctx) w := &watcher{ @@ -79,13 +83,8 @@ func newWatcher(ctx context.Context, ms *xmain.State, layoutPlugin d2plugin.Plug cancel: cancel, devMode: devMode, - ms: ms, - layoutPlugin: layoutPlugin, - themeID: themeID, - host: host, - port: port, - inputPath: inputPath, - outputPath: outputPath, + ms: ms, + watcherOpts: opts, compileCh: make(chan struct{}, 1), wsclients: make(map[*wsclient]struct{}),