github.com/ipld/go-ipld-prime@v0.21.0/schema/gen/go/genStructReprStringjoin.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  var _ TypeGenerator = &structReprStringjoinGenerator{}
    11  
    12  func NewStructReprStringjoinGenerator(pkgName string, typ *schema.TypeStruct, adjCfg *AdjunctCfg) TypeGenerator {
    13  	return structReprStringjoinGenerator{
    14  		structGenerator{
    15  			adjCfg,
    16  			mixins.MapTraits{
    17  				PkgName:    pkgName,
    18  				TypeName:   string(typ.Name()),
    19  				TypeSymbol: adjCfg.TypeSymbol(typ),
    20  			},
    21  			pkgName,
    22  			typ,
    23  		},
    24  	}
    25  }
    26  
    27  type structReprStringjoinGenerator struct {
    28  	structGenerator
    29  }
    30  
    31  func (g structReprStringjoinGenerator) GetRepresentationNodeGen() NodeGenerator {
    32  	return structReprStringjoinReprGenerator{
    33  		g.AdjCfg,
    34  		mixins.StringTraits{
    35  			PkgName:    g.PkgName,
    36  			TypeName:   string(g.Type.Name()) + ".Repr",
    37  			TypeSymbol: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr",
    38  		},
    39  		g.PkgName,
    40  		g.Type,
    41  	}
    42  }
    43  
    44  type structReprStringjoinReprGenerator struct {
    45  	AdjCfg *AdjunctCfg
    46  	mixins.StringTraits
    47  	PkgName string
    48  	Type    *schema.TypeStruct
    49  }
    50  
    51  func (structReprStringjoinReprGenerator) IsRepr() bool { return true } // hint used in some generalized templates.
    52  
    53  func (g structReprStringjoinReprGenerator) EmitNodeType(w io.Writer) {
    54  	// The type is structurally the same, but will have a different set of methods.
    55  	doTemplate(`
    56  		type _{{ .Type | TypeSymbol }}__Repr _{{ .Type | TypeSymbol }}
    57  	`, w, g.AdjCfg, g)
    58  }
    59  
    60  func (g structReprStringjoinReprGenerator) EmitNodeTypeAssertions(w io.Writer) {
    61  	doTemplate(`
    62  		var _ datamodel.Node = &_{{ .Type | TypeSymbol }}__Repr{}
    63  	`, w, g.AdjCfg, g)
    64  }
    65  
    66  func (g structReprStringjoinReprGenerator) EmitNodeMethodAsString(w io.Writer) {
    67  	// Prerequisites:
    68  	//  - every field must be a string, or have string representation.
    69  	//    - this should've been checked when compiling the type system info.
    70  	//    - we're willing to imply a base-10 atoi/itoa for ints (but it's not currently supported).
    71  	//  - there are NO sanity checks that your value doesn't contain the delimiter
    72  	//    - you need to do this in validation hooks or some other way
    73  	//  - optional or nullable fields are not supported with this representation strategy.
    74  	//    - this should've been checked when compiling the type system info.
    75  	//    - if support for this is added in the future, you can bet all optionals
    76  	//      will be required to be *either* in a row at the start, or in a row at the end.
    77  	//      (a 'direction' property might also be needed, so behavior is defined if every field is optional.)
    78  	//
    79  	// A speciated String method is also generated here.
    80  	//  (Organization questionable: if this was at type level, it'd be in the 'EmitNativeAccessors' block,
    81  	//   but we don't have that in the NodeGenerator interface so we don't have it here.  Maybe that's a mistake.)
    82  	//
    83  	// A String method is *also* generated on the type-level node.
    84  	//  This might be worth consistency review...
    85  	//  It's a practical necessity in areas like stringifying for key error messages if used in map keys, for example.
    86  	doTemplate(`
    87  		func (n *_{{ .Type | TypeSymbol }}__Repr) AsString() (string, error) {
    88  			return n.String(), nil
    89  		}
    90  		func (n *_{{ .Type | TypeSymbol }}__Repr) String() string {
    91  			return {{ "" }}
    92  			{{- $type := .Type -}} {{- /* ranging modifies dot, unhelpfully */ -}}
    93  			{{- range $i, $field := .Type.Fields }}
    94  			{{- if $i }} + "{{ $type.RepresentationStrategy.GetDelim }}" + {{end -}}
    95  			(*_{{ $field.Type | TypeSymbol }}__Repr)(&n.{{ $field | FieldSymbolLower }}).String()
    96  			{{- end}}
    97  		}
    98  		func (n {{ .Type | TypeSymbol }}) String() string {
    99  			return (*_{{ .Type | TypeSymbol }}__Repr)(n).String()
   100  		}
   101  	`, w, g.AdjCfg, g)
   102  }
   103  
   104  func (g structReprStringjoinReprGenerator) EmitNodeMethodPrototype(w io.Writer) {
   105  	emitNodeMethodPrototype_typical(w, g.AdjCfg, g)
   106  }
   107  
   108  func (g structReprStringjoinReprGenerator) EmitNodePrototypeType(w io.Writer) {
   109  	emitNodePrototypeType_typical(w, g.AdjCfg, g)
   110  }
   111  
   112  // --- NodeBuilder and NodeAssembler --->
   113  
   114  func (g structReprStringjoinReprGenerator) GetNodeBuilderGenerator() NodeBuilderGenerator {
   115  	return structReprStringjoinReprBuilderGenerator{
   116  		g.AdjCfg,
   117  		mixins.StringAssemblerTraits{
   118  			PkgName:       g.PkgName,
   119  			TypeName:      g.TypeName,
   120  			AppliedPrefix: "_" + g.AdjCfg.TypeSymbol(g.Type) + "__Repr",
   121  		},
   122  		g.PkgName,
   123  		g.Type,
   124  	}
   125  }
   126  
   127  type structReprStringjoinReprBuilderGenerator struct {
   128  	AdjCfg *AdjunctCfg
   129  	mixins.StringAssemblerTraits
   130  	PkgName string
   131  	Type    *schema.TypeStruct
   132  }
   133  
   134  func (structReprStringjoinReprBuilderGenerator) IsRepr() bool { return true } // hint used in some generalized templates.
   135  
   136  func (g structReprStringjoinReprBuilderGenerator) EmitNodeBuilderType(w io.Writer) {
   137  	emitEmitNodeBuilderType_typical(w, g.AdjCfg, g)
   138  }
   139  func (g structReprStringjoinReprBuilderGenerator) EmitNodeBuilderMethods(w io.Writer) {
   140  	emitNodeBuilderMethods_typical(w, g.AdjCfg, g)
   141  
   142  	// Generate a single-step construction function -- this is easy to do for a scalar,
   143  	//  and all representations of scalar kind can be expected to have a method like this.
   144  	// The function is attached to the NodePrototype for convenient namespacing;
   145  	//  it needs no new memory, so it would be inappropriate to attach to the builder or assembler.
   146  	// The function is directly used internally by anything else that might involve recursive destructuring on the same scalar kind
   147  	//  (for example, structs using stringjoin strategies that have one of this type as a field, etc).
   148  	// Since we're a representation of scalar kind, and can recurse,
   149  	//  we ourselves presume this plain construction method must also exist for all our members.
   150  	// REVIEW: We could make an immut-safe verion of this and export it on the NodePrototype too, as `FromString(string)`.
   151  	// FUTURE: should engage validation flow.
   152  	doTemplate(`
   153  		func (_{{ .Type | TypeSymbol }}__ReprPrototype) fromString(w *_{{ .Type | TypeSymbol }}, v string) error {
   154  			ss, err := mixins.SplitExact(v, "{{ .Type.RepresentationStrategy.GetDelim }}", {{ len .Type.Fields }})
   155  			if err != nil {
   156  				return schema.ErrUnmatchable{TypeName:"{{ .PkgName }}.{{ .Type.Name }}.Repr", Reason: err}
   157  			}
   158  			{{- $dot := . -}} {{- /* ranging modifies dot, unhelpfully */ -}}
   159  			{{- range $i, $field := .Type.Fields }}
   160  			if err := (_{{ $field.Type | TypeSymbol }}__ReprPrototype{}).fromString(&w.{{ $field | FieldSymbolLower }}, ss[{{ $i }}]); err != nil {
   161  				return schema.ErrUnmatchable{TypeName:"{{ $dot.PkgName }}.{{ $dot.Type.Name }}.Repr", Reason: err}
   162  			}
   163  			{{- end}}
   164  			return nil
   165  		}
   166  	`, w, g.AdjCfg, g)
   167  }
   168  func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerType(w io.Writer) {
   169  	doTemplate(`
   170  		type _{{ .Type | TypeSymbol }}__ReprAssembler struct {
   171  			w *_{{ .Type | TypeSymbol }}
   172  			m *schema.Maybe
   173  		}
   174  
   175  		func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) reset() {}
   176  	`, w, g.AdjCfg, g)
   177  }
   178  func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerMethodAssignNull(w io.Writer) {
   179  	emitNodeAssemblerMethodAssignNull_scalar(w, g.AdjCfg, g)
   180  }
   181  func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerMethodAssignString(w io.Writer) {
   182  	// This method contains a branch to support MaybeUsesPtr because new memory may need to be allocated.
   183  	//  This allocation only happens if the 'w' ptr is nil, which means we're being used on a Maybe;
   184  	//  otherwise, the 'w' ptr should already be set, and we fill that memory location without allocating, as usual.
   185  	doTemplate(`
   186  		func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) AssignString(v string) error {
   187  			switch *na.m {
   188  			case schema.Maybe_Value, schema.Maybe_Null:
   189  				panic("invalid state: cannot assign into assembler that's already finished")
   190  			}
   191  			{{- if .Type | MaybeUsesPtr }}
   192  			if na.w == nil {
   193  				na.w = &_{{ .Type | TypeSymbol }}{}
   194  			}
   195  			{{- end}}
   196  			if err := (_{{ .Type | TypeSymbol }}__ReprPrototype{}).fromString(na.w, v); err != nil {
   197  				return err
   198  			}
   199  			*na.m = schema.Maybe_Value
   200  			return nil
   201  		}
   202  	`, w, g.AdjCfg, g)
   203  }
   204  
   205  func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerMethodAssignNode(w io.Writer) {
   206  	// AssignNode goes through three phases:
   207  	// 1. is it null?  Jump over to AssignNull (which may or may not reject it).
   208  	// 2. is it our own type?  Handle specially -- we might be able to do efficient things.
   209  	// 3. is it the right kind to morph into us?  Do so.
   210  	doTemplate(`
   211  		func (na *_{{ .Type | TypeSymbol }}__ReprAssembler) AssignNode(v datamodel.Node) error {
   212  			if v.IsNull() {
   213  				return na.AssignNull()
   214  			}
   215  			if v2, ok := v.(*_{{ .Type | TypeSymbol }}); ok {
   216  				switch *na.m {
   217  				case schema.Maybe_Value, schema.Maybe_Null:
   218  					panic("invalid state: cannot assign into assembler that's already finished")
   219  				}
   220  				{{- if .Type | MaybeUsesPtr }}
   221  				if na.w == nil {
   222  					na.w = v2
   223  					*na.m = schema.Maybe_Value
   224  					return nil
   225  				}
   226  				{{- end}}
   227  				*na.w = *v2
   228  				*na.m = schema.Maybe_Value
   229  				return nil
   230  			}
   231  			if v2, err := v.AsString(); err != nil {
   232  				return err
   233  			} else {
   234  				return na.AssignString(v2)
   235  			}
   236  		}
   237  	`, w, g.AdjCfg, g)
   238  }
   239  func (g structReprStringjoinReprBuilderGenerator) EmitNodeAssemblerOtherBits(w io.Writer) {
   240  	// None for this.
   241  }