github.com/ipld/go-ipld-prime@v0.21.0/schema/gen/go/genpartsCommon.go (about) 1 package gengo 2 3 import ( 4 "io" 5 ) 6 7 /* 8 This file is full of "typical" templates. 9 They may not be used by *every* type and representation, 10 but if they're extracted here, they're at least used by *many*. 11 */ 12 13 // The codegen do-not-edit warning comment. 14 // Follows the pattern in https://golang.org/s/generatedcode / https://github.com/golang/go/issues/13560#issuecomment-288457920 . 15 // Should appear somewhere near the top of every file (though precise order doesn't matter). 16 const doNotEditComment = `// Code generated by go-ipld-prime gengo. DO NOT EDIT.` 17 18 // emitNativeMaybe turns out to be completely agnostic to pretty much everything; 19 // it doesn't vary by kind at all, and has never yet ended up needing specialization. 20 func emitNativeMaybe(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 21 doTemplate(` 22 type _{{ .Type | TypeSymbol }}__Maybe struct { 23 m schema.Maybe 24 v {{if not (MaybeUsesPtr .Type) }}_{{end}}{{ .Type | TypeSymbol }} 25 } 26 type Maybe{{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }}__Maybe 27 28 func (m Maybe{{ .Type | TypeSymbol }}) IsNull() bool { 29 return m.m == schema.Maybe_Null 30 } 31 func (m Maybe{{ .Type | TypeSymbol }}) IsAbsent() bool { 32 return m.m == schema.Maybe_Absent 33 } 34 func (m Maybe{{ .Type | TypeSymbol }}) Exists() bool { 35 return m.m == schema.Maybe_Value 36 } 37 func (m Maybe{{ .Type | TypeSymbol }}) AsNode() datamodel.Node { 38 switch m.m { 39 case schema.Maybe_Absent: 40 return datamodel.Absent 41 case schema.Maybe_Null: 42 return datamodel.Null 43 case schema.Maybe_Value: 44 return {{if not (MaybeUsesPtr .Type) }}&{{end}}m.v 45 default: 46 panic("unreachable") 47 } 48 } 49 func (m Maybe{{ .Type | TypeSymbol }}) Must() {{ .Type | TypeSymbol }} { 50 if !m.Exists() { 51 panic("unbox of a maybe rejected") 52 } 53 return {{if not (MaybeUsesPtr .Type) }}&{{end}}m.v 54 } 55 `, w, adjCfg, data) 56 } 57 58 func emitNativeType_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 59 // Using a struct with a single member is the same size in memory as a typedef, 60 // while also having the advantage of meaning we can block direct casting, 61 // which is desirable because the compiler then ensures our validate methods can't be evaded. 62 doTemplate(` 63 {{- if Comments -}} 64 // {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .Kind }} kind. 65 {{- end}} 66 type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }} 67 type _{{ .Type | TypeSymbol }} struct{ x {{ .Kind | KindPrim }} } 68 `, w, adjCfg, data) 69 } 70 71 func emitNativeAccessors_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 72 // The node interface's `AsFoo` method is almost sufficient... but 73 // this method unboxes without needing to return an error that's statically impossible, 74 // which makes it easier to use in chaining. 75 doTemplate(` 76 func (n {{ .Type | TypeSymbol }}) {{ .Kind.String | title }}() {{ .Kind | KindPrim }} { 77 return n.x 78 } 79 `, w, adjCfg, data) 80 } 81 82 func emitNativeBuilder_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 83 // Generate a single-step construction function -- this is easy to do for a scalar, 84 // and all representations of scalar kind can be expected to have a method like this. 85 // The function is attached to the NodePrototype for convenient namespacing; 86 // it needs no new memory, so it would be inappropriate to attach to the builder or assembler. 87 // FUTURE: should engage validation flow. 88 doTemplate(` 89 func (_{{ .Type | TypeSymbol }}__Prototype) From{{ .Kind.String | title }}(v {{ .Kind | KindPrim }}) ({{ .Type | TypeSymbol }}, error) { 90 n := _{{ .Type | TypeSymbol }}{v} 91 return &n, nil 92 } 93 `, w, adjCfg, data) 94 } 95 96 func emitNodeTypeAssertions_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 97 doTemplate(` 98 var _ datamodel.Node = ({{ .Type | TypeSymbol }})(&_{{ .Type | TypeSymbol }}{}) 99 var _ schema.TypedNode = ({{ .Type | TypeSymbol }})(&_{{ .Type | TypeSymbol }}{}) 100 `, w, adjCfg, data) 101 } 102 103 func emitNodeMethodAsKind_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 104 doTemplate(` 105 func (n {{ .Type | TypeSymbol }}) As{{ .Kind.String | title }}() ({{ .Kind | KindPrim }}, error) { 106 return n.x, nil 107 } 108 `, w, adjCfg, data) 109 } 110 111 func emitNodeMethodPrototype_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 112 doTemplate(` 113 func ({{ if .IsRepr }}_{{end}}{{ .Type | TypeSymbol }}{{ if .IsRepr }}__Repr{{end}}) Prototype() datamodel.NodePrototype { 114 return _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Prototype{} 115 } 116 `, w, adjCfg, data) 117 } 118 119 // nodePrototype doesn't really vary textually at all between types and kinds 120 // because it's just builders and standard resets. 121 func emitNodePrototypeType_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 122 doTemplate(` 123 type _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Prototype struct{} 124 125 func (_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Prototype) NewBuilder() datamodel.NodeBuilder { 126 var nb _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder 127 nb.Reset() 128 return &nb 129 } 130 `, w, adjCfg, data) 131 } 132 133 // emitTypicalTypedNodeMethodRepresentation does... what it says on the tin. 134 // 135 // For most types, the way to get the representation node pointer doesn't 136 // textually depend on either the node implementation details nor what the representation strategy is, 137 // or really much at all for that matter. 138 // It only depends on that they have the same structure, so this cast works. 139 // 140 // Most (all?) types can use this. However, it's here rather in the mixins, for two reasons: 141 // one, it still seems possible to imagine we'll have a type someday for which this pattern won't hold; 142 // and two, mixins are also used in the repr generators, and it wouldn't be all sane for this method to end up also on reprs. 143 func emitTypicalTypedNodeMethodRepresentation(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 144 doTemplate(` 145 func (n {{ .Type | TypeSymbol }}) Representation() datamodel.Node { 146 return (*_{{ .Type | TypeSymbol }}__Repr)(n) 147 } 148 `, w, adjCfg, data) 149 } 150 151 // Turns out basically all builders are just an embed of the corresponding assembler. 152 func emitEmitNodeBuilderType_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 153 doTemplate(` 154 type _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder struct { 155 _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler 156 } 157 `, w, adjCfg, data) 158 } 159 160 // Builder build and reset methods are common even when some parts of the assembler vary. 161 // We count on the zero value of any addntl non-common fields of the assembler being correct. 162 func emitNodeBuilderMethods_typical(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 163 doTemplate(` 164 func (nb *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder) Build() datamodel.Node { 165 if *nb.m != schema.Maybe_Value { 166 panic("invalid state: cannot call Build on an assembler that's not finished") 167 } 168 return nb.w 169 } 170 func (nb *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder) Reset() { 171 var w _{{ .Type | TypeSymbol }} 172 var m schema.Maybe 173 *nb = _{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Builder{_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler{w: &w, m: &m}} 174 } 175 `, w, adjCfg, data) 176 } 177 178 // emitNodeAssemblerType_scalar emits a NodeAssembler that's typical for a scalar. 179 // Types that are recursive tend to have more state and custom stuff, so won't use this 180 // (although the 'm' and 'w' variable names may still be presumed universally). 181 func emitNodeAssemblerType_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 182 doTemplate(` 183 type _{{ .Type | TypeSymbol }}__Assembler struct { 184 w *_{{ .Type | TypeSymbol }} 185 m *schema.Maybe 186 } 187 188 func (na *_{{ .Type | TypeSymbol }}__Assembler) reset() {} 189 `, w, adjCfg, data) 190 } 191 192 func emitNodeAssemblerMethodAssignNull_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 193 doTemplate(` 194 func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssignNull() error { 195 switch *na.m { 196 case allowNull: 197 *na.m = schema.Maybe_Null 198 return nil 199 case schema.Maybe_Absent: 200 return mixins.{{ .Kind.String | title }}Assembler{TypeName: "{{ .PkgName }}.{{ .TypeName }}{{ if .IsRepr }}.Repr{{end}}"}.AssignNull() 201 case schema.Maybe_Value, schema.Maybe_Null: 202 panic("invalid state: cannot assign into assembler that's already finished") 203 } 204 panic("unreachable") 205 } 206 `, w, adjCfg, data) 207 } 208 209 // almost the same as the variant for scalars, but also has to check for midvalue state. 210 func emitNodeAssemblerMethodAssignNull_recursive(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 211 doTemplate(` 212 func (na *_{{ .Type | TypeSymbol }}__{{ if .IsRepr }}Repr{{end}}Assembler) AssignNull() error { 213 switch *na.m { 214 case allowNull: 215 *na.m = schema.Maybe_Null 216 return nil 217 case schema.Maybe_Absent: 218 return mixins.{{ .Kind.String | title }}Assembler{TypeName: "{{ .PkgName }}.{{ .TypeName }}{{ if .IsRepr }}.Repr{{end}}"}.AssignNull() 219 case schema.Maybe_Value, schema.Maybe_Null: 220 panic("invalid state: cannot assign into assembler that's already finished") 221 case midvalue: 222 panic("invalid state: cannot assign null into an assembler that's already begun working on recursive structures!") 223 } 224 panic("unreachable") 225 } 226 `, w, adjCfg, data) 227 } 228 229 // works for the AssignFoo methods for scalar kinds that are just boxing a thing. 230 // There's no equivalent of this at all for recursives -- they're too diverse. 231 func emitNodeAssemblerMethodAssignKind_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 232 // This method contains a branch to support MaybeUsesPtr because new memory may need to be allocated. 233 // This allocation only happens if the 'w' ptr is nil, which means we're being used on a Maybe; 234 // otherwise, the 'w' ptr should already be set, and we fill that memory location without allocating, as usual. 235 doTemplate(` 236 func (na *_{{ .Type | TypeSymbol }}__Assembler) Assign{{ .Kind.String | title }}(v {{ .Kind | KindPrim }}) error { 237 switch *na.m { 238 case schema.Maybe_Value, schema.Maybe_Null: 239 panic("invalid state: cannot assign into assembler that's already finished") 240 } 241 {{- if .Type | MaybeUsesPtr }} 242 if na.w == nil { 243 na.w = &_{{ .Type | TypeSymbol }}{} 244 } 245 {{- end}} 246 na.w.x = v 247 *na.m = schema.Maybe_Value 248 return nil 249 } 250 `, w, adjCfg, data) 251 } 252 253 // leans heavily on the fact all the AsFoo and AssignFoo methods follow a very consistent textual pattern. 254 // FUTURE: may be able to get this to work for recursives, too -- but maps and lists each have very unique bottom thirds of this function. 255 func emitNodeAssemblerMethodAssignNode_scalar(w io.Writer, adjCfg *AdjunctCfg, data interface{}) { 256 // AssignNode goes through three phases: 257 // 1. is it null? Jump over to AssignNull (which may or may not reject it). 258 // 2. is it our own type? Handle specially -- we might be able to do efficient things. 259 // 3. is it the right kind to morph into us? Do so. 260 doTemplate(` 261 func (na *_{{ .Type | TypeSymbol }}__Assembler) AssignNode(v datamodel.Node) error { 262 if v.IsNull() { 263 return na.AssignNull() 264 } 265 if v2, ok := v.(*_{{ .Type | TypeSymbol }}); ok { 266 switch *na.m { 267 case schema.Maybe_Value, schema.Maybe_Null: 268 panic("invalid state: cannot assign into assembler that's already finished") 269 } 270 {{- if .Type | MaybeUsesPtr }} 271 if na.w == nil { 272 na.w = v2 273 *na.m = schema.Maybe_Value 274 return nil 275 } 276 {{- end}} 277 *na.w = *v2 278 *na.m = schema.Maybe_Value 279 return nil 280 } 281 if v2, err := v.As{{ .Kind.String | title }}(); err != nil { 282 return err 283 } else { 284 return na.Assign{{ .Kind.String | title }}(v2) 285 } 286 } 287 `, w, adjCfg, data) 288 }