genyaml

package
v0.1.0 Latest Latest
Warning

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

Go to latest
Published: May 3, 2023 License: Apache-2.0 Imports: 17 Imported by: 0

README

Genyaml

Description

genyaml is a simple documentation tool used to marshal YAML from Golang structs. It extracts doc comments from .go sources files and adds them as comment nodes in the YAML output.

Usage

TODOs are ignored (e.g. TODO(clarketm)... or TODO...) if and only if they are on a single line.

type Employee struct {
	// Name of employee
	Name string
	// Age of employee
	Age int
    // TODO(clarketm): change this to a float64
	// Salary of employee
	Salary int
}
# Age of employee
Age: 22

# Name of employee
Name: Jim

# Salary of employee
Salary: 100000

Multiline comments are preserved, albeit tabs are converted to spaces and multiple spaces are compressed into a single line.

type Multiline struct {
	// StringField1 comment
	// second line
	// third line
	StringField1 string `json:"string1"`

	/* StringField2 comment
	second line
	third line
	*/
	StringField2 string `json:"string2"`

	/* StringField3 comment
			second line
			third line
	*/
	StringField3 string `json:"string3"`
}
# StringField1 comment
# second line
# third line
string1: string1

# StringField2 comment
# second line
# third line
string2: string2

# StringField3 comment
# second line
# third line
string3: string3

All subsequent lines and blocks after a --- will be ignored.

type Person struct {
	// Name of person
	// ---
	// The name of the person is both the first and last name separated
	// by a space character
	Name string
}
# Name of person
Name: Frank

Generator instructions prefixed with a + are ignored.

type Dog struct {
	// Gender of dog (male|female)
	// +optional
	Gender string `json:"gender,omitempty"`
	// Weight in pounds of dog
	Weight int `json:"weight,omitempty"`
}
# Gender of dog (male|female)
gender: male

# Weight in pounds of dog
weight: 150

Example

First, assume there is a Go file config.go with the following contents:

NOTE: genyaml reads json tags for maximum portability.

// config.go

package example

type Configuration struct {
	// Plugin comment
	Plugin []Plugin `json:"plugin,omitempty"`
}

type Plugin struct {
	// StringField comment
	StringField string `json:"string,omitempty"`
	// BooleanField comment
	BooleanField bool `json:"boolean,omitempty"`
	// IntegerField comment
	IntegerField int `json:"integer,omitempty"`
}
//...

Next, in a separate example.go file, initialize a Configuration struct and marshal it to commented YAML.

// example.go

package example

// Import genyaml
import "k8s.io/test-infra/pkg/genyaml"

//...

// Initialize a `Configuration` struct:
config := &example.Configuration{
    Plugin: []example.Plugin{
        {
            StringField:  "string",
            BooleanField: true,
            IntegerField: 1,
        },
    },
}

// Initialize a CommentMap instance from the `config.go` source file:
cm, err := genyaml.NewCommentMap(nil, "config.go")

// Generate a commented YAML snippet:
yamlSnippet, err := cm.GenYaml(config)

The doc comments are extracted from the config.go file and attached to the corresponding YAML fields:

fmt.Println(yamlSnippet)
# Plugin comment
plugin:
  - # BooleanField comment
    boolean: true

    # IntegerField comment
    integer: 1

    # StringField comment
    string: string

Limitations / Going Forward

  • Embedded structs must include a tag name (i.e. must not be spread), otherwise the type can not be inferred from the YAML output.
  • Interface types, more specifically concrete types implementing a particular interface, can can not be inferred from the YAML output.
  • Upstream this functionality to go-yaml (or a fork) to leverage custom formatting of YAML and direct reflection on types for resolving embedded structs and interface types across multiple source files.

Documentation

Overview

Package genyaml can generate an example YAML snippet from an initialized struct and decorate it with godoc comments parsed from the AST of a given file.

Example:

cm, err := NewCommentMap("example_config.go")

yamlSnippet, err := cm.GenYaml(&plugins.Configuration{
	Approve: []plugins.Approve{
		{
			Repos: []string{
				"ORGANIZATION",
				"ORGANIZATION/REPOSITORY",
			},
			IssueRequired:       false,
			RequireSelfApproval: new(bool),
			LgtmActsAsApprove:   false,
			IgnoreReviewState:   new(bool),
		},
	},
})

Alternatively, you can also use `PopulateStruct` to recursively fill all pointer fields, slices and maps of a struct via reflection:

yamlSnippet, err := cm.GenYaml(PopulateStruct(&plugins.Configuration{}))

yamlSnippet will be assigned a string containing the following YAML:

# Approve is the configuration for the Approve plugin.
approve:
  - # Repos is either of the form org/repos or just org.
	repos:
	  - ORGANIZATION
	  - ORGANIZATION/REPOSITORY

	# IssueRequired indicates if an associated issue is required for approval in the specified repos.
	issue_required: true

	# RequireSelfApproval requires PR authors to explicitly approve their PRs. Otherwise the plugin assumes the author of the PR approves the changes in the PR.
	require_self_approval: false

	# LgtmActsAsApprove indicates that the lgtm command should be used to indicate approval
	lgtm_acts_as_approve: true

	# IgnoreReviewState causes the approve plugin to ignore the GitHub review state. Otherwise: * an APPROVE github review is equivalent to leaving an \"/approve\" message. * A REQUEST_CHANGES github review is equivalent to leaving an /approve cancel\" message.
	ignore_review_state: false

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func PopulateStruct

func PopulateStruct(in interface{}) interface{}

PopulateStruct will recursively populate a struct via reflection for consumption by genyaml by: * Filling all pointer fields * Filling all slices with a one-element slice and filling that one element * Filling all maps with a one-element map and filling that one element NOTE: PopulateStruct will panic if not fed a pointer. Generally if you care about the stability of the app that runs this code, it is strongly recommended to recover panics: defer func(){if r := recover(); r != nil { fmt.Printf("Recovered panic: %v", r } }(

Types

type Comment

type Comment struct {
	// Type is the underlying type of the identifier associated with the comment.
	Type string
	// IsObj determines if the underlying type is a object type (e.g. struct) or primitive type (e.g. string).
	IsObj bool
	// Doc is a comment string parsed from the AST of a node.
	Doc string
}

Comment is an abstract structure for storing parsed AST comments decorated with contextual information.

type CommentMap

type CommentMap struct {

	// RWMutex is a read/write mutex.
	sync.RWMutex
	// contains filtered or unexported fields
}

Comment is an abstract structure for storing mapped types to comments.

func NewCommentMap

func NewCommentMap(rawFiles map[string][]byte, paths ...string) (*CommentMap, error)

NewCommentMap is the constructor for CommentMap accepting a variadic number of path and raw files contents.

func (*CommentMap) EncodeYaml

func (cm *CommentMap) EncodeYaml(config interface{}, encoder *yaml3.Encoder) error

EncodeYaml encodes a fully commented YAML snippet for a given plugin configuration using the given encoder.

func (*CommentMap) GenYaml

func (cm *CommentMap) GenYaml(config interface{}) (string, error)

GenYaml generates a fully commented YAML snippet for a given plugin configuration.

func (*CommentMap) PrintComments

func (cm *CommentMap) PrintComments()

PrintComments pretty prints comments.

Jump to

Keyboard shortcuts

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