presilo

package
v0.0.0-...-d77992a Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 5, 2023 License: Apache-2.0, Apache-2.0 Imports: 16 Imported by: 0

README

presilo

Build Status Godoc

Generates (and updates) code for data models from a JSON schema. Works for Go, C#, Java, Python, Lua, SQL, and Ruby.

Presilo is Esperanto for "printing press", because presilo makes it trivial to "reprint" the same content in different languages.

Why do I want this?

Before writing code, it's a good idea to define your data models. What fields is each structure going to have, the valid values for each field, and which fields need to be present for an instance to be valid. Doing this makes it more clear how your code will need to work, and lets you code your solutions much quicker and with less refactoring.

The best way I've found to do this is with a schema, which is a way to describe a data structure and its field.

Isn't there something else which does this?

There are a few projects which do similar things, but I don't find their patterns to match what I've found is the best method for defining schemas. I wanted one schema to be enforced on webservers (client request validation), client SDKs, and data persistence (DB schemas, file formats). One single schema that describes the data format for everything else. I wanted to generate large amounts of code for multiple languages, and not need to maintain any of it. I also wanted to be able to update existing code when a schema changes, without needing to manually edit code. I haven't found a project that does that.

There are some projects which take a piece of code, or a sql table definition, and try to generate other pieces of code or schemas. That's great if you fit exactly into those use cases. But I wanted to define a contract about the data which is implemented by code or a DB schema. And, specifically, I didn't want a language- or DB-specific solution, I wanted to use this everywhere.

Where's the binary?

This project is solely the library which handles parsing and codegen. You can reference this library in your own code, but most users will probably just want to head over to the executable project and find a release there.

Branching

I use green masters, and heavily develop with private feature branches. Full releases are pinned and unchangeable, representing the best available version with the best documentation and test coverage. Master branch, however, should always have all tests pass and implementations considered "working", even if it's just a first pass. Master should never panic.

Activity

If this repository hasn't been updated in a while, it's probably because I don't have any outstanding issues to work on - it's not because I've abandoned the project. If you have questions, issues, or patches; I'm completely open to pull requests, issues opened on github, or emails from out of the blue.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func GenerateCSharp

func GenerateCSharp(schema *ObjectSchema, module string, tabstyle string) string
  Generates valid CSharp code for a given schema.

	C# code contains DataContractJsonSerializer annotations,
	which are required for proper serialization/deserialization of fields.
	Unfortunately this isn't available before .NET 4.5, so any generated code
	will need to be compiled with .NET 4.5+

func GenerateCSharpTypeForSchema

func GenerateCSharpTypeForSchema(subschema TypeSchema) string

func GenerateGo

func GenerateGo(schema *ObjectSchema, module string, tabstyle string) string

Generates valid Go code for a given schema.

func GenerateGoTypeForSchema

func GenerateGoTypeForSchema(schema interface{}) string

Returns the Go equivalent of type for the given schema. If no type is found, this returns "interface{}"

func GenerateJava

func GenerateJava(schema *ObjectSchema, module string, tabstyle string) string

Generates valid Java code for a given schema.

func GenerateJavaTypeForSchema

func GenerateJavaTypeForSchema(subschema TypeSchema) string

func GenerateMySQL

func GenerateMySQL(schema *ObjectSchema, module string, tabstyle string) string

Generates valid mysql table creation/update query.

SQL query generation is a bit different than other languages, it: - Generates one query file to represent all data structures - Uses "module" not as a harmless namespace, but as the DB name tables should be contained - Does not attempt to represent arrays - Assumes internationalized "nvarchar" for all strings - Provides a best guess for string column length based on constraints for min/max length - Does not provide a primary key! - Does not support regex constraints. - Uses 'bit' to represent booleans, with 0 = true, 1 = false.

func GeneratePython

func GeneratePython(schema *ObjectSchema, module string, tabstyle string) string

func GenerateRuby

func GenerateRuby(schema *ObjectSchema, module string, tabstyle string) string

func LinkSchemas

func LinkSchemas(context *SchemaParseContext) error

"Links" any remaining unresolved schema references together. Returns an error if there are any schema references which cannot be resolved.

func ParseSchemaFile

func ParseSchemaFile(path string) (TypeSchema, *SchemaParseContext, error)

Parses (and returns) the schema from the given path.

func ParseSchemaStream

func ParseSchemaStream(reader io.Reader, defaultTitle string) (TypeSchema, *SchemaParseContext, error)

Parses (and returns) the schema from the given [reader]. Once this method ends, all returned schemas ought to be properly populated and resolved.

func ToCamelCase

func ToCamelCase(target string) string

Converts the given target string to CamelCase; e.g. "something" becomes "Something"

func ToJavaCase

func ToJavaCase(target string) string

Converts the given target string to javaCase, e.g. "SomethingElse" becomes "somethingElse"

func ToSnakeCase

func ToSnakeCase(target string) string

Converts the given target string to snake_case, e.g. "somethingQuiteFine" becomes "something_quite_fine"

func ToStrictCamelCase

func ToStrictCamelCase(target string) string

Same as ToCamelCase, except that any non-alphanumeric character is stripped from the returned value.

func ToStrictJavaCase

func ToStrictJavaCase(target string) string

Same as ToJavaCase, except that any non-alphanumeric character is stripped from the returned value.

func ToStrictSnakeCase

func ToStrictSnakeCase(target string) string

Same as "ToSnakeCase", except that any non-alphanumeric / underscore character is stripped from the returned value.

func ValidateCSharpModule

func ValidateCSharpModule(module string) bool

func ValidateGoModule

func ValidateGoModule(module string) bool

func ValidateJavaModule

func ValidateJavaModule(module string) bool

func ValidateMySQLModule

func ValidateMySQLModule(module string) bool

func ValidatePythonModule

func ValidatePythonModule(module string) bool

func ValidateRubyModule

func ValidateRubyModule(module string) bool

func WriteGeneratedCode

func WriteGeneratedCode(context *SchemaParseContext, module string, targetPath string, language string, tabstyle string, unsafeModule bool, splitFiles bool) error

Types

type ArraySchema

type ArraySchema struct {
	Schema

	// TODO: item schema should be able to have more than one type in it.
	// mixed types should be automatically set as subclasses of a common ancestor, in languages that support it
	Items TypeSchema

	MaxItems    *int  `json:"maxItems"`
	MinItems    *int  `json:"minItems"`
	UniqueItems *bool `json:"uniqueItems"`

	RawItems *json.RawMessage `json:"items"`
}

A schema which describes an array.

func NewArraySchema

func NewArraySchema() *ArraySchema

func ParseArraySchema

func ParseArraySchema(contents []byte, context *SchemaParseContext) (*ArraySchema, error)

Creates a new integer schema from a byte slice that can be interpreted as json.

func (*ArraySchema) HasConstraints

func (this *ArraySchema) HasConstraints() bool

type BooleanSchema

type BooleanSchema struct {
	Schema
}

A schema which describes an integer.

func NewBooleanSchema

func NewBooleanSchema() *BooleanSchema

func ParseBooleanSchema

func ParseBooleanSchema(contents []byte, context *SchemaParseContext) (*BooleanSchema, error)

Creates a new integer schema from a byte slice that can be interpreted as json.

func (*BooleanSchema) HasConstraints

func (this *BooleanSchema) HasConstraints() bool

type BufferedFormatString

type BufferedFormatString struct {
	// contains filtered or unexported fields
}

Represents a buffered string which

func NewBufferedFormatString

func NewBufferedFormatString(separator string) *BufferedFormatString

Returns a new buffered format string. The given [separator] is used to properly indent lines as content is written. Best practice is to use the tab character ("\t"), but some languages may have legacy standards encouraging spaces or other whitespace characters, which should be used if the language expects them.

func (*BufferedFormatString) AddIndentation

func (this *BufferedFormatString) AddIndentation(count int)

Adds the given [count] of indentation points to the current running total of indentation. Note that [count] can be negative, in order to decrement indentation.

Indentation will never go lower than zero.

func (*BufferedFormatString) Print

func (this *BufferedFormatString) Print(source string)

Appends the given [source] into this buffer.

func (*BufferedFormatString) Printf

func (this *BufferedFormatString) Printf(source string, parameters ...interface{})

Appends the given [source] into this buffer, interpolating the string in the same fashion as "fmt.Printf".

func (*BufferedFormatString) Printfln

func (this *BufferedFormatString) Printfln(source string, parameters ...interface{})

Same as "Printf", except appends a newline to the end of the given [source].

func (*BufferedFormatString) String

func (this *BufferedFormatString) String() string

Returns the current string representation of this buffer.

type IntegerSchema

type IntegerSchema struct {
	Schema
	Minimum          *int   `json:"minimum"`
	Maximum          *int   `json:"maximum"`
	ExclusiveMaximum *bool  `json:"exclusiveMaximum"`
	ExclusiveMinimum *bool  `json:"exclusiveMinimum"`
	MultipleOf       *int   `json:"multipleOf"`
	Enum             *[]int `json:"enum"`
}

A schema which describes an integer.

func NewIntegerSchema

func NewIntegerSchema() *IntegerSchema

func ParseIntegerSchema

func ParseIntegerSchema(contents []byte, context *SchemaParseContext) (*IntegerSchema, error)

Creates a new integer schema from a byte slice that can be interpreted as json.

func (*IntegerSchema) GetConstraintFormat

func (this *IntegerSchema) GetConstraintFormat() string

func (*IntegerSchema) GetEnum

func (this *IntegerSchema) GetEnum() []interface{}

func (*IntegerSchema) GetMaximum

func (this *IntegerSchema) GetMaximum() interface{}

func (*IntegerSchema) GetMinimum

func (this *IntegerSchema) GetMinimum() interface{}

func (*IntegerSchema) GetMultiple

func (this *IntegerSchema) GetMultiple() interface{}

func (*IntegerSchema) HasConstraints

func (this *IntegerSchema) HasConstraints() bool

func (*IntegerSchema) HasEnum

func (this *IntegerSchema) HasEnum() bool

func (*IntegerSchema) HasMaximum

func (this *IntegerSchema) HasMaximum() bool

func (*IntegerSchema) HasMinimum

func (this *IntegerSchema) HasMinimum() bool

func (*IntegerSchema) HasMultiple

func (this *IntegerSchema) HasMultiple() bool

func (*IntegerSchema) IsExclusiveMaximum

func (this *IntegerSchema) IsExclusiveMaximum() bool

func (*IntegerSchema) IsExclusiveMinimum

func (this *IntegerSchema) IsExclusiveMinimum() bool

type NumberSchema

type NumberSchema struct {
	Schema
	Minimum          *float64   `json:"minimum"`
	Maximum          *float64   `json:"maximum"`
	ExclusiveMinimum *bool      `json:"exclusiveMinimum"`
	ExclusiveMaximum *bool      `json:"exclusiveMaximum"`
	MultipleOf       *float64   `json:"multipleOf"`
	Enum             *[]float64 `json:"enum"`
}

A schema which describes a number, which may include floating point numbers.

func NewNumberSchema

func NewNumberSchema() *NumberSchema

func ParseNumberSchema

func ParseNumberSchema(contents []byte, context *SchemaParseContext) (*NumberSchema, error)

Creates a new integer schema from a byte slice that can be interpreted as json.

func (*NumberSchema) GetConstraintFormat

func (this *NumberSchema) GetConstraintFormat() string

func (*NumberSchema) GetEnum

func (this *NumberSchema) GetEnum() []interface{}

func (*NumberSchema) GetMaximum

func (this *NumberSchema) GetMaximum() interface{}

func (*NumberSchema) GetMinimum

func (this *NumberSchema) GetMinimum() interface{}

func (*NumberSchema) GetMultiple

func (this *NumberSchema) GetMultiple() interface{}

func (*NumberSchema) HasConstraints

func (this *NumberSchema) HasConstraints() bool

func (*NumberSchema) HasEnum

func (this *NumberSchema) HasEnum() bool

func (*NumberSchema) HasMaximum

func (this *NumberSchema) HasMaximum() bool

func (*NumberSchema) HasMinimum

func (this *NumberSchema) HasMinimum() bool

func (*NumberSchema) HasMultiple

func (this *NumberSchema) HasMultiple() bool

func (*NumberSchema) IsExclusiveMaximum

func (this *NumberSchema) IsExclusiveMaximum() bool

func (*NumberSchema) IsExclusiveMinimum

func (this *NumberSchema) IsExclusiveMinimum() bool

type NumericSchemaType

type NumericSchemaType interface {
	TypeSchema
	HasMinimum() bool
	HasMaximum() bool
	HasMultiple() bool
	HasEnum() bool
	GetMinimum() interface{}
	GetMaximum() interface{}
	GetMultiple() interface{}
	GetEnum() []interface{}
	IsExclusiveMaximum() bool
	IsExclusiveMinimum() bool
	GetConstraintFormat() string
}

type ObjectSchema

type ObjectSchema struct {
	Schema
	Properties         map[string]TypeSchema
	RequiredProperties []string `json:"required"`

	// TODO: MaxProperties *int `json:"maxProperties"`
	// TODO: MinProperties *int `json:"minProperties"`
	// TODO: AdditionalProperties *bool `json:"additionalProperties"`
	// NOT SUPPORTED: patternProperties
	RawProperties map[string]*json.RawMessage `json:"properties"`

	ConstrainedProperties   SortableStringArray
	UnconstrainedProperties SortableStringArray
}

A schema which describes an integer.

func NewObjectSchema

func NewObjectSchema() *ObjectSchema

func ParseObjectSchema

func ParseObjectSchema(contents []byte, context *SchemaParseContext) (*ObjectSchema, error)

Creates a new object schema from a byte slice that can be interpreted as json. Object schemas may contain multiple schemas.

func RecurseObjectSchemas

func RecurseObjectSchemas(schema TypeSchema, schemas []*ObjectSchema) []*ObjectSchema

Recurses the properties of the given [root], adding all sub-schemas to the given [schemas].

func (*ObjectSchema) AddProperty

func (this *ObjectSchema) AddProperty(name string, schema TypeSchema)

func (*ObjectSchema) GetOrderedPropertyNames

func (this *ObjectSchema) GetOrderedPropertyNames() []string

Returns an ordered array of property names, guaranteed to be the same for the same schema input over multiple runs of the program.

func (*ObjectSchema) HasConstraints

func (this *ObjectSchema) HasConstraints() bool

type Schema

type Schema struct {
	Title       string `json:"title"`
	ID          string `json:"id"`
	Description string `json:"description"`
	Nullable    bool
	// contains filtered or unexported fields
}

Represents the schema of one field in a json document.

func (*Schema) GetDescription

func (this *Schema) GetDescription() string

func (*Schema) GetID

func (this *Schema) GetID() string

func (*Schema) GetNullable

func (this *Schema) GetNullable() bool

func (*Schema) GetSchemaType

func (this *Schema) GetSchemaType() SchemaType

func (*Schema) GetTitle

func (this *Schema) GetTitle() string

func (*Schema) SetID

func (this *Schema) SetID(id string)

func (*Schema) SetNullable

func (this *Schema) SetNullable(nullable bool)

func (*Schema) SetTitle

func (this *Schema) SetTitle(title string)

type SchemaGraph

type SchemaGraph struct {
	// contains filtered or unexported fields
}

Represents a dependency graph that can order schemas.

func NewSchemaGraph

func NewSchemaGraph(schemas []*ObjectSchema) *SchemaGraph

func (*SchemaGraph) GetOrderedSchemas

func (this *SchemaGraph) GetOrderedSchemas() ([]*ObjectSchema, error)

Returns a slice of schemas which represents an ordering where dependent schemas are given first. If there is a circular dependency in this graph, an error is returned instead.

type SchemaGraphNode

type SchemaGraphNode struct {
	// contains filtered or unexported fields
}

type SchemaParseContext

type SchemaParseContext struct {
	SchemaDefinitions map[string]TypeSchema
}

Contains parsing context, such as the currently-defined schemas by ID, and schema-local definitions.

func NewSchemaParseContext

func NewSchemaParseContext() *SchemaParseContext

type SchemaType

type SchemaType int
const (
	SCHEMATYPE_OBJECT SchemaType = iota
	SCHEMATYPE_ARRAY
	SCHEMATYPE_STRING
	SCHEMATYPE_NUMBER
	SCHEMATYPE_INTEGER
	SCHEMATYPE_BOOLEAN
	SCHEMATYPE_UNRESOLVED
)

type SortableStringArray

type SortableStringArray []string

func (SortableStringArray) Len

func (this SortableStringArray) Len() int

Len is part of sort.Interface.

func (SortableStringArray) Less

func (this SortableStringArray) Less(i, j int) bool

Less is part of sort.Interface. It is implemented by calling the "by" closure in the sorter.

func (*SortableStringArray) Sort

func (this *SortableStringArray) Sort()

func (SortableStringArray) Swap

func (this SortableStringArray) Swap(i, j int)

Swap is part of sort.Interface.

type StringSchema

type StringSchema struct {
	Schema
	MaxLength     *int      `json:"maxLength"`
	MinLength     *int      `json:"minLength"`
	Pattern       *string   `json:"pattern"`
	MaxByteLength *int      `json:"maxByteLength"`
	MinByteLength *int      `json:"minByteLength"`
	Enum          *[]string `json:"enum"`
}

A schema which describes an integer.

func NewStringSchema

func NewStringSchema() *StringSchema

func ParseStringSchema

func ParseStringSchema(contents []byte, context *SchemaParseContext) (*StringSchema, error)

Creates a new integer schema from a byte slice that can be interpreted as json.

func (*StringSchema) GetEnum

func (this *StringSchema) GetEnum() []interface{}

func (*StringSchema) HasConstraints

func (this *StringSchema) HasConstraints() bool

func (*StringSchema) HasEnum

func (this *StringSchema) HasEnum() bool

type TypeSchema

type TypeSchema interface {
	GetSchemaType() SchemaType
	GetTitle() string
	GetDescription() string
	SetTitle(string)
	GetID() string
	SetID(string)
	GetNullable() bool
	SetNullable(bool)
	HasConstraints() bool
}

func ParseSchema

func ParseSchema(contentsBytes []byte, defaultTitle string, context *SchemaParseContext) (TypeSchema, error)

func ParseSchemaFileContinue

func ParseSchemaFileContinue(path string, context *SchemaParseContext) (TypeSchema, error)

Same as ParseSchemaStreamContinue, except that instead of a stream, this takes a filepath.

func ParseSchemaHTTPContinue

func ParseSchemaHTTPContinue(httpPath string, context *SchemaParseContext) (TypeSchema, error)

Same as ParseSchemaStreamContinue, except that instead of a stream, it takes an HTTP URL that will be fetched and parsed.

func ParseSchemaStreamContinue

func ParseSchemaStreamContinue(reader io.Reader, defaultTitle string, context *SchemaParseContext) (TypeSchema, error)

Parses the given [reader] into the given context, returning the resulatant schema as well as any error encountered. This is primarily useful for when you want to parse multiple schemas that are formatted normally, but are not presented in one reader. This might happen when parsing multiple files that reference each other, drawing from multiple input sources, or when schemas are given in some other form, like an array.

After using this method to parse all required schemas, you must call LinkSchemas() to resolve any outstanding unresolved schema references.

type UnresolvedSchema

type UnresolvedSchema struct {
	Reference string
}

Represents a schema which could not be resolved when it was encountered. Mainly serves as a placeholder so that when all actual schemas are defined, a later process can use this reference to properly link references. This should NEVER be used during codegen.

func NewUnresolvedSchema

func NewUnresolvedSchema(ref string) *UnresolvedSchema

func (*UnresolvedSchema) GetDescription

func (this *UnresolvedSchema) GetDescription() string

Used to satisfy the TypeSchema contract, stub.

func (*UnresolvedSchema) GetID

func (this *UnresolvedSchema) GetID() string

func (*UnresolvedSchema) GetNullable

func (this *UnresolvedSchema) GetNullable() bool

Used to satisfy the TypeSchema contract, stub.

func (*UnresolvedSchema) GetSchemaType

func (this *UnresolvedSchema) GetSchemaType() SchemaType

func (*UnresolvedSchema) GetTitle

func (this *UnresolvedSchema) GetTitle() string

Used to satisfy the TypeSchema contract, stub.

func (*UnresolvedSchema) HasConstraints

func (this *UnresolvedSchema) HasConstraints() bool

Used to satisfy the TypeSchema contract, stub.

func (*UnresolvedSchema) SetID

func (this *UnresolvedSchema) SetID(string)

func (*UnresolvedSchema) SetNullable

func (this *UnresolvedSchema) SetNullable(bool)

Used to satisfy the TypeSchema contract, stub.

func (*UnresolvedSchema) SetTitle

func (this *UnresolvedSchema) SetTitle(string)

Used to satisfy the TypeSchema contract, stub.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL