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  }