eeaao_codegen

package module
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: Dec 8, 2024 License: MIT Imports: 17 Imported by: 0

README

eeaao-codegen

everything-everywhere-all-at-once code generator

eeaao-codegen is a highly customizable code generation tool that empowers developers to integrate schema-defined interfaces (IDLs) into their projects seamlessly. Unlike traditional code generators, it does not impose predefined generation logic. Instead, it allows you to define your own rules and templates using its codelets.

Motivation

When you use a language-agnostic Interface Definition language (IDL) in your project --such as gRPC, Thrift, GraphQL, OpenAPI specification, AsyncAPI for pub-sub systems, SQL or prisma and so on-- more often than not, manually writing schema-to-language mappings or implementing a custom parser is a tedious and error-prone task. Code generators are a common solution to this problem. They automatically generate code from schema files, which can save you a lot of time and effort.

Typical code generators take schema files (e.g. openAPI specification, GraphQL schemas, Protobuf definitions...) as input and produce code in one of their predefined supported language. It works like a charm, until it doesn't. Because the codegen lacks awareness of your codebase, they often have to make trade-offs between the following:

  • to keep generated code be generic, codegen tries to cover wide variation of versions of language, dependencies and environments. sometimes it abuses reflection of language, or full of boilerplate codes that has overhead on runtime.
  • to be more idiomatic, codegen should make bold assumptions over source code, or provide enormous number of options and flags to tune the code generation. You have to read the documentation and try-and-error to find the best fit for your project. On out of luck, you might find them unusable without substantial customization.

To avoid both situation, eeaao-codegen does not provide any predefined generation logic. Instead, it is a tool that helps you write your own code generation logic, organized into units called codelet.

Usage

Assume you have openAPI specs in the openapi directory, a codelet defined in the ./codelet directory, and you want to generate code in the __generated__ directory.

eeaao-codegen-cli --specdir=./openapi --codeletdir=./codelet --outputdir=./__generated__

Codelet

codelet is a unit of code generation, which is a directory that contains below:

  • render.star : an entrypoint of codelet
  • templates : a directory that contains template files. render.star can use these files to render the code.
  • values.json: a global key-value json file that can contains anything (optional)

render.start is a starlark script that defines the code generation process. It must have a main() function. eeaao-codegen calls main() function on code generation.

def main():
    pass

of course, this is not useful. eeaao-codegen provides eeaao_codegen module to generate code. Belows are the functions that you can use in render.star:

Function Description
loadSpecFile(pluginName: str, filepath: str): Any load spec file of filepath with plugin of given name and return loaded spec data
loadSpecsGlob(pluginName: str, glob: str): Any load multilple spec files with globa patterns with plugin of given name and return loaded spec data
renderFile(filePath: str, templatePath: str, data: Any): str render templates/{templatePath} file with given data on filePath and return rendered file path
loadValues(filePaths: List[str], templatePath: str, data: Any): Any load values.json and return loaded value

using these functions, you can write your own codelet. below is the example codelet that generates a simple hello world

def main():
    # load openapi specs
    specs = eeaao_codegen.loadSpecFile("openapi", "petstore.yaml")

    # render a file
    eeaao_codegen.renderFile("petstoreApi.js", "petstoreApi.js.tmpl", specs)

The codelet above loads {specdir}/petstore.yaml file, reads template file {codeletdir}/templates/petstoreApi.js.tmpl, and renders the template with loaded spec data. The rendered file will be saved as {outputdir}/petstoreApi.js.

The template file used in renderFile must be valid go template files. below is the example template file that generates a simple javascript code from openapi spec.

// petstoreApi.js.tmpl
export const petstoreApi = {
{{- range $path, $pathSpec := .paths }}
    {{- range $method, $methodSpec := $pathSpec }}
    {{ $methodSpec.operationId }}: (data) => {
        return fetch("{{ $path }}", {
            method: "{{ $method }}",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify(data)
        })
    },
    {{- end }}
{{- end }}
}

Inside the template, eeaao-codegen exposes some variables and functions. You can use sprig and functions defined in HelperFuncs

Plugins

eeaao-codegen itself does not have any knowledge about schema language. It delegates the schema parsing to its internal plugins. Writing your own schema plugin is working in progress.

Currently, eeaao-codegen supports below plugins:

  • json
  • yaml
  • openapi
  • proto

Documentation

Overview

Package codelet declares the exposed functions for go/template and starlark built-ins.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ToStarlarkModule

func ToStarlarkModule(h HelperFuncs) *starlarkstruct.Module

ToStarlarkModule exposes the helper functions to starlarkstruct.Module.

The module provides the following functions:

  • loadSpecFile(pluginName: str, filepath: str) -> any
  • loadSpecsGlob(pluginName: str, glob: str) -> dict[str, any]
  • renderFile(filePath: str, templatePath: str, data: any) -> str
  • loadValues() -> dict[str, any]

due to the limitation of interoperability between Go and Starlark, JSON encoding is used internally.

For example, HelperFuncs.LoadValues() returns a map[string]any. When `eeaao_codegen.loadValues` is called in starlark script, the map[string]any is first encoded using json.Marshal and then decoded as starlark.Dict by `json.decode`.

func ToTemplateFuncmap

func ToTemplateFuncmap(h HelperFuncs) template.FuncMap

ToTemplateFuncmap converts the helper functions into a template.FuncMap for use with template.

The resulting FuncMap includes the following functions:

  • loadSpecFile: HelperFuncs.LoadSpecFile
  • loadSpecsGlob: HelperFuncs.LoadSpecsGlob
  • renderFile: HelperFuncs.RenderFile
  • loadValues: HelperFuncs.LoadValues

Additionally, it incorporates the sprig.FuncMap for extended functionality.

Types

type App

type App struct {
	OutDir     string
	CodeletDir string
	Values     map[string]any
	// contains filtered or unexported fields
}

func NewApp

func NewApp(specDir string, outDir string, codeletDir string, valuesFile string) *App

NewApp creates a new App instance specDir: directory for specifications outDir: directory for output codeletDir: directory for codelet valuesFile: file path of external values file. if empty, it will be ignored.

func (*App) Include

func (a *App) Include(templatePath string, data interface{}) (string, error)

func (*App) LoadSpecFile

func (a *App) LoadSpecFile(pluginName string, filePath string) (plugin.SpecData, error)

func (*App) LoadSpecsGlob

func (a *App) LoadSpecsGlob(pluginName string, glob string) (map[string]plugin.SpecData, error)

func (*App) LoadValues

func (a *App) LoadValues() map[string]any

func (*App) Render

func (a *App) Render() error

Render renders the templates. internally, it just runs `render.star` file in the CodeletDir

func (*App) RenderFile

func (a *App) RenderFile(filePath string, templatePath string, data any) (dst string, err error)

func (*App) RunShell

func (a *App) RunShell()

RunShell starts a REPL shell for testing

type HelperFuncs

type HelperFuncs interface {
	// LoadSpecFile loads plugin.SpecData from filePath with plugin of pluginName
	// filePath is relative path from spec directory
	LoadSpecFile(pluginName string, filePath string) (plugin.SpecData, error)
	// LoadSpecsGlob loads specs from the given glob pattern in the spec directory.
	// pluginName: the plugin name to load the specs
	// glob: the glob pattern to search for specs
	// returns a map of spec file path and spec content as json encoded string.
	LoadSpecsGlob(pluginName string, glob string) (specs map[string]plugin.SpecData, err error)
	// RenderFile renders a file with the given template and data
	// filePath: the file path to render. The path is relative to the output directory.
	// templatePath: the template path. The path is relative to the ${codeletdir}/templates directory.
	// data: the data to render
	// returns the destination file path.
	RenderFile(filePath string, templatePath string, data any) (dst string, err error)
	// LoadValues returns the values data from codelet's default values.yaml file and given values file.
	LoadValues() (config map[string]any)
	// Include renders a template with the given data.
	//
	// Drop-in replacement for template pipeline, but with a string return value so that it can be treated as a string in the template.
	//
	// Inspired by [helm include function](https://helm.sh/docs/chart_template_guide/named_templates/#the-include-function)
	Include(templatePath string, data interface{}) (string, error)
}

HelperFuncs defines the helper functions for codelet. The functions should be exposed to go/template and starlark built-ins for codelet.

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

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