Skip to content

Commit

Permalink
Merge pull request #2101 from authzed/add-new-schema-package
Browse files Browse the repository at this point in the history
Copy old schema package into new package
  • Loading branch information
tstirrat15 authored Oct 25, 2024
2 parents b02eed1 + 371405c commit c11aac6
Show file tree
Hide file tree
Showing 91 changed files with 29,996 additions and 0 deletions.
167 changes: 167 additions & 0 deletions pkg/composableschemadsl/compiler/compiler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package compiler

import (
"errors"
"fmt"

"google.golang.org/protobuf/proto"

"github.com/authzed/spicedb/pkg/composableschemadsl/dslshape"
"github.com/authzed/spicedb/pkg/composableschemadsl/input"
"github.com/authzed/spicedb/pkg/composableschemadsl/parser"
core "github.com/authzed/spicedb/pkg/proto/core/v1"
)

// InputSchema defines the input for a Compile.
type InputSchema struct {
// Source is the source of the schema being compiled.
Source input.Source

// Schema is the contents being compiled.
SchemaString string
}

// SchemaDefinition represents an object or caveat definition in a schema.
type SchemaDefinition interface {
proto.Message

GetName() string
}

// CompiledSchema is the result of compiling a schema when there are no errors.
type CompiledSchema struct {
// ObjectDefinitions holds the object definitions in the schema.
ObjectDefinitions []*core.NamespaceDefinition

// CaveatDefinitions holds the caveat definitions in the schema.
CaveatDefinitions []*core.CaveatDefinition

// OrderedDefinitions holds the object and caveat definitions in the schema, in the
// order in which they were found.
OrderedDefinitions []SchemaDefinition

rootNode *dslNode
mapper input.PositionMapper
}

// SourcePositionToRunePosition converts a source position to a rune position.
func (cs CompiledSchema) SourcePositionToRunePosition(source input.Source, position input.Position) (int, error) {
return cs.mapper.LineAndColToRunePosition(position.LineNumber, position.ColumnPosition, source)
}

type config struct {
skipValidation bool
objectTypePrefix *string
}

func SkipValidation() Option { return func(cfg *config) { cfg.skipValidation = true } }

func ObjectTypePrefix(prefix string) ObjectPrefixOption {
return func(cfg *config) { cfg.objectTypePrefix = &prefix }
}

func RequirePrefixedObjectType() ObjectPrefixOption {
return func(cfg *config) { cfg.objectTypePrefix = nil }
}

func AllowUnprefixedObjectType() ObjectPrefixOption {
return func(cfg *config) { cfg.objectTypePrefix = new(string) }
}

type Option func(*config)

type ObjectPrefixOption func(*config)

// Compile compilers the input schema into a set of namespace definition protos.
func Compile(schema InputSchema, prefix ObjectPrefixOption, opts ...Option) (*CompiledSchema, error) {
cfg := &config{}
prefix(cfg) // required option

for _, fn := range opts {
fn(cfg)
}

mapper := newPositionMapper(schema)
root := parser.Parse(createAstNode, schema.Source, schema.SchemaString).(*dslNode)
errs := root.FindAll(dslshape.NodeTypeError)
if len(errs) > 0 {
err := errorNodeToError(errs[0], mapper)
return nil, err
}

compiled, err := translate(translationContext{
objectTypePrefix: cfg.objectTypePrefix,
mapper: mapper,
schemaString: schema.SchemaString,
skipValidate: cfg.skipValidation,
}, root)
if err != nil {
var errorWithNode errorWithNode
if errors.As(err, &errorWithNode) {
err = toContextError(errorWithNode.error.Error(), errorWithNode.errorSourceCode, errorWithNode.node, mapper)
}

return nil, err
}

return compiled, nil
}

func errorNodeToError(node *dslNode, mapper input.PositionMapper) error {
if node.GetType() != dslshape.NodeTypeError {
return fmt.Errorf("given none error node")
}

errMessage, err := node.GetString(dslshape.NodePredicateErrorMessage)
if err != nil {
return fmt.Errorf("could not get error message for error node: %w", err)
}

errorSourceCode := ""
if node.Has(dslshape.NodePredicateErrorSource) {
es, err := node.GetString(dslshape.NodePredicateErrorSource)
if err != nil {
return fmt.Errorf("could not get error source for error node: %w", err)
}

errorSourceCode = es
}

return toContextError(errMessage, errorSourceCode, node, mapper)
}

func toContextError(errMessage string, errorSourceCode string, node *dslNode, mapper input.PositionMapper) error {
sourceRange, err := node.Range(mapper)
if err != nil {
return fmt.Errorf("could not get range for error node: %w", err)
}

formattedRange, err := formatRange(sourceRange)
if err != nil {
return err
}

source, err := node.GetString(dslshape.NodePredicateSource)
if err != nil {
return fmt.Errorf("missing source for node: %w", err)
}

return ErrorWithContext{
BaseCompilerError: BaseCompilerError{
error: fmt.Errorf("parse error in %s: %s", formattedRange, errMessage),
BaseMessage: errMessage,
},
SourceRange: sourceRange,
Source: input.Source(source),
ErrorSourceCode: errorSourceCode,
}
}

func formatRange(rnge input.SourceRange) (string, error) {
startLine, startCol, err := rnge.Start().LineAndColumn()
if err != nil {
return "", err
}

return fmt.Sprintf("`%s`, line %v, column %v", rnge.Source(), startLine+1, startCol+1), nil
}
Loading

0 comments on commit c11aac6

Please sign in to comment.