type_struct.go 6.25 KB
Newer Older
1 2 3 4 5
package schema

import (
	"fmt"

tavit ohanian's avatar
tavit ohanian committed
6
	ld "gitlab.dms3.io/ld/go-ld-prime"
tavit ohanian's avatar
tavit ohanian committed
7
	schemadmt "gitlab.dms3.io/ld/go-ld-prime/schema/dmt"
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
)

type TypeStruct struct {
	name TypeName
	dmt  schemadmt.TypeStruct
	ts   *TypeSystem
}

type StructField struct {
	parent *TypeStruct
	name   schemadmt.FieldName
	dmt    schemadmt.StructField
}

type StructRepresentation interface{ _StructRepresentation() }

func (StructRepresentation_Map) _StructRepresentation()         {}
func (StructRepresentation_Tuple) _StructRepresentation()       {}
func (StructRepresentation_Stringpairs) _StructRepresentation() {}
func (StructRepresentation_Stringjoin) _StructRepresentation()  {}
func (StructRepresentation_Listpairs) _StructRepresentation()   {}

type StructRepresentation_Map struct {
	parent *TypeStruct // this one needs a pointer back up to figure out its defaults.
	dmt    schemadmt.StructRepresentation_Map
}
type StructRepresentation_Tuple struct {
	dmt schemadmt.StructRepresentation_Tuple
}
type StructRepresentation_Stringpairs struct {
	dmt schemadmt.StructRepresentation_Stringpairs
}
type StructRepresentation_Stringjoin struct {
	dmt schemadmt.StructRepresentation_Stringjoin
}
type StructRepresentation_Listpairs struct {
	dmt schemadmt.StructRepresentation_Listpairs
}

// -- schema.Type interface satisfaction -->

var _ Type = (*TypeStruct)(nil)

func (t *TypeStruct) _Type() {}

func (t *TypeStruct) TypeSystem() *TypeSystem {
	return t.ts
}

57 58
func (TypeStruct) TypeKind() TypeKind {
	return TypeKind_Struct
59 60 61 62 63 64
}

func (t *TypeStruct) Name() TypeName {
	return t.name
}

tavit ohanian's avatar
tavit ohanian committed
65
func (t TypeStruct) RepresentationBehavior() ld.Kind {
66 67
	switch t.dmt.FieldRepresentation().AsInterface().(type) {
	case schemadmt.StructRepresentation_Map:
tavit ohanian's avatar
tavit ohanian committed
68
		return ld.Kind_Map
69
	case schemadmt.StructRepresentation_Tuple:
tavit ohanian's avatar
tavit ohanian committed
70
		return ld.Kind_List
71
	case schemadmt.StructRepresentation_Stringpairs:
tavit ohanian's avatar
tavit ohanian committed
72
		return ld.Kind_String
73
	case schemadmt.StructRepresentation_Stringjoin:
tavit ohanian's avatar
tavit ohanian committed
74
		return ld.Kind_String
75
	case schemadmt.StructRepresentation_Listpairs:
tavit ohanian's avatar
tavit ohanian committed
76
		return ld.Kind_List
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 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
	default:
		panic("unreachable")
	}
}

// -- specific to TypeStruct -->

// Fields returns a slice of descriptions of the object's fields.
func (t *TypeStruct) Fields() []StructField {
	a := make([]StructField, 0, t.dmt.FieldFields().Length())
	for itr := t.dmt.FieldFields().Iterator(); itr.Done(); {
		k, v := itr.Next()
		a = append(a, StructField{t, k, v})
	}
	return a
}

// Field looks up a StructField by name, or returns nil if no such field.
func (t *TypeStruct) Field(name string) *StructField {
	fndmt, err := schemadmt.Type.FieldName.FromString(name)
	if err != nil {
		panic(fmt.Errorf("invalid fieldname: %w", err))
	}
	fdmt := t.dmt.FieldFields().Lookup(fndmt)
	if fdmt == nil {
		return nil
	}
	return &StructField{t, fndmt, fdmt}
}

// Parent returns the type information that this field describes a part of.
//
// While in many cases, you may know the parent already from context,
// there may still be situations where want to pass around a field and
// not need to continue passing down the parent type with it; this method
// helps your code be less redundant in such a situation.
// (You'll find this useful for looking up any rename directives, for example,
// when holding onto a field, since that requires looking up information from
// the representation strategy, which is a property of the type as a whole.)
func (f *StructField) Parent() *TypeStruct { return f.parent }

// Name returns the string name of this field.  The name is the string that
// will be used as a map key if the structure this field is a member of is
// serialized as a map representation.
func (f *StructField) Name() string { return f.name.String() }

// Type returns the Type of this field's value.  Note the field may
// also be unset if it is either Optional or Nullable.
func (f *StructField) Type() Type { return f.parent.ts.types[f.dmt.FieldType().TypeReference()] }

// IsOptional returns true if the field is allowed to be absent from the object.
// If IsOptional is false, the field may be absent from the serial representation
// of the object entirely.
//
// Note being optional is different than saying the value is permitted to be null!
// A field may be both nullable and optional simultaneously, or either, or neither.
func (f *StructField) IsOptional() bool { return f.dmt.FieldOptional().Bool() }

// IsNullable returns true if the field value is allowed to be null.
//
// If is Nullable is false, note that it's still possible that the field value
// will be absent if the field is Optional!  Being nullable is unrelated to
// whether the field's presence is optional as a whole.
//
// Note that a field may be both nullable and optional simultaneously,
// or either, or neither.
func (f *StructField) IsNullable() bool { return f.dmt.FieldNullable().Bool() }

// IsMaybe returns true if the field value is allowed to be either null or absent.
//
// This is a simple "or" of the two properties,
// but this method is a shorthand that turns out useful often.
func (f *StructField) IsMaybe() bool { return f.IsNullable() || f.IsOptional() }

func (t *TypeStruct) RepresentationStrategy() StructRepresentation {
	switch x := t.dmt.FieldRepresentation().AsInterface().(type) {
	case schemadmt.StructRepresentation_Map:
		return StructRepresentation_Map{t, x}
	case schemadmt.StructRepresentation_Tuple:
		return StructRepresentation_Tuple{x}
	case schemadmt.StructRepresentation_Stringpairs:
		return StructRepresentation_Stringpairs{x}
	case schemadmt.StructRepresentation_Stringjoin:
		return StructRepresentation_Stringjoin{x}
	case schemadmt.StructRepresentation_Listpairs:
		return StructRepresentation_Listpairs{x}
	default:
		panic("unreachable")
	}
}

// GetFieldKey returns the string that should be the key when serializing this field.
// For some fields, it's the same as the field name; for others, a rename directive may provide a different value.
func (r StructRepresentation_Map) GetFieldKey(field StructField) string {
	maybeOverrides := r.dmt.FieldFields()
	if !maybeOverrides.Exists() {
		return field.Name()
	}
	fieldInfo := maybeOverrides.Must().Lookup(field.name)
	if fieldInfo == nil {
		return field.Name()
	}
	maybeRename := fieldInfo.FieldRename()
	if !maybeRename.Exists() {
		return field.Name()
	}
	return maybeRename.Must().String()
}

func (r StructRepresentation_Stringjoin) GetJoinDelim() string {
	return r.dmt.FieldJoin().String()
}