generator

package module
v0.1.17 Latest Latest
Warning

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

Go to latest
Published: Oct 2, 2024 License: MIT Imports: 19 Imported by: 1

README

go-generator

A Go package used for generating Go codes.

Usage

Initialization

First and foremost, you have to initialize the template. You can do like this:

import (
   "text/template"
)

type GenData struct {
   // Initialize here the various fields that will be used in the template.
}

var (
   my_template *template.Template
   my_generator *ggen.CodeGenerator
)

func init() {
   my_template = template.Must(template.New("").Parse(templ))

   tmp, err := ggen.NewCodeGenerator[*GenData](my_template)
   if err != nil {
      // Handle the error. (e.g. print the error message)
   }

   my_generator = tmp

   // Set the various do functions if needed.
   // my_generator.SetDoFunc(func(g *GenData) error {
   //    // Do something
   //
   //    return nil
   // }
}

const templ = "my template"

Or you can do like this:

import (
   "text/template"
)

type GenData struct {
   // Initialize here the various fields that will be used in the template.
}

var (
   my_generator *ggen.CodeGenerator
)

func init() {
   tmp, err := ggen.NewCodeGeneratorFromTemplate[*GenData]("", templ)
   if err != nil {
      // Handle the error. (e.g. print the error message)
   }

   my_generator = tmp

   // Set the various do functions if needed.
   // my_generator.SetDoFunc(func(g *GenData) error {
   //    // Do something
   //
   //    return nil
   // }
}

const templ = "my template"

The latter is much easier.

Setup

Then, you have to initialize the various flags. The only mandatory flag is ggen.SetOutputFlag() as it specifies the location of the output file.

After that, you have to declare a struct that will be used in the template. For this example, we will use the struct GenData. This struct must implement the SetPackageName(pkg_name string) ggen.Generater method as it will be used to set the package name for the generated code.

Main Function

The main function is composed of the following steps:

  1. Flag Parsing: Call the ggen.ParseFlags() method to parse the command-line flags and any flag set by the user.
  2. Validation: Do some validation or sanity checks.
  3. Fixing: Call the ggen.FixOutputLoc() method to fix the output location. This will validate and fix the output path if necessary.
  4. Generation: Call the ggen.Generate() method to generate the code. This method requires the following parameters:
    • output_loc: The location of the output file.
    • data: The data that will be used in the template. Ideally, this should only be an empty struct as any other field should be initialized in the functions. However, for complex checks or other reasons, they can be initialized in the "validation" step.
    • template: The template that will be used to generate the code.
    • doFunc: Here, you can specify any function that will be called right before generating the code. It is suggested to initialize all the struct fields in this portion.

Here's an example of a simple usage:

func main() {
   // 1. Call ParseFlags() method to parse the command-line flags.
	err := ggen.ParseFlags()
	if err != nil {
		// Handle the error. (e.g. print the error message)
	}

	// 2. Do some validation.

   // 3. Generate the code.
   data, err := my_generator.Generate(
      "foo", "_template.go",
      &GenData{
         // Initialize here the initial values of the struct that will be used in the template.
      },
   )
	if err != nil {
		// Handle the error.
	}

   // 4. Print the generated code to either stdout or a file.
}
Additional Notes

Logging

A common way to handle errors is to log them with the log.Fatalf() function. Because of that, I provided the InitLogger() function that initializes a common logger.

my_logger := ggen.InitLogger(os.Stdout, "my_logger")

This code will create a new logger that will print the file and line number where the error occurred. This is equivalent to do as follows:

my_logger := *log.New(os.Stdout, "[my_logger]: ", log.Lshortfile)

Naming Validation

Usually, generation requires a name of the type that is generated. To simplify this process, I provided the IsValidName() function that checks if the name is valid. Here's an example:

err = ggen.IsValidName("foo", []string{"bar"}, ggen.Exported)
if err != nil {
	// Handle the error. (e.g. print the error message)
}

Here's a breakdown:

  • "foo": This parameter is the name given by the user and, often, passed through a flag. For instance, if we wanted to generate a function named foo, we would pass "foo" as the parameter.
  • []string{"bar"}: This parameter is a list of all names that cannot be used as a valid name. For instance, if the user specified a name that would conflict with the name of an existing function, we would pass the name of that function as a member of the list. IMPORTANT: Go keywords are already excluded and so, it is not necessary to include them in the list. Thus, names such as "var", "chan", and so on are never allowed.
  • ggen.Exported: This parameter checks the casing of the name. More specifically:
    • ggen.Exported will check if the name starts with an uppercase letter.
    • ggen.NotExported will check if the name starts with a lowercase letter.
    • ggen.Either will not check if the name starts with an uppercase letter or a lowercase letter.

Therefore, if you need a lowercase name that is specified by the user and not "foo", you would specify the parameter as follows:

var (
   type_name *string = flag.String("type", "", "The type of the linked stack.")
)

err = ggen.IsValidName(type_name, []string{"foo"}, ggen.NotExported)
if err != nil {
   // Handle the error. (e.g. print the error message)
}

Type Signatures

For handling type signatures, I provided the MakeTypeSig() function. Here's an example:

sig, err := ggen.MakeTypeSig("foo", "bar")
if err != nil {
   // Handle the error. (e.g. print the error message)
}

This function will create a type signature "foobar" because the first parameter is the name of the type and the second parameter is the suffix.

One particular thing of this function is that it also handles generics. In fact, if the flag GenericsSigFlag is set, then the function will add the generics signature to the type signature. As such, it is useful for handling methods that have generics.

For example:

// Assuming that the GenericsSigFlag is set as: "foo/T,bar/C"

sig, err := ggen.MakeTypeSig("foo", "bar")
if err != nil {
   // Handle the error. (e.g. print the error message)
}

// The output will be: "foobar[T, C]"

Likewise, to have the full type signature, we can call the String() method of the GenericsSigFlag:

// Assuming that the GenericsSigFlag is set as: "foo/T,bar/C"

type_name := "foobar" + GenericsSigFlag.String()

// The output will be: "foobar[T any, C any]"

Of course, if the GenericsSigFlag is set but no generics are specified, then the function will return an empty string.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AlignGenerics

func AlignGenerics(g *GenericsSignVal, values ...flag.Value) error

AlignGenerics aligns the generics in the given values.

Parameters:

  • g: The *GenericsSignVal to align.
  • values: The values to align.

Returns:

  • error: An error if occurred.

func FixVariableName

func FixVariableName(variable_name string, keywords []string, exported GoExport) (string, error)

FixVariableName acts in the same way as IsValidName but fixes the variable name if it is invalid.

Parameters:

  • variable_name: The variable name to check.
  • keywords: The list of keywords to check against.
  • exported: Whether the variable is exported or not.

Returns:

  • string: The fixed variable name.
  • error: An error if the variable name is invalid.

func GetPackages

func GetPackages(packages []string) []string

GetPackages returns a list of packages from a list of strings.

Parameters:

  • packages: The list of strings to get the packages from.

Returns:

  • []string: The list of packages. Never returns nil.

func GetStringFnCall

func GetStringFnCall(var_name string, type_name string, custom map[string][]string) (string, []string)

GetStringFnCall returns the string function call for the given element. It is just a wrapper around the reflect.GetStringOf function.

Parameters:

  • var_name: The name of the variable.
  • type_name: The name of the type.
  • custom: The custom strings to use. Empty values are ignored.

Returns:

  • string: The string function call.
  • []string: The dependencies of the string function call.

func InitLogger

func InitLogger(out io.Writer, name string) *log.Logger

InitLogger initializes the logger.

Parameters:

  • out: The output writer. Defaults to os.Stdout.
  • name: The name of the logger. Defaults to "go-generator".

Returns:

  • *log.Logger: The initialized logger. Never returns nil.

func IsValidVariableName

func IsValidVariableName(variable_name string, keywords []string, exported GoExport) error

IsValidVariableName checks if the given variable name is valid.

This function checks if the variable name is not empty and if it is not a Go reserved keyword. It also checks if the variable name is not in the list of keywords.

Parameters:

  • variable_name: The variable name to check.
  • keywords: The list of keywords to check against.
  • exported: Whether the variable is exported or not.

Returns:

  • error: An error if the variable name is invalid.

If the variable is exported, the function checks if the variable name starts with an uppercase letter. If the variable is not exported, the function checks if the variable name starts with a lowercase letter. Any other case, the function does not perform any checks.

func MakeTypeSign

func MakeTypeSign(g *GenericsSignVal, type_name string, suffix string) (string, error)

MakeTypeSign creates a type signature from a type name and a suffix.

It also adds the generic signature if it exists.

Parameters:

  • type_name: The name of the type.
  • suffix: The suffix of the type.

Returns:

  • string: The type signature.
  • error: An error if the type signature cannot be created. (i.e., the type name is empty)

func ParseFlags

func ParseFlags()

ParseFlags parses the command line flags.

func PrintFlags

func PrintFlags()

PrintFlags prints the default values of the flags.

It is useful for debugging and for error messages.

func ZeroValueOf

func ZeroValueOf(type_name string, custom map[string]string) string

ZeroValueOf returns the zero value of a type.

Parameters:

  • type_name: The name of the type.
  • custom: A map of custom types and their zero values.

Returns:

  • string: The zero value of the type.

Types

type CodeGenerator

type CodeGenerator[T PackageNameSetter] struct {
	// contains filtered or unexported fields
}

CodeGenerator is the code generator.

func NewCodeGenerator

func NewCodeGenerator[T PackageNameSetter](templ *template.Template) (*CodeGenerator[T], error)

NewCodeGenerator creates a new code generator.

Parameters:

  • templ: The template to use for the generated code.

Returns:

  • *CodeGenerator: The code generator.
  • error: An error of type *errors.ErrInvalidParameter if 'templ' is nil.

func NewCodeGeneratorFromTemplate

func NewCodeGeneratorFromTemplate[T PackageNameSetter](name, templ string) (*CodeGenerator[T], error)

NewCodeGeneratorFromTemplate creates a new code generator from a template. Panics if the template is not valid.

Parameters:

  • name: The name of the template.
  • templ: The template to use for the generated code.

Returns:

  • *CodeGenerator: The code generator.
  • error: An error if template.Parse fails.

func (*CodeGenerator[T]) AddDoFunc

func (cg *CodeGenerator[T]) AddDoFunc(do_func DoFunc[T]) bool

AddDoFunc adds a function to perform on the data before generating the code.

Parameters:

  • do_func: The function to perform on the data before generating the code.

Returns:

  • bool: True if neither the receiver nor the 'do_func' are nil, false otherwise.

func (CodeGenerator[T]) Generate

func (cg CodeGenerator[T]) Generate(o *OutputLocVal, default_file_name string, data T) (*Generated, error)

Generate generates code using the given generator and writes it to the given destination file.

WARNING:

  • Remember to call this function iff the function go-generator.SetOutputFlag() was called and only after the function flag.Parse() was called.

Parameters:

  • file_name: The file name to use for the generated code.
  • suffix: The suffix to use for the generated code. This should end with the ".go" extension.
  • data: The data to use for the generated code.

Returns:

  • string: The output location of the generated code.
  • error: An error if occurred.

Errors:

  • *common.ErrInvalidParameter: If the file_name or suffix is an empty string.
  • error: Any other type of error that may have occurred.

func (CodeGenerator[T]) GenerateWithLoc

func (cg CodeGenerator[T]) GenerateWithLoc(loc string, data T) (*Generated, error)

Generate generates code using the given generator and writes it to the given destination file.

WARNING:

  • Remember to call this function iff the function go-generator.SetOutputFlag() was called and only after the function flag.Parse() was called.

Parameters:

  • file_name: The file name to use for the generated code.
  • suffix: The suffix to use for the generated code. This should end with the ".go" extension.
  • data: The data to use for the generated code.

Returns:

  • string: The output location of the generated code.
  • error: An error if occurred.

Errors:

  • *common.ErrInvalidParameter: If the file_name or suffix is an empty string.
  • error: Any other type of error that may have occurred.

type DoFunc

type DoFunc[T PackageNameSetter] func(data T) error

DoFunc is the type of the function to perform on the data before generating the code.

Parameters:

  • data: The data to perform the function on.

Returns:

  • error: An error if occurred.

type ErrorCode added in v0.1.17

type ErrorCode int

ErrorCode represents an error code.

const (
	// BadID occurs when an identifier is invalid.
	BadID ErrorCode = iota

	// BadType occurs when the type of the generic is invalid.
	BadGeneric
)

func (ErrorCode) Int added in v0.1.17

func (e ErrorCode) Int() int

Error implements the errors.ErrorCoder interface.

func (ErrorCode) String added in v0.1.17

func (i ErrorCode) String() string

type Generated

type Generated struct {
	// DestLoc is the destination location of the generated code.
	DestLoc string

	// Data is the data to use for the generated code.
	Data []byte
}

Generated is the type containing the generated code and its location.

func (*Generated) ModifyPrefixPath

func (g *Generated) ModifyPrefixPath(prefix string, sub_directories ...string) bool

ModifyPrefixPath modifies the path of the generated code.

Parameters:

  • prefix: The prefix to add to the file name. If empty, no prefix is added.
  • sub_directories: The sub directories to create the file in.

Returns:

  • bool: True if the receiver is not nil, false otherwise.

The prefix is useful for when generating multiple files as it adds a prefix without changing the extension.

func (*Generated) ModifySuffixPath

func (g *Generated) ModifySuffixPath(suffix string, sub_directories ...string) bool

ModifySuffixPath modifies the path of the generated code.

Parameters:

  • suffix: The suffix to add to the file name. If empty, no suffix is added.
  • sub_directories: The sub directories to create the file in.

Returns:

  • bool: True if the receiver is not nil, false otherwise.

The suffix is useful for when generating multiple files as it adds a suffix without changing the extension.

func (*Generated) ReplaceFileName added in v0.1.17

func (g *Generated) ReplaceFileName(file_name string) bool

ReplaceFileName replaces the file name of the generated code.

Parameters:

  • file_name: The file name to use for the generated code.

Returns:

  • bool: True if the receiver is not nil, false otherwise.

The file name is useful for when generating multiple files as it adds a prefix without changing the extension.

func (Generated) WriteFile

func (g Generated) WriteFile() error

WriteFile writes the generated code to the destination file.

Parameters:

  • suffix: The suffix to add to the file name. If empty, no suffix is added.
  • sub_directories: The sub directories to create the file in.

Returns:

  • error: An error if occurred.

The suffix is useful for when generating multiple files as it adds a suffix without changing the extension.

type GenericsSignVal

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

GenericsSignVal is a struct that contains the values of the generics.

func NewGenericsSignFlag

func NewGenericsSignFlag(flag_name string, is_required bool, count int) *GenericsSignVal

NewGenericsSignFlag sets the flag that specifies the generics to generate the code for.

Parameters:

  • flag_name: The name of the flag.
  • is_required: Whether the flag is required or not.
  • count: The number of generics. If -1, no upper bound is set, 0 means no generics.

Returns:

  • *GenericsSignVal: The value of the flag.

This function returns nil iff count is 0.

Documentation:

**Flag: Generics**

This optional flag is used to specify the type(s) of the generics. However, this only applies if at least one generic type is specified in the fields flag. If none, then this flag is ignored.

As an edge case, if this flag is not specified but the fields flag contains generics, then all generics are set to the default value of "any".

As with the fields flag, its argument is specified as a list of key-value pairs where each pair is separated by a comma (",") and a slash ("/") is used to separate the key and the value. The key indicates the name of the generic and the value indicates the type of the generic.

For instance, running the following command:

//go:generate treenode -type="TreeNode" -fields=a/MyType[T],b/MyType[C] -g=T/any,C/int

will generate a tree node with the following fields:

type TreeNode[T any, C int] struct {
	// Node pointers.

	a T
	b C
}

func (*GenericsSignVal) Set

func (s *GenericsSignVal) Set(value string) error

Set implements the flag.Value interface.

func (GenericsSignVal) Signature

func (gv GenericsSignVal) Signature() string

Signature returns the signature of the generics.

Format:

[T1, T2, T3]

Returns:

  • string: The list of generics.

func (GenericsSignVal) String

func (s GenericsSignVal) String() string

String implements the flag.Value interface.

Format:

[letter1 type1, letter2 type2, ...]

type GoExport

type GoExport int

GoExport is an enum that represents whether a variable is exported or not.

const (
	// NotExported represents a variable that is not exported.
	NotExported GoExport = iota

	// Exported represents a variable that is exported.
	Exported

	// Either represents a variable that is either exported or not exported.
	Either
)

type OutputLocVal

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

OutputLocVal is the value of the output_flag flag.

func NewOutputFlag

func NewOutputFlag(def_value string, required bool) *OutputLocVal

NewOutputFlag sets the flag that specifies the location of the output file.

Parameters:

  • def_value: The default value of the output_flag flag.
  • required: Whether the flag is required or not.

Returns:

  • *OutputLocVal: The new output_flag flag. Never returns nil.

Here are all the possible valid calls to this function:

NewOutputFlag("", false) <-> NewOutputFlag("[no location]", false)
NewOutputFlag("path/to/file.go", false)
NewOutputFlag("", true) <-> NewOutputFlag("path/to/file.go", true)

However, the def_value parameter does not specify the actual default location of the output file. Instead, it is merely used in the "usage" portion of the flag specification in order to give the user more information about the location of the output file. Thus, if no output flag is set, the actual default location of the flag is an empty string.

Documentation:

**Flag: Output File**

This optional flag is used to specify the output file. If not specified, the output will be written to standard output, that is, the file "<type_name>_treenode.go" in the root of the current directory.

func (OutputLocVal) Loc

func (o OutputLocVal) Loc() string

Loc gets the location of the output file.

Returns:

  • string: The location of the output file.

func (*OutputLocVal) Set

func (v *OutputLocVal) Set(loc string) error

Set implements the flag.Value interface.

func (OutputLocVal) String

func (v OutputLocVal) String() string

String implements the flag.Value interface.

type PackageNameSetter

type PackageNameSetter interface {
	// SetPackageName sets the package name for the generated code.
	//
	// Parameters:
	//   - pkg_name: The package name to use for the generated code.
	SetPackageName(pkg_name string)
}

PackageNameSetter is the interface that all generators must implement.

type StructFieldsVal

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

struct_fields_vaò is a struct that represents the fields value.

func NewStructFieldsFlag

func NewStructFieldsFlag(flag_name string, is_required bool, count int, brief string) *StructFieldsVal

NewStructFieldsFlag sets the flag that specifies the fields of the struct to generate the code for.

Parameters:

  • flag_name: The name of the flag.
  • is_required: Whether the flag is required or not.
  • count: The number of fields expected. -1 for unlimited number of fields.
  • brief: A brief description of the flag.

Returns:

  • *StructFieldsVal: The value of the flag.

This function returns nil iff count is 0.

Any negative number will be interpreted as unlimited number of fields. Also, the value 0 will not set the flag.

Documentation:

**Flag: Fields**

The "fields" flag is used to specify the fields that the tree node contains. Because it doesn't make a lot of sense to have a tree node without fields, this flag must be set.

Its argument is specified as a list of key-value pairs where each pair is separated by a comma (",") and a slash ("/") is used to separate the key and the value.

The key indicates the name of the field while the value indicates the type of the field.

For instance, running the following command:

//go:generate treenode -type="TreeNode" -fields=a/int,b/int,name/string

will generate a tree node with the following fields:

type TreeNode struct {
	// Node pointers.

	a int
	b int
	name string
}

It is important to note that spaces are not allowed.

Also, it is possible to specify generics by following the value with the generics between square brackets; like so: "a/MyType[T,C]"

func (StructFieldsVal) Fields

func (s StructFieldsVal) Fields() map[string]string

Fields returns the fields of the struct.

Returns:

  • map[string]string: A map of field names and their types. Never returns nil.

func (StructFieldsVal) Generics

func (s StructFieldsVal) Generics() []rune

Generics returns the letters of the generics.

Returns:

  • []rune: The letters of the generics.

func (StructFieldsVal) MakeAssignmentList

func (s StructFieldsVal) MakeAssignmentList() (map[string]string, error)

MakeAssignmentList makes a string representing a list of assignments.

WARNING: Call this function only if StructFieldsFlag is set.

Parameters:

  • fields: A map of field names and their types.

Returns:

  • string: A string representing the assignments.
  • error: An error if any.

func (StructFieldsVal) MakeParameterList

func (s StructFieldsVal) MakeParameterList() (string, error)

MakeParameterList makes a string representing a list of parameters.

WARNING: Call this function only if StructFieldsFlag is set.

Parameters:

  • fields: A map of field names and their types.

Returns:

  • string: A string representing the parameters.
  • error: An error if any.

func (*StructFieldsVal) Set

func (s *StructFieldsVal) Set(value string) error

Set implements the flag.Value interface.

func (StructFieldsVal) String

func (s StructFieldsVal) String() string

String implements the flag.Value interface.

Format:

"<value1> <type1>, <value2> <type2>, ..."

type TypeListVal

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

TypeListVal is a struct that represents a list of types.

func NewTypeListFlag

func NewTypeListFlag(flag_name string, is_required bool, count int, brief string) *TypeListVal

NewTypeListFlag sets the flag that specifies the fields of the struct to generate the code for.

Parameters:

  • flag_name: The name of the flag.
  • is_required: Whether the flag is required or not.
  • count: The number of fields expected. -1 for unlimited number of fields.
  • brief: A brief description of the flag.

Returns:

  • *TypeListVal: The flag value.

This function returns nil iff count is 0.

Any negative number will be interpreted as unlimited number of fields. Also, the value 0 will not set the flag. If value is nil, it will panic.

Documentation:

**Flag: Types**

The "types" flag is used to specify a list of types that are accepted by the generator.

Its argument is specidied as a list of Go types separated by commas without spaces.

For instance, running the following command:

//go:generate table -name=IntTable -type=int -fields=a/int,b/int,name/string

will generate a tree node with the following fields:

type TreeNode struct {
	// Node pointers.

	a int
	b int
	name string
}

It is important to note that spaces are not allowed.

Also, it is possible to specify generics by following the value with the generics between square brackets; like so: "a/MyType[T,C]"

func (TypeListVal) Generics

func (s TypeListVal) Generics() []rune

Generics returns the generics of the struct.

Returns:

  • []rune: The generics of the struct.

func (*TypeListVal) Set

func (s *TypeListVal) Set(value string) error

Set implements the flag.Value interface.

func (TypeListVal) String

func (s TypeListVal) String() string

String implements the flag.Value interface.

Format:

"<type1>, <type2>, ..."

func (TypeListVal) Type

func (s TypeListVal) Type(idx int) (string, error)

Type returns the type at the given index.

Parameters:

  • idx: The index of the type to return.

Return:

  • string: The type at the given index.
  • error: An error of type *luc.ErrInvalidParameter if the index is out of bounds.

Jump to

Keyboard shortcuts

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