diff --git a/README.md b/README.md index e4fd6e387..b6abd44f7 100644 --- a/README.md +++ b/README.md @@ -111,56 +111,8 @@ For detailed installation docs, with alternative methods and examples for each O In addition to being a runnable CLI tool, D2 can also be used to produce diagrams from Go programs. -```go -import ( - "context" - "io/ioutil" - "path/filepath" - "strings" +For examples, see [./docs/examples/lib](./docs/examples/lib). - "oss.terrastruct.com/d2/d2compiler" - "oss.terrastruct.com/d2/d2exporter" - "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" - "oss.terrastruct.com/d2/d2renderers/d2svg" - "oss.terrastruct.com/d2/d2renderers/textmeasure" - "oss.terrastruct.com/d2/d2themes/d2themescatalog" -) - -func main() { - graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), &d2compiler.CompileOptions{UTF16: true}) - ruler, _ := textmeasure.NewRuler() - graph.SetDimensions(nil, ruler) - d2dagrelayout.Layout(context.Background(), graph) - diagram, _ := d2exporter.Export(context.Background(), graph, d2themescatalog.NeutralDefault.ID) - out, _ := d2svg.Render(diagram) - ioutil.WriteFile(filepath.Join("out.svg"), out, 0600) -} -``` - -D2 is built to be hackable -- the language has an API built on top of it to make edits -programmatically. Modifying the above diagram: - -```go -import ( - "oss.terrastruct.com/d2/d2renderers/textmeasure" - "oss.terrastruct.com/d2/d2themes/d2themescatalog" -) - -// Create a shape with the ID, "meow" -graph, _, _ = d2oracle.Create(graph, "meow") -// Style the shape green -color := "green" -graph, _ = d2oracle.Set(graph, "meow.style.fill", nil, &color) -// Create a shape with the ID, "cat" -graph, _, _ = d2oracle.Create(graph, "cat") -// Move the shape "meow" inside the container "cat" -graph, _ = d2oracle.Move(graph, "meow", "cat.meow") -// Prints formatted D2 script -println(d2format.Format(graph.AST)) -``` - -This makes it easy to build functionality on top of D2. Terrastruct uses the above API to -implement editing of D2 from mouse actions in a visual interface. ## Themes diff --git a/ci/release/changelogs/next.md b/ci/release/changelogs/next.md index b253f2e8b..25d993081 100644 --- a/ci/release/changelogs/next.md +++ b/ci/release/changelogs/next.md @@ -26,7 +26,7 @@ - `d2` now lives in the root folder of the repository instead of as a subcommand. So you can run `go install oss.terrastruct.com/d2@latest` to install from source now. - [#xxx](https://github.com/terrastruct/d2/pull/xxx) + [#290](https://github.com/terrastruct/d2/pull/290) #### Bugfixes 🔴 diff --git a/ci/sub b/ci/sub index 28fb67e3b..0dacc9c6c 160000 --- a/ci/sub +++ b/ci/sub @@ -1 +1 @@ -Subproject commit 28fb67e3bf11d7df2be9ad57b67b78a1733a7f2d +Subproject commit 0dacc9c6cee5b8d901f22f57e9dc7f9acf640cbc diff --git a/d2lib/d2.go b/d2lib/d2.go index 91267326e..2b4d800fb 100644 --- a/d2lib/d2.go +++ b/d2lib/d2.go @@ -22,7 +22,7 @@ type CompileOptions struct { ThemeID int64 } -func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target.Diagram, error) { +func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target.Diagram, *d2graph.Graph, error) { if opts == nil { opts = &CompileOptions{} } @@ -31,12 +31,12 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target UTF16: opts.UTF16, }) if err != nil { - return nil, err + return nil, nil, err } err = g.SetDimensions(opts.MeasuredTexts, opts.Ruler) if err != nil { - return nil, err + return nil, nil, err } if opts.Layout != nil { @@ -47,11 +47,11 @@ func Compile(ctx context.Context, input string, opts *CompileOptions) (*d2target err = errors.New("no available layout") } if err != nil { - return nil, err + return nil, nil, err } diagram, err := d2exporter.Export(ctx, g, opts.ThemeID) - return diagram, err + return diagram, g, err } // See c.go diff --git a/docs/examples/lib/1-d2lib/.gitignore b/docs/examples/lib/1-d2lib/.gitignore new file mode 100644 index 000000000..958df5ba9 --- /dev/null +++ b/docs/examples/lib/1-d2lib/.gitignore @@ -0,0 +1 @@ +out.svg diff --git a/docs/examples/lib/1-d2lib/d2lib.go b/docs/examples/lib/1-d2lib/d2lib.go new file mode 100644 index 000000000..421e14677 --- /dev/null +++ b/docs/examples/lib/1-d2lib/d2lib.go @@ -0,0 +1,25 @@ +package main + +import ( + "context" + "io/ioutil" + "path/filepath" + + "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" + "oss.terrastruct.com/d2/d2lib" + "oss.terrastruct.com/d2/d2renderers/d2svg" + "oss.terrastruct.com/d2/d2renderers/textmeasure" + "oss.terrastruct.com/d2/d2themes/d2themescatalog" +) + +// Remember to add if err != nil checks in production. +func main() { + ruler, _ := textmeasure.NewRuler() + diagram, _, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{ + Layout: d2dagrelayout.Layout, + Ruler: ruler, + ThemeID: d2themescatalog.GrapeSoda.ID, + }) + out, _ := d2svg.Render(diagram) + _ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600) +} diff --git a/docs/examples/lib/1-d2lib/d2lib_test.go b/docs/examples/lib/1-d2lib/d2lib_test.go new file mode 100644 index 000000000..53b3d5bcc --- /dev/null +++ b/docs/examples/lib/1-d2lib/d2lib_test.go @@ -0,0 +1,9 @@ +package main + +import ( + "testing" +) + +func TestMain_(t *testing.T) { + main() +} diff --git a/docs/examples/lib/2-d2oracle/d2oracle.go b/docs/examples/lib/2-d2oracle/d2oracle.go new file mode 100644 index 000000000..e9e7ba944 --- /dev/null +++ b/docs/examples/lib/2-d2oracle/d2oracle.go @@ -0,0 +1,36 @@ +package main + +import ( + "context" + "fmt" + + "oss.terrastruct.com/d2/d2format" + "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" + "oss.terrastruct.com/d2/d2lib" + "oss.terrastruct.com/d2/d2oracle" + "oss.terrastruct.com/d2/d2renderers/textmeasure" + "oss.terrastruct.com/d2/d2themes/d2themescatalog" +) + +// Remember to add if err != nil checks in production. +func main() { + // From one.go + ruler, _ := textmeasure.NewRuler() + _, graph, _ := d2lib.Compile(context.Background(), "x -> y", &d2lib.CompileOptions{ + Layout: d2dagrelayout.Layout, + Ruler: ruler, + ThemeID: d2themescatalog.GrapeSoda.ID, + }) + + // Create a shape with the ID, "meow" + graph, _, _ = d2oracle.Create(graph, "meow") + // Style the shape green + color := "green" + graph, _ = d2oracle.Set(graph, "meow.style.fill", nil, &color) + // Create a shape with the ID, "cat" + graph, _, _ = d2oracle.Create(graph, "cat") + // Move the shape "meow" inside the container "cat" + graph, _ = d2oracle.Move(graph, "meow", "cat.meow") + // Prints formatted D2 script + fmt.Print(d2format.Format(graph.AST)) +} diff --git a/docs/examples/lib/2-d2oracle/d2oracle_test.go b/docs/examples/lib/2-d2oracle/d2oracle_test.go new file mode 100644 index 000000000..53b3d5bcc --- /dev/null +++ b/docs/examples/lib/2-d2oracle/d2oracle_test.go @@ -0,0 +1,9 @@ +package main + +import ( + "testing" +) + +func TestMain_(t *testing.T) { + main() +} diff --git a/docs/examples/lib/3-lowlevel/.gitignore b/docs/examples/lib/3-lowlevel/.gitignore new file mode 100644 index 000000000..958df5ba9 --- /dev/null +++ b/docs/examples/lib/3-lowlevel/.gitignore @@ -0,0 +1 @@ +out.svg diff --git a/docs/examples/lib/3-lowlevel/lowlevel.go b/docs/examples/lib/3-lowlevel/lowlevel.go new file mode 100644 index 000000000..2bba9bfcf --- /dev/null +++ b/docs/examples/lib/3-lowlevel/lowlevel.go @@ -0,0 +1,26 @@ +package main + +import ( + "context" + "io/ioutil" + "path/filepath" + "strings" + + "oss.terrastruct.com/d2/d2compiler" + "oss.terrastruct.com/d2/d2exporter" + "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" + "oss.terrastruct.com/d2/d2renderers/d2svg" + "oss.terrastruct.com/d2/d2renderers/textmeasure" + "oss.terrastruct.com/d2/d2themes/d2themescatalog" +) + +// Remember to add if err != nil checks in production. +func main() { + graph, _ := d2compiler.Compile("", strings.NewReader("x -> y"), nil) + ruler, _ := textmeasure.NewRuler() + _ = graph.SetDimensions(nil, ruler) + _ = d2dagrelayout.Layout(context.Background(), graph) + diagram, _ := d2exporter.Export(context.Background(), graph, d2themescatalog.NeutralDefault.ID) + out, _ := d2svg.Render(diagram) + _ = ioutil.WriteFile(filepath.Join("out.svg"), out, 0600) +} diff --git a/docs/examples/lib/3-lowlevel/lowlevel_test.go b/docs/examples/lib/3-lowlevel/lowlevel_test.go new file mode 100644 index 000000000..53b3d5bcc --- /dev/null +++ b/docs/examples/lib/3-lowlevel/lowlevel_test.go @@ -0,0 +1,9 @@ +package main + +import ( + "testing" +) + +func TestMain_(t *testing.T) { + main() +} diff --git a/docs/examples/lib/README.md b/docs/examples/lib/README.md new file mode 100644 index 000000000..979e9af7e --- /dev/null +++ b/docs/examples/lib/README.md @@ -0,0 +1,30 @@ +# D2 library examples + +We have a few examples in this directory on how to use the D2 library to turn D2 scripts +into rendered svg diagrams and more. + +Each example is runnable though does not include error handling for readability. + +### [./1-d2lib](./1-d2lib) + +A minimal example showing you how to compile the diagram `x -> y` into an svg. + +### [./2-d2oracle](./2-d2oracle) + +D2 is built to be hackable -- the language has an API built on top of it to make edits +programmatically. + +Modifying the previous example, this example demonstrates how +[d2oracle](../../../d2oracle) can be used to create a new shape, style it programatically +and then output the modified d2 script. + +This makes it easy to build functionality on top of D2. Terrastruct uses the +[d2oracle](../../../d2oracle) API to implement editing of D2 from mouse actions in a +visual interface. + +### [./3-lowlevel](./3-lowlevel) + +`d2lib` from the first example is just a wrapper around the lower level APIs. They +can be used directly and this example demonstrates such usage. + +This shouldn't be necessary for most usecases. diff --git a/e2etests/e2e_test.go b/e2etests/e2e_test.go index ea7cfaa65..1e231f39d 100644 --- a/e2etests/e2e_test.go +++ b/e2etests/e2e_test.go @@ -19,7 +19,7 @@ import ( "oss.terrastruct.com/d2/d2graph" "oss.terrastruct.com/d2/d2layouts/d2dagrelayout" "oss.terrastruct.com/d2/d2layouts/d2elklayout" - d2 "oss.terrastruct.com/d2/d2lib" + "oss.terrastruct.com/d2/d2lib" "oss.terrastruct.com/d2/d2renderers/d2svg" "oss.terrastruct.com/d2/d2renderers/textmeasure" "oss.terrastruct.com/d2/d2target" @@ -104,7 +104,7 @@ func run(t *testing.T, tc testCase) { } else if layoutName == "elk" { layout = d2elklayout.Layout } - diagram, err := d2.Compile(ctx, tc.script, &d2.CompileOptions{ + diagram, _, err := d2lib.Compile(ctx, tc.script, &d2lib.CompileOptions{ UTF16: true, Ruler: ruler, ThemeID: 0, diff --git a/main.go b/main.go index c6fd94647..df06bcaf2 100644 --- a/main.go +++ b/main.go @@ -15,7 +15,7 @@ import ( "go.uber.org/multierr" "oss.terrastruct.com/d2/d2layouts/d2sequence" - d2 "oss.terrastruct.com/d2/d2lib" + "oss.terrastruct.com/d2/d2lib" "oss.terrastruct.com/d2/d2plugin" "oss.terrastruct.com/d2/d2renderers/d2svg" "oss.terrastruct.com/d2/d2renderers/textmeasure" @@ -196,7 +196,7 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, theme if os.Getenv("D2_SEQUENCE") == "1" { layout = d2sequence.Layout } - d, err := d2.Compile(ctx, string(input), &d2.CompileOptions{ + diagram, _, err := d2lib.Compile(ctx, string(input), &d2lib.CompileOptions{ Layout: layout, Ruler: ruler, ThemeID: themeID, @@ -205,7 +205,7 @@ func compile(ctx context.Context, ms *xmain.State, plugin d2plugin.Plugin, theme return nil, false, err } - svg, err := d2svg.Render(d) + svg, err := d2svg.Render(diagram) if err != nil { return nil, false, err }