generate.go 7.44 KB
Newer Older
Eric Myhre's avatar
Eric Myhre committed
1 2 3
package gengo

import (
4
	"fmt"
Eric Myhre's avatar
Eric Myhre committed
5 6 7
	"io"
	"os"
	"path/filepath"
8
	"sort"
Eric Myhre's avatar
Eric Myhre committed
9 10 11 12

	"github.com/ipld/go-ipld-prime/schema"
)

13 14 15 16
// Generate takes a typesystem and the adjunct config for codegen,
// and emits generated code in the given path with the given package name.
//
// All of the files produced will match the pattern "ipldsch.*.gen.go".
Eric Myhre's avatar
Eric Myhre committed
17
func Generate(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg) {
18 19 20 21 22 23 24 25
	// Emit fixed bits.
	withFile(filepath.Join(pth, "ipldsch.minima.gen.go"), func(f io.Writer) {
		EmitInternalEnums(pkgName, f)
	})

	// Local helper function for applying generation logic to each type.
	//  We will end up doing this more than once because in this layout, more than one file contains part of the story for each type.
	applyToEachType := func(fn func(tg TypeGenerator, w io.Writer), f io.Writer) {
26 27 28 29 30 31 32 33 34 35
		// Sort the type names so we have a determinisic order; this affects output consistency.
		//  Any stable order would do, but we don't presently have one, so a sort is necessary.
		types := ts.GetTypes()
		keys := make(sortableTypeNames, 0, len(types))
		for tn := range types {
			keys = append(keys, tn)
		}
		sort.Sort(keys)
		for _, tn := range keys {
			switch t2 := types[tn].(type) {
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130
			case *schema.TypeBool:
				fn(NewBoolReprBoolGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeInt:
				fn(NewIntReprIntGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeFloat:
				fn(NewFloatReprFloatGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeString:
				fn(NewStringReprStringGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeBytes:
				fn(NewBytesReprBytesGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeLink:
				fn(NewLinkReprLinkGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeStruct:
				switch t2.RepresentationStrategy().(type) {
				case schema.StructRepresentation_Map:
					fn(NewStructReprMapGenerator(pkgName, t2, adjCfg), f)
				case schema.StructRepresentation_Tuple:
					fn(NewStructReprTupleGenerator(pkgName, t2, adjCfg), f)
				case schema.StructRepresentation_Stringjoin:
					fn(NewStructReprStringjoinGenerator(pkgName, t2, adjCfg), f)
				default:
					panic("unrecognized struct representation strategy")
				}
			case *schema.TypeMap:
				fn(NewMapReprMapGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeList:
				fn(NewListReprListGenerator(pkgName, t2, adjCfg), f)
			case *schema.TypeUnion:
				switch t2.RepresentationStrategy().(type) {
				case schema.UnionRepresentation_Keyed:
					fn(NewUnionReprKeyedGenerator(pkgName, t2, adjCfg), f)
				case schema.UnionRepresentation_Kinded:
					fn(NewUnionReprKindedGenerator(pkgName, t2, adjCfg), f)
				default:
					panic("unrecognized union representation strategy")
				}
			default:
				panic("add more type switches here :)")
			}
		}
	}

	// Emit a file with the type table, and the golang type defns for each type.
	withFile(filepath.Join(pth, "ipldsch.types.gen.go"), func(f io.Writer) {
		// Emit headers, import statements, etc.
		fmt.Fprintf(f, "package %s\n\n", pkgName)
		fmt.Fprintf(f, doNotEditComment+"\n\n")

		// Emit the type table.
		EmitTypeTable(pkgName, ts, adjCfg, f)

		// Emit the type defns matching the schema types.
		fmt.Fprintf(f, "\n// --- type definitions follow ---\n\n")
		applyToEachType(func(tg TypeGenerator, w io.Writer) {
			tg.EmitNativeType(w)
			fmt.Fprintf(f, "\n")
		}, f)

	})

	// Emit a file with all the Node/NodeBuilder/NodeAssembler boilerplate.
	//  Also includes typedefs for representation-level data.
	//  Also includes the MaybeT boilerplate.
	withFile(filepath.Join(pth, "ipldsch.satisfaction.gen.go"), func(f io.Writer) {
		// Emit headers, import statements, etc.
		fmt.Fprintf(f, "package %s\n\n", pkgName)
		fmt.Fprintf(f, doNotEditComment+"\n\n")
		fmt.Fprintf(f, "import (\n")
		fmt.Fprintf(f, "\tipld \"github.com/ipld/go-ipld-prime\"\n")        // referenced everywhere.
		fmt.Fprintf(f, "\t\"github.com/ipld/go-ipld-prime/node/mixins\"\n") // referenced by node implementation guts.
		fmt.Fprintf(f, "\t\"github.com/ipld/go-ipld-prime/schema\"\n")      // referenced by maybes (and surprisingly little else).
		fmt.Fprintf(f, ")\n\n")

		// For each type, we'll emit... everything except the native type, really.
		applyToEachType(func(tg TypeGenerator, w io.Writer) {
			tg.EmitNativeAccessors(w)
			tg.EmitNativeBuilder(w)
			tg.EmitNativeMaybe(w)
			EmitNode(tg, w)
			tg.EmitTypedNodeMethodType(w)
			tg.EmitTypedNodeMethodRepresentation(w)

			nrg := tg.GetRepresentationNodeGen()
			EmitNode(nrg, w)

			fmt.Fprintf(f, "\n")
		}, f)
	})
}

// GenerateSplayed is like Generate, but emits a differnet pattern of files.
// GenerateSplayed emits many more individual files than Generate.
//
// This function should be considered deprecated and may be removed in the future.
func GenerateSplayed(pth string, pkgName string, ts schema.TypeSystem, adjCfg *AdjunctCfg) {
Eric Myhre's avatar
Eric Myhre committed
131 132 133 134 135 136 137 138 139 140
	// Emit fixed bits.
	withFile(filepath.Join(pth, "minima.go"), func(f io.Writer) {
		EmitInternalEnums(pkgName, f)
	})

	// Emit a file for each type.
	for _, typ := range ts.GetTypes() {
		withFile(filepath.Join(pth, "t"+typ.Name().String()+".go"), func(f io.Writer) {
			EmitFileHeader(pkgName, f)
			switch t2 := typ.(type) {
141
			case *schema.TypeBool:
Eric Myhre's avatar
Eric Myhre committed
142
				EmitEntireType(NewBoolReprBoolGenerator(pkgName, t2, adjCfg), f)
143
			case *schema.TypeInt:
Eric Myhre's avatar
Eric Myhre committed
144
				EmitEntireType(NewIntReprIntGenerator(pkgName, t2, adjCfg), f)
145
			case *schema.TypeFloat:
Eric Myhre's avatar
Eric Myhre committed
146
				EmitEntireType(NewFloatReprFloatGenerator(pkgName, t2, adjCfg), f)
147
			case *schema.TypeString:
Eric Myhre's avatar
Eric Myhre committed
148
				EmitEntireType(NewStringReprStringGenerator(pkgName, t2, adjCfg), f)
149
			case *schema.TypeBytes:
Eric Myhre's avatar
Eric Myhre committed
150
				EmitEntireType(NewBytesReprBytesGenerator(pkgName, t2, adjCfg), f)
151
			case *schema.TypeLink:
Eric Myhre's avatar
Eric Myhre committed
152
				EmitEntireType(NewLinkReprLinkGenerator(pkgName, t2, adjCfg), f)
153
			case *schema.TypeStruct:
Eric Myhre's avatar
Eric Myhre committed
154 155 156
				switch t2.RepresentationStrategy().(type) {
				case schema.StructRepresentation_Map:
					EmitEntireType(NewStructReprMapGenerator(pkgName, t2, adjCfg), f)
157 158
				case schema.StructRepresentation_Tuple:
					EmitEntireType(NewStructReprTupleGenerator(pkgName, t2, adjCfg), f)
159 160
				case schema.StructRepresentation_Stringjoin:
					EmitEntireType(NewStructReprStringjoinGenerator(pkgName, t2, adjCfg), f)
Eric Myhre's avatar
Eric Myhre committed
161 162 163
				default:
					panic("unrecognized struct representation strategy")
				}
164
			case *schema.TypeMap:
165
				EmitEntireType(NewMapReprMapGenerator(pkgName, t2, adjCfg), f)
166
			case *schema.TypeList:
Eric Myhre's avatar
Eric Myhre committed
167
				EmitEntireType(NewListReprListGenerator(pkgName, t2, adjCfg), f)
168
			case *schema.TypeUnion:
169 170 171
				switch t2.RepresentationStrategy().(type) {
				case schema.UnionRepresentation_Keyed:
					EmitEntireType(NewUnionReprKeyedGenerator(pkgName, t2, adjCfg), f)
172 173
				case schema.UnionRepresentation_Kinded:
					EmitEntireType(NewUnionReprKindedGenerator(pkgName, t2, adjCfg), f)
174 175 176
				default:
					panic("unrecognized union representation strategy")
				}
Eric Myhre's avatar
Eric Myhre committed
177 178 179 180 181
			default:
				panic("add more type switches here :)")
			}
		})
	}
182 183 184

	// Emit the unified type table.
	withFile(filepath.Join(pth, "typeTable.go"), func(f io.Writer) {
185 186
		fmt.Fprintf(f, "package %s\n\n", pkgName)
		fmt.Fprintf(f, doNotEditComment+"\n\n")
187 188
		EmitTypeTable(pkgName, ts, adjCfg, f)
	})
Eric Myhre's avatar
Eric Myhre committed
189 190 191 192 193 194 195 196 197 198
}

func withFile(filename string, fn func(io.Writer)) {
	f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
	if err != nil {
		panic(err)
	}
	defer f.Close()
	fn(f)
}
199 200 201 202 203 204

type sortableTypeNames []schema.TypeName

func (a sortableTypeNames) Len() int           { return len(a) }
func (a sortableTypeNames) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a sortableTypeNames) Less(i, j int) bool { return a[i] < a[j] }