Skip to content

Commit

Permalink
Merge master
Browse files Browse the repository at this point in the history
  • Loading branch information
MetalBlueberry committed May 1, 2024
2 parents aeee85c + bf9776c commit 74e8195
Show file tree
Hide file tree
Showing 12 changed files with 312 additions and 283 deletions.
27 changes: 27 additions & 0 deletions .github/workflows/test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# This workflow will build a golang project
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-go

name: Go

on:
push:
pull_request:

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Set up Go
uses: actions/setup-go@v4
with:
go-version: "1.22"

- name: Test
run: go test -v ./...

- name: Check Generated Schema
run: |
go generate ./...
git diff --exit-code
30 changes: 26 additions & 4 deletions generator/cmd/generator/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,26 @@ import (
"io"
"log"
"os"
"path/filepath"

"github.com/MetalBlueberry/go-plotly/generator"
)

type Creator struct{}

func (c Creator) Create(name string) (io.WriteCloser, error) {
return os.Create(name)
abs, err := filepath.Abs(name)
if err != nil {
return nil, err
}

return os.Create(abs)
}

//go:generate go run main.go --clean --schema ../../schema.json --output-directory ../../../graph_objects

func main() {
clean := flag.Bool("clean", false, "clean the output directory first. Mandatory on CI")
schema := flag.String("schema", "schema.json", "plotly schema")
outputDirectory := flag.String("output-directory", "gen/", "output directory, must exist before generation")

Expand All @@ -34,13 +43,26 @@ func main() {
r, err := generator.NewRenderer(Creator{}, root)
if err != nil {
log.Fatalf("unable to create a new renderer, %s", err)
panic(err)
}

output := *outputDirectory

if err = os.MkdirAll(output, 0755); err != nil {
log.Fatalf("Error creating directory, %s", err)
if *clean {
err = os.RemoveAll(output)
if err != nil {
log.Fatalf("Failed to clean output directory, %s", err)
}
}

err = os.MkdirAll(output, os.0755)

Check failure on line 57 in generator/cmd/generator/main.go

View workflow job for this annotation

GitHub Actions / build

syntax error: unexpected literal .0755 in argument list; possibly missing comma or )
if err != nil {
log.Fatalf("Failed to create output dir %s, %s", *outputDirectory, err)

}

err = r.CreatePlotly(output)
if err != nil {
log.Fatalf("unable to write plotly, %s", err)
}

err = r.CreateTraces(output)
Expand Down
2 changes: 1 addition & 1 deletion generator/generator_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package generator_test
import (
"testing"

. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

Expand Down
2 changes: 1 addition & 1 deletion generator/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import (
"os"

"github.com/MetalBlueberry/go-plotly/generator"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

Expand Down
33 changes: 32 additions & 1 deletion generator/renderer.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,36 @@ func NewRenderer(fs Creator, root *Root) (*Renderer, error) {

var doNotEdit = "// Code generated by go-plotly/generator. DO NOT EDIT."

func (r *Renderer) CreatePlotly(dir string) error {
src := &bytes.Buffer{}
err := r.WritePlotly(src)
if err != nil {
return err
}

fmtsrc, err := format.Source(src.Bytes())
if err != nil {
return fmt.Errorf("cannot format source, %w", err)
}

file, err := r.fs.Create(path.Join(dir, "plotly_gen.go"))
if err != nil {
return fmt.Errorf("Path %s, error %s", dir, err)
}
defer file.Close()
_, err = file.Write(fmtsrc)
if err != nil {
return fmt.Errorf("cannot write source, %w", err)
}

return nil
}

// WritePlotly writes the base plotly file
func (r *Renderer) WritePlotly(w io.Writer) error {
return r.tmpl.ExecuteTemplate(w, "plotly.tmpl", w)
}

// CreateTrace creates a file with the content of a trace by name
func (r *Renderer) CreateTrace(dir string, name string) error {
src := &bytes.Buffer{}
Expand Down Expand Up @@ -204,7 +234,8 @@ func (r *Renderer) WriteLayout(w io.Writer) error {
}
traceFile.MainType.Fields = append(traceFile.MainType.Fields, fields...)

for name, trace := range r.root.Schema.Traces {
for _, name := range sortKeys(r.root.Schema.Traces) {
trace := r.root.Schema.Traces[name]
fields, err := traceFile.parseAttributes(xstrings.ToCamelCase(name), "Layout", trace.LayoutAttributes.Names)
if err != nil {
return fmt.Errorf("cannot parse attributes, %w", err)
Expand Down
106 changes: 78 additions & 28 deletions generator/renderer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
_ "embed"

"github.com/golang/mock/gomock"
. "github.com/onsi/ginkgo"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"

// . "github.com/onsi/gomega/format"

"github.com/MetalBlueberry/go-plotly/generator"
"github.com/MetalBlueberry/go-plotly/generator/mocks"
)
Expand All @@ -19,41 +21,89 @@ var schema []byte

var _ = Describe("Renderer", func() {

var (
ctrl *gomock.Controller
mockCreator *mocks.MockCreator
)
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
mockCreator = mocks.NewMockCreator(ctrl)
})
AfterEach(func() {
ctrl.Finish()
})
Describe("A single trace", func() {
var (
ctrl *gomock.Controller
mockCreator *mocks.MockCreator
)
BeforeEach(func() {
ctrl = gomock.NewController(GinkgoT())
mockCreator = mocks.NewMockCreator(ctrl)
})
AfterEach(func() {
ctrl.Finish()
})

It("Should create package", func() {
buf := NopWriterCloser{&bytes.Buffer{}}
It("Should create package", func() {
buf := NopWriterCloser{&bytes.Buffer{}}

mockCreator.EXPECT().Create(gomock.Eq("scatter_gen.go")).Return(buf, nil).Times(1)
mockCreator.EXPECT().Create(gomock.Eq("scatter_gen.go")).Return(buf, nil).Times(1)

root, err := generator.LoadSchema(bytes.NewReader(schema))
Expect(err).To(BeNil())
root, err := generator.LoadSchema(bytes.NewReader(schema))
Expect(err).To(BeNil())

r, err := generator.NewRenderer(mockCreator, root)
Expect(err).To(BeNil())
r, err := generator.NewRenderer(mockCreator, root)
Expect(err).To(BeNil())

err = r.CreateTrace(".", "scatter")
Expect(err).To(BeNil())
err = r.CreateTrace(".", "scatter")
Expect(err).To(BeNil())

formatted, err := format.Source(buf.Bytes())
Expect(err).To(BeNil())
formatted, err := format.Source(buf.Bytes())
Expect(err).To(BeNil())

Expect(string(formatted)).To(ContainSubstring(`package grob`))
// Type is defined
Expect(string(formatted)).To(ContainSubstring(`type Scatter struct`))
// Implements interface GetType()
Expect(string(formatted)).To(ContainSubstring(`func (trace *Scatter) GetType() TraceType`))
Expect(string(formatted)).To(ContainSubstring(`package grob`))
// Type is defined
Expect(string(formatted)).To(ContainSubstring(`type Scatter struct`))
// Implements interface GetType()
Expect(string(formatted)).To(ContainSubstring(`func (trace *Scatter) GetType() TraceType`))

})
})
Describe("When writing", func() {
var r *generator.Renderer

BeforeEach(func() {
root, err := generator.LoadSchema(bytes.NewReader(schema))
Expect(err).To(BeNil())

r, err = generator.NewRenderer(nil, root)
Expect(err).To(BeNil())
})

Describe("The config", func() {
It("Should be consistent", func() {

original := &bytes.Buffer{}
err := r.WriteConfig(original)
Expect(err).To(BeNil())

for i := 0; i < 10; i++ {
attempt := &bytes.Buffer{}
err = r.WriteConfig(attempt)
Expect(err).To(BeNil())
Expect(attempt).To(Equal(original))
}

})
})
Describe("The Layout", func() {
// TruncatedDiff = false
// MaxLength = 0
It("Should be consistent", func() {

original := &bytes.Buffer{}
err := r.WriteLayout(original)
Expect(err).To(BeNil())

for i := 0; i < 10; i++ {
attempt := &bytes.Buffer{}
err = r.WriteLayout(attempt)
Expect(err).To(BeNil())
Expect(attempt.String()).To(Equal(original.String()))
}

})
})
})
})

Expand Down
102 changes: 102 additions & 0 deletions generator/templates/plotly.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@

package grob

import (
"encoding/json"
)

// Generate the files
//go:generate go run ../generator/cmd/generator/main.go --schema ../generator/schema.json --output-directory .

// TraceType is the type for the TraceType field on every trace
type TraceType string

// Trace Every trace implements this interface
// It is useful for autocompletion, it is a better idea to use
// type assertions/switches to identify trace types
type Trace interface {
GetType() TraceType
}

// Traces is a slice of Traces
type Traces []Trace

// Fig is the base type for figures.
type Fig struct {
// Data The data to be plotted is described in an array usually called data, whose elements are trace objects of various types (e.g. scatter, bar etc) as documented in the Full Reference.
// https://plotly.com/javascript/reference
Data Traces `json:"data,omitempty"`

// Layout The layout of the plot – non-data-related visual attributes such as the title, annotations etc – is described in an object usually called layout, as documented in/ the Full Reference.
// https://plotly.com/javascript/reference/layout
Layout *Layout `json:"layout,omitempty"`

// Config High-level configuration options for the plot, such as the scroll/zoom/hover behaviour, is described in an object usually called config, as documented here. The difference between config and layout is that layout relates to the content of the plot, whereas config relates to the context in which the plot is being shown.
// https://plotly.com/javascript/configuration-options
Config *Config `json:"config,omitempty"`

// Animation is not yet implemented, feel free to insert custom a struct
Animation interface{} `json:"animation,omitempty"`
}

// AddTraces Is a shorthand to add figures to a given figure. It handles the case where the Traces value is nil.
func (fig *Fig) AddTraces(traces ...Trace) {
if fig.Data == nil {
fig.Data = make(Traces, 0)
}
fig.Data = append(fig.Data, traces...)
}

// UnmarshalJSON is a custom unmarshal function to properly handle special cases.
func (fig *Fig) UnmarshalJSON(data []byte) error {
var err error
tmp := unmarshalFig{}
err = json.Unmarshal(data, &tmp)
if err != nil {
return err
}

fig.Layout = tmp.Layout
fig.Config = tmp.Config

for i := range tmp.Data {
trace, err := UnmarshalTrace(tmp.Data[i])
if err != nil {
return err
}
fig.AddTraces(trace)
}
return nil
}

type unmarshalFig struct {
Data []json.RawMessage `json:"data,omitempty"`
Layout *Layout `json:"layout,omitempty"`
Config *Config `json:"config,omitempty"`
}

// Bool represents a *bool value. Needed to tell the differenc between false and nil.
type Bool *bool

var (
trueValue bool = true
falseValue bool = false

// True is a *bool with true value
True Bool = &trueValue
// False is a *bool with false value
False Bool = &falseValue
)

// String is a string value, can be a []string if arrayOK is true.
// numeric values are converted to string by plotly, so []<number> can work
type String interface{}

// Color A string describing color. Supported formats: - hex (e.g. '#d3d3d3') - rgb (e.g. 'rgb(255, 0, 0)') - rgba (e.g. 'rgb(255, 0, 0, 0.5)') - hsl (e.g. 'hsl(0, 100%, 50%)') - hsv (e.g. 'hsv(0, 100%, 100%)') - named colors (full list: http://www.w3.org/TR/css3-color/#svg-color)",
type Color interface{}

// ColorList A list of colors. Must be an {array} containing valid colors.
type ColorList []Color

// ColorScale A Plotly colorscale either picked by a name: (any of Greys, YlGnBu, Greens, YlOrRd, Bluered, RdBu, Reds, Blues, Picnic, Rainbow, Portland, Jet, Hot, Blackbody, Earth, Electric, Viridis, Cividis ) customized as an {array} of 2-element {arrays} where the first element is the normalized color level value (starting at *0* and ending at *1*), and the second item is a valid color string.
type ColorScale interface{}
2 changes: 1 addition & 1 deletion generator/typefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -360,7 +360,7 @@ func (file *typeFile) parseFlaglist(name string, attr *Attribute) error {
return nil
}

func sortKeys(attr map[string]*Attribute) []string {
func sortKeys[T any](attr map[string]*T) []string {
keys := make([]string, 0, len(attr))
for k := range attr {
keys = append(keys, k)
Expand Down
Loading

0 comments on commit 74e8195

Please sign in to comment.