Skip to content

Commit

Permalink
Pulled in changes from staging, and fixed things up. e2e tests workin…
Browse files Browse the repository at this point in the history
…g on scale-cli
  • Loading branch information
jimmyaxod committed Nov 22, 2023
1 parent d814e74 commit 3cf51d5
Show file tree
Hide file tree
Showing 18 changed files with 275 additions and 348 deletions.
5 changes: 2 additions & 3 deletions build/golang.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ import (
"github.com/loopholelabs/scale/extension"

"github.com/loopholelabs/scale/compile/golang"
"github.com/loopholelabs/scale/extension"
"github.com/loopholelabs/scale/scalefile"
"github.com/loopholelabs/scale/scalefunc"
"github.com/loopholelabs/scale/signature"
Expand Down Expand Up @@ -122,7 +121,7 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.V1BetaSchema, error) {
}

if len(options.ExtensionSchemas) != len(options.Scalefile.Extensions) {
return nil, fmt.Errorf("number of extension schemas does not match number of extensions in scalefile")
return nil, fmt.Errorf("number of extension schemas does not match number of extensions in scalefile %d %d", len(options.ExtensionSchemas), len(options.Scalefile.Extensions))
}

if !filepath.IsAbs(options.SourceDirectory) {
Expand Down Expand Up @@ -195,7 +194,7 @@ func LocalGolang(options *LocalGolangOptions) (*scalefunc.V1BetaSchema, error) {
_ = options.Storage.Delete(build)
}()

modfile, err := golang.GenerateGoModfile(signatureInfo, functionInfo)
modfile, err := golang.GenerateGoModfile(signatureInfo, functionInfo, options.Extensions)
if err != nil {
return nil, fmt.Errorf("unable to generate go.mod file: %w", err)
}
Expand Down
12 changes: 7 additions & 5 deletions compile/golang/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"go/format"
"text/template"

"github.com/loopholelabs/scale/extension"
"github.com/loopholelabs/scale/signature"

"github.com/loopholelabs/scale/version"
Expand All @@ -28,8 +29,8 @@ import (

var generator *Generator

func GenerateGoModfile(signatureInfo *SignatureInfo, functionInfo *FunctionInfo) ([]byte, error) {
return generator.GenerateGoModfile(signatureInfo, functionInfo)
func GenerateGoModfile(signatureInfo *SignatureInfo, functionInfo *FunctionInfo, extensionInfo []extension.Info) ([]byte, error) {
return generator.GenerateGoModfile(signatureInfo, functionInfo, extensionInfo)
}

func GenerateGoMain(scalefileSchema *scalefile.Schema, signatureSchema *signature.Schema, functionInfo *FunctionInfo) ([]byte, error) {
Expand All @@ -50,14 +51,15 @@ func New() *Generator {
}
}

func (g *Generator) GenerateGoModfile(signatureInfo *SignatureInfo, functionInfo *FunctionInfo) ([]byte, error) {
func (g *Generator) GenerateGoModfile(signatureInfo *SignatureInfo, functionInfo *FunctionInfo, extensionInfo []extension.Info) ([]byte, error) {
signatureInfo.normalize()
functionInfo.normalize()

buf := new(bytes.Buffer)
err := g.template.ExecuteTemplate(buf, "mod.go.templ", map[string]interface{}{
"function": functionInfo,
"signature": signatureInfo,
"function": functionInfo,
"signature": signatureInfo,
"extensions": extensionInfo,
})
if err != nil {
return nil, err
Expand Down
4 changes: 4 additions & 0 deletions compile/golang/templates/mod.go.templ
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ replace signature v0.1.0 => {{ .signature.ImportPath }} {{ .signature.ImportVers

replace {{ .function.PackageName }} v0.1.0 => {{ .function.ImportPath }}

{{ range $extension := .extensions }}
replace {{ $extension.Name }} => {{ $extension.Path }}
{{ end }}

require (
signature v0.1.0
{{ .function.PackageName }} v0.1.0
Expand Down
167 changes: 65 additions & 102 deletions extension/schema.go → extension/extension.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,44 +18,20 @@ package extension

import (
"crypto/sha256"
"errors"
"fmt"
"os"
"regexp"

"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsyntax"
"github.com/hashicorp/hcl/v2/hclwrite"
"golang.org/x/text/cases"
"golang.org/x/text/language"

"github.com/loopholelabs/scale/signature"
)

const (
V1AlphaVersion = "v1alpha"
)

var (
ErrInvalidName = errors.New("invalid name")
ErrInvalidFunctionName = errors.New("invalid function name")
ErrInvalidTag = errors.New("invalid tag")
)

var (
ValidLabel = regexp.MustCompile(`^[A-Za-z0-9]*$`)
InvalidString = regexp.MustCompile(`[^A-Za-z0-9-.]`)
)

var (
TitleCaser = cases.Title(language.Und, cases.NoLower)
)

// Schema is the top-level structure of a Scale Extension schema
type Schema struct {
Version string `hcl:"version,attr"`
Name string `hcl:"name,attr"`
Tag string `hcl:"tag,attr"`
Interfaces []*InterfaceSchema `hcl:"interface,block"`
Functions []*FunctionSchema `hcl:"function,block"`
Enums []*signature.EnumSchema `hcl:"enum,block"`
Expand All @@ -66,6 +42,7 @@ type Schema struct {
hasCaseModifier bool
}

// ReadSchema reads a Scale Extension schema from a file at the given path
func ReadSchema(path string) (*Schema, error) {
data, err := os.ReadFile(path)
if err != nil {
Expand All @@ -76,6 +53,9 @@ func ReadSchema(path string) (*Schema, error) {
return s, s.Decode(data)
}

// Decode decodes the given byte slice into the Schema
//
// Note: This function modifies the Schema in-place and validates/normalizes it as well.
func (s *Schema) Decode(data []byte) error {
file, diag := hclsyntax.ParseConfig(data, "", hcl.Pos{Line: 1, Column: 1})
if diag.HasErrors() {
Expand All @@ -87,36 +67,47 @@ func (s *Schema) Decode(data []byte) error {
return diag.Errs()[0]
}

err := s.validateAndNormalize()
if err != nil {
return err
}

return nil
}

// Encode encodes the Schema into a byte slice
func (s *Schema) Encode() ([]byte, error) {
f := hclwrite.NewEmptyFile()
gohcl.EncodeIntoBody(s, f.Body())
return f.Bytes(), nil
}

func (s *Schema) Validate() error {
// validateAndNormalize validates the Schema and normalizes it
//
// Note: This function modifies the Schema in-place
func (s *Schema) validateAndNormalize() error {
switch s.Version {
case V1AlphaVersion:
if !ValidLabel.MatchString(s.Name) {
return ErrInvalidName
}

if InvalidString.MatchString(s.Tag) {
return ErrInvalidTag
}

case signature.V1AlphaVersion:
// Transform all model names and references to TitleCase (e.g. "myModel" -> "MyModel")
for _, model := range s.Models {
model.Normalize()
}

// Transform all model names and references to TitleCase (e.g. "myModel" -> "MyModel")
// Transform all enum names to TitleCase (e.g. "myModel" -> "MyModel")
for _, enum := range s.Enums {
enum.Normalize()
}

// Transform all function names and references to TitleCase (e.g. "myFunction" -> "MyFunction")
for _, function := range s.Functions {
function.Normalize()
}

// Transform all interface names and references to TitleCase (e.g. "myInterface" -> "MyInterface")
for _, inter := range s.Interfaces {
inter.Normalize()
}

// Validate all models
knownModels := make(map[string]struct{})
for _, model := range s.Models {
Expand All @@ -135,6 +126,22 @@ func (s *Schema) Validate() error {
}
}

knownFunctions := make(map[string]struct{})
for _, function := range s.Functions {
err := function.Validate(knownFunctions)
if err != nil {
return err
}
}

knownInterfaces := make(map[string]map[string]struct{})
for _, inter := range s.Interfaces {
err := inter.Validate(knownInterfaces)
if err != nil {
return err
}
}

// Ensure all model and enum references are valid
for _, model := range s.Models {
for _, modelReference := range model.Models {
Expand Down Expand Up @@ -262,69 +269,37 @@ func (s *Schema) Validate() error {
}
}

// Map of interfaces, and check for name collisions.
knownInterfaces := make(map[string]struct{})
for _, inter := range s.Interfaces {
_, dupe := knownModels[inter.Name]
if dupe {
return fmt.Errorf("interface name collides with a model %s", inter.Name)
}
_, dupe = knownInterfaces[inter.Name]
if dupe {
return fmt.Errorf("interface name collides with an interface %s", inter.Name)
}
knownInterfaces[inter.Name] = struct{}{}
}

for _, inter := range s.Interfaces {
for _, f := range inter.Functions {
// Make sure the function name is ok
if !ValidLabel.MatchString(f.Name) {
return ErrInvalidFunctionName
}

// Make sure the params exist as model.
if f.Params != "" {
f.Params = TitleCaser.String(f.Params)
if _, ok := knownModels[f.Params]; !ok {
return fmt.Errorf("unknown params in function %s: %s", f.Name, f.Params)
}
// Ensure all model and enum references are valid
for _, function := range s.Functions {
if function.Params != "" {
if _, ok := knownModels[function.Params]; !ok {
return fmt.Errorf("unknown %s.params: %s", function.Name, function.Params)
}
}

// Return can either be a model or interface
if f.Return != "" {
f.Return = TitleCaser.String(f.Return)
_, foundModel := knownModels[f.Return]
_, foundInterface := knownInterfaces[f.Return]
if !foundModel && !foundInterface {
return fmt.Errorf("unknown return in function %s: %s", f.Name, f.Return)
if function.Return != "" {
if _, ok := knownModels[function.Return]; !ok {
if _, ok = knownInterfaces[function.Return]; !ok {
return fmt.Errorf("unknown %s.return: %s", function.Name, function.Return)
}
}
}
}

// Check any global functions
for _, f := range s.Functions {
// Make sure the function name is ok
if !ValidLabel.MatchString(f.Name) {
return ErrInvalidFunctionName
}

// Make sure the params exist as model.
if f.Params != "" {
f.Params = TitleCaser.String(f.Params)
if _, ok := knownModels[f.Params]; !ok {
return fmt.Errorf("unknown params in function %s: %s", f.Name, f.Params)
for _, inter := range s.Interfaces {
for _, function := range inter.Functions {
if function.Params != "" {
if _, ok := knownModels[function.Params]; !ok {
return fmt.Errorf("unknown %s.%s.params: %s", inter.Name, function.Name, function.Params)
}
}
}

// Return can either be a model or interface
if f.Return != "" {
f.Return = TitleCaser.String(f.Return)
_, foundModel := knownModels[f.Return]
_, foundInterface := knownInterfaces[f.Return]
if !foundModel && !foundInterface {
return fmt.Errorf("unknown return in function %s: %s", f.Name, f.Return)
if function.Return != "" {
if _, ok := knownModels[function.Return]; !ok {
if _, ok = knownInterfaces[function.Return]; !ok {
return fmt.Errorf("unknown %s.%s.return: %s", inter.Name, function.Name, function.Return)
}
}
}
}
}
Expand Down Expand Up @@ -520,16 +495,6 @@ func (s *Schema) CloneWithDisabledAccessorsValidatorsAndModifiers() (*Schema, er
return clone, clone.validateAndNormalize()
}

// validateAndNormalize validates the Schema and normalizes it
//
// Note: This function modifies the Schema in-place
func (s *Schema) validateAndNormalize() error {

// TODO...

return nil
}

func (s *Schema) HasLimitValidator() bool {
return s.hasLimitValidator
}
Expand Down Expand Up @@ -557,8 +522,6 @@ func ValidPrimitiveType(t string) bool {

const MasterTestingSchema = `
version = "v1alpha"
name = "HttpFetch"
tag = "alpha"
function New {
params = "HttpConfig"
Expand Down
9 changes: 4 additions & 5 deletions extension/generator/generator.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ type Options struct {

GolangPackageImportPath string
GolangPackageName string
GolangPackageVersion string

RustPackageName string
RustPackageVersion string
Expand All @@ -75,12 +74,12 @@ func GenerateGuestLocal(options *Options) (*GuestLocalPackage, error) {
return nil, err
}

golangGuest, err := golang.GenerateGuest(options.Extension, hashString, options.GolangPackageName, options.GolangPackageVersion)
golangGuest, err := golang.GenerateGuest(options.Extension, hashString, options.GolangPackageName)
if err != nil {
return nil, err
}

golangInterfaces, err := golang.GenerateInterfaces(options.Extension, options.GolangPackageName, options.GolangPackageVersion)
golangInterfaces, err := golang.GenerateInterfaces(options.Extension, options.GolangPackageName)
if err != nil {
return nil, err
}
Expand Down Expand Up @@ -136,12 +135,12 @@ func GenerateHostLocal(options *Options) (*HostLocalPackage, error) {
return nil, err
}

golangHost, err := golang.GenerateHost(options.Extension, hashString, options.GolangPackageName, options.GolangPackageVersion)
golangHost, err := golang.GenerateHost(options.Extension, hashString, options.GolangPackageName)
if err != nil {
return nil, err
}

golangInterfaces, err := golang.GenerateInterfaces(options.Extension, options.GolangPackageName, options.GolangPackageVersion)
golangInterfaces, err := golang.GenerateInterfaces(options.Extension, options.GolangPackageName)
if err != nil {
return nil, err
}
Expand Down
Loading

0 comments on commit 3cf51d5

Please sign in to comment.