github.com/ipld/go-ipld-prime@v0.21.0/schema/gen/go/genList.go (about) 1 package gengo 2 3 import ( 4 "io" 5 6 "github.com/ipld/go-ipld-prime/schema" 7 "github.com/ipld/go-ipld-prime/schema/gen/go/mixins" 8 ) 9 10 type listGenerator struct { 11 AdjCfg *AdjunctCfg 12 mixins.ListTraits 13 PkgName string 14 Type *schema.TypeList 15 } 16 17 func (listGenerator) IsRepr() bool { return false } // hint used in some generalized templates. 18 19 // --- native content and specializations ---> 20 21 func (g listGenerator) EmitNativeType(w io.Writer) { 22 // Lists are a pretty straightforward struct enclosing a slice. 23 doTemplate(` 24 {{- if Comments -}} 25 // {{ .Type | TypeSymbol }} matches the IPLD Schema type "{{ .Type.Name }}". It has {{ .Kind }} kind. 26 {{- end}} 27 type {{ .Type | TypeSymbol }} = *_{{ .Type | TypeSymbol }} 28 type _{{ .Type | TypeSymbol }} struct { 29 x []_{{ .Type.ValueType | TypeSymbol }}{{if .Type.ValueIsNullable }}__Maybe{{end}} 30 } 31 `, w, g.AdjCfg, g) 32 } 33 34 func (g listGenerator) EmitNativeAccessors(w io.Writer) { 35 // Generate a speciated Lookup as well as LookupMaybe method. 36 // The Lookup method returns nil in case of *either* an out-of-range/absent value or a null value, 37 // and so should only be used if the list type doesn't allow nullable keys or if the caller doesn't care about the difference. 38 // The LookupMaybe method returns a MaybeT type for the list value, 39 // and is needed if the list allows nullable values and the caller wishes to distinguish between null and out-of-range/absent. 40 // (The Lookup method should be preferred for lists that have non-nullable keys, because LookupMaybe may incur additional costs; 41 // boxing something into a maybe when it wasn't already stored that way costs an alloc(!), 42 // and may additionally incur a memcpy if the maybe for the value type doesn't use pointers internally). 43 doTemplate(` 44 func (n *_{{ .Type | TypeSymbol }}) Lookup(idx int64) {{ .Type.ValueType | TypeSymbol }} { 45 if n.Length() <= idx { 46 return nil 47 } 48 v := &n.x[idx] 49 {{- if .Type.ValueIsNullable }} 50 if v.m == schema.Maybe_Null { 51 return nil 52 } 53 return {{ if not (MaybeUsesPtr .Type.ValueType) }}&{{end}}v.v 54 {{- else}} 55 return v 56 {{- end}} 57 } 58 func (n *_{{ .Type | TypeSymbol }}) LookupMaybe(idx int64) Maybe{{ .Type.ValueType | TypeSymbol }} { 59 if n.Length() <= idx { 60 return nil 61 } 62 v := &n.x[idx] 63 {{- if .Type.ValueIsNullable }} 64 return v 65 {{- else}} 66 return &_{{ .Type.ValueType | TypeSymbol }}__Maybe{ 67 m: schema.Maybe_Value, 68 v: {{ if not (MaybeUsesPtr .Type.ValueType) }}*{{end}}v, 69 } 70 {{- end}} 71 } 72 73 var _{{ .Type | TypeSymbol }}__valueAbsent = _{{ .Type.ValueType | TypeSymbol }}__Maybe{m:schema.Maybe_Absent} 74 `, w, g.AdjCfg, g) 75 76 // Generate a speciated iterator. 77 // The main advantage of this over the general datamodel.ListIterator is of course keeping types visible (and concrete, to the compiler's eyes in optimizations, too). 78 // It also elides the error return from the iterator's Next method. (Overreads will result in -1 as an index and nil values; this is both easily avoidable, and unambiguous if you do goof and hit it.) 79 doTemplate(` 80 func (n {{ .Type | TypeSymbol }}) Iterator() *{{ .Type | TypeSymbol }}__Itr { 81 return &{{ .Type | TypeSymbol }}__Itr{n, 0} 82 } 83 84 type {{ .Type | TypeSymbol }}__Itr struct { 85 n {{ .Type | TypeSymbol }} 86 idx int 87 } 88 89 func (itr *{{ .Type | TypeSymbol }}__Itr) Next() (idx int64, v {{if .Type.ValueIsNullable }}Maybe{{end}}{{ .Type.ValueType | TypeSymbol }}) { 90 if itr.idx >= len(itr.n.x) { 91 return -1, nil 92 } 93 idx = int64(itr.idx) 94 v = &itr.n.x[itr.idx] 95 itr.idx++ 96 return 97 } 98 func (itr *{{ .Type | TypeSymbol }}__Itr) Done() bool { 99 return itr.idx >= len(itr.n.x) 100 } 101 102 `, w, g.AdjCfg, g) 103 } 104 105 func (g listGenerator) EmitNativeBuilder(w io.Writer) { 106 // FUTURE: come back to this -- not yet clear what exactly might be most worth emitting here. 107 } 108 109 func (g listGenerator) EmitNativeMaybe(w io.Writer) { 110 emitNativeMaybe(w, g.AdjCfg, g) 111 } 112 113 // --- type info ---> 114 115 func (g listGenerator) EmitTypeConst(w io.Writer) { 116 doTemplate(` 117 // TODO EmitTypeConst 118 `, w, g.AdjCfg, g) 119 } 120 121 // --- TypedNode interface satisfaction ---> 122 123 func (g listGenerator) EmitTypedNodeMethodType(w io.Writer) { 124 doTemplate(` 125 func ({{ .Type | TypeSymbol }}) Type() schema.Type { 126 return nil /*TODO:typelit*/ 127 } 128 `, w, g.AdjCfg, g) 129 } 130 131 func (g listGenerator) EmitTypedNodeMethodRepresentation(w io.Writer) { 132 emitTypicalTypedNodeMethodRepresentation(w, g.AdjCfg, g) 133 } 134 135 // --- Node interface satisfaction ---> 136 137 func (g listGenerator) EmitNodeType(w io.Writer) { 138 // No additional types needed. Methods all attach to the native type. 139 } 140 141 func (g listGenerator) EmitNodeTypeAssertions(w io.Writer) { 142 emitNodeTypeAssertions_typical(w, g.AdjCfg, g) 143 } 144 145 func (g listGenerator) EmitNodeMethodLookupByIndex(w io.Writer) { 146 doTemplate(` 147 func (n {{ .Type | TypeSymbol }}) LookupByIndex(idx int64) (datamodel.Node, error) { 148 if n.Length() <= idx { 149 return nil, datamodel.ErrNotExists{Segment: datamodel.PathSegmentOfInt(idx)} 150 } 151 v := &n.x[idx] 152 {{- if .Type.ValueIsNullable }} 153 if v.m == schema.Maybe_Null { 154 return datamodel.Null, nil 155 } 156 return {{ if not (MaybeUsesPtr .Type.ValueType) }}&{{end}}v.v, nil 157 {{- else}} 158 return v, nil 159 {{- end}} 160 } 161 `, w, g.AdjCfg, g) 162 } 163 164 func (g listGenerator) EmitNodeMethodLookupByNode(w io.Writer) { 165 // LookupByNode will procede by coercing to int64 if it can; or fail; those are really the only options. 166 // REVIEW: how much coercion is done by other types varies quite wildly. so we should figure out if that inconsistency is acceptable, and at least document it if so. 167 doTemplate(` 168 func (n {{ .Type | TypeSymbol }}) LookupByNode(k datamodel.Node) (datamodel.Node, error) { 169 idx, err := k.AsInt() 170 if err != nil { 171 return nil, err 172 } 173 return n.LookupByIndex(idx) 174 } 175 `, w, g.AdjCfg, g) 176 } 177 178 func (g listGenerator) EmitNodeMethodListIterator(w io.Writer) { 179 doTemplate(` 180 func (n {{ .Type | TypeSymbol }}) ListIterator() datamodel.ListIterator { 181 return &_{{ .Type | TypeSymbol }}__ListItr{n, 0} 182 } 183 184 type _{{ .Type | TypeSymbol }}__ListItr struct { 185 n {{ .Type | TypeSymbol }} 186 idx int 187 } 188 189 func (itr *_{{ .Type | TypeSymbol }}__ListItr) Next() (idx int64, v datamodel.Node, _ error) { 190 if itr.idx >= len(itr.n.x) { 191 return -1, nil, datamodel.ErrIteratorOverread{} 192 } 193 idx = int64(itr.idx) 194 x := &itr.n.x[itr.idx] 195 {{- if .Type.ValueIsNullable }} 196 switch x.m { 197 case schema.Maybe_Null: 198 v = datamodel.Null 199 case schema.Maybe_Value: 200 v = {{ if not (MaybeUsesPtr .Type.ValueType) }}&{{end}}x.v 201 } 202 {{- else}} 203 v = x 204 {{- end}} 205 itr.idx++ 206 return 207 } 208 func (itr *_{{ .Type | TypeSymbol }}__ListItr) Done() bool { 209 return itr.idx >= len(itr.n.x) 210 } 211 212 `, w, g.AdjCfg, g) 213 } 214 215 func (g listGenerator) EmitNodeMethodLength(w io.Writer) { 216 doTemplate(` 217 func (n {{ .Type | TypeSymbol }}) Length() int64 { 218 return int64(len(n.x)) 219 } 220 `, w, g.AdjCfg, g) 221 } 222 223 func (g listGenerator) EmitNodeMethodPrototype(w io.Writer) { 224 emitNodeMethodPrototype_typical(w, g.AdjCfg, g) 225 } 226 227 func (g listGenerator) EmitNodePrototypeType(w io.Writer) { 228 emitNodePrototypeType_typical(w, g.AdjCfg, g) 229 } 230 231 // --- NodeBuilder and NodeAssembler ---> 232 233 func (g listGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator { 234 return listBuilderGenerator{ 235 g.AdjCfg, 236 mixins.ListAssemblerTraits{ 237 PkgName: g.PkgName, 238 TypeName: g.TypeName, 239 AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__", 240 }, 241 g.PkgName, 242 g.Type, 243 } 244 } 245 246 type listBuilderGenerator struct { 247 AdjCfg *AdjunctCfg 248 mixins.ListAssemblerTraits 249 PkgName string 250 Type *schema.TypeList 251 } 252 253 func (listBuilderGenerator) IsRepr() bool { return false } // hint used in some generalized templates. 254 255 func (g listBuilderGenerator) EmitNodeBuilderType(w io.Writer) { 256 emitEmitNodeBuilderType_typical(w, g.AdjCfg, g) 257 } 258 func (g listBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) { 259 emitNodeBuilderMethods_typical(w, g.AdjCfg, g) 260 } 261 func (g listBuilderGenerator) EmitNodeAssemblerType(w io.Writer) { 262 // - 'w' is the "**w**ip" pointer. 263 // - 'm' is the **m**aybe which communicates our completeness to the parent if we're a child assembler. 264 // - 'state' is what it says on the tin. this is used for the list state (the broad transitions between null, start-list, and finish are handled by 'm' for consistency with other types). 265 // 266 // - 'cm' is **c**hild **m**aybe and is used for the completion message from children. 267 // It's only present if list values *aren't* allowed to be nullable, since otherwise they have their own per-value maybe slot we can use. 268 // - 'va' is the embedded child value assembler. 269 doTemplate(` 270 type _{{ .Type | TypeSymbol }}__Assembler struct { 271 w *_{{ .Type | TypeSymbol }} 272 m *schema.Maybe 273 state laState 274 275 {{ if not .Type.ValueIsNullable }}cm schema.Maybe{{end}} 276 va _{{ .Type.ValueType | TypeSymbol }}__Assembler 277 } 278 279 func (na *_{{ .Type | TypeSymbol }}__Assembler) reset() { 280 na.state = laState_initial 281 na.va.reset() 282 } 283 `, w, g.AdjCfg, g) 284 } 285 func (g listBuilderGenerator) EmitNodeAssemblerMethodBeginList(w io.Writer) { 286 emitNodeAssemblerMethodBeginList_listoid(w, g.AdjCfg, g) 287 } 288 func (g listBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) { 289 emitNodeAssemblerMethodAssignNull_recursive(w, g.AdjCfg, g) 290 } 291 func (g listBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) { 292 emitNodeAssemblerMethodAssignNode_listoid(w, g.AdjCfg, g) 293 } 294 func (g listBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) { 295 emitNodeAssemblerHelper_listoid_tidyHelper(w, g.AdjCfg, g) 296 emitNodeAssemblerHelper_listoid_listAssemblerMethods(w, g.AdjCfg, g) 297 }