elements.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. /*
  2. Copyright 2018 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package schema
  14. import "sync"
  15. // Schema is a list of named types.
  16. //
  17. // Schema types are indexed in a map before the first search so this type
  18. // should be considered immutable.
  19. type Schema struct {
  20. Types []TypeDef `yaml:"types,omitempty"`
  21. once sync.Once
  22. m map[string]TypeDef
  23. }
  24. // A TypeSpecifier references a particular type in a schema.
  25. type TypeSpecifier struct {
  26. Type TypeRef `yaml:"type,omitempty"`
  27. Schema Schema `yaml:"schema,omitempty"`
  28. }
  29. // TypeDef represents a named type in a schema.
  30. type TypeDef struct {
  31. // Top level types should be named. Every type must have a unique name.
  32. Name string `yaml:"name,omitempty"`
  33. Atom `yaml:"atom,omitempty,inline"`
  34. }
  35. // TypeRef either refers to a named type or declares an inlined type.
  36. type TypeRef struct {
  37. // Either the name or one member of Atom should be set.
  38. NamedType *string `yaml:"namedType,omitempty"`
  39. Inlined Atom `yaml:",inline,omitempty"`
  40. }
  41. // Atom represents the smallest possible pieces of the type system.
  42. // Each set field in the Atom represents a possible type for the object.
  43. // If none of the fields are set, any object will fail validation against the atom.
  44. type Atom struct {
  45. *Scalar `yaml:"scalar,omitempty"`
  46. *List `yaml:"list,omitempty"`
  47. *Map `yaml:"map,omitempty"`
  48. }
  49. // Scalar (AKA "primitive") represents a type which has a single value which is
  50. // either numeric, string, or boolean.
  51. //
  52. // TODO: split numeric into float/int? Something even more fine-grained?
  53. type Scalar string
  54. const (
  55. Numeric = Scalar("numeric")
  56. String = Scalar("string")
  57. Boolean = Scalar("boolean")
  58. )
  59. // ElementRelationship is an enum of the different possible relationships
  60. // between the elements of container types (maps, lists).
  61. type ElementRelationship string
  62. const (
  63. // Associative only applies to lists (see the documentation there).
  64. Associative = ElementRelationship("associative")
  65. // Atomic makes container types (lists, maps) behave
  66. // as scalars / leaf fields
  67. Atomic = ElementRelationship("atomic")
  68. // Separable means the items of the container type have no particular
  69. // relationship (default behavior for maps).
  70. Separable = ElementRelationship("separable")
  71. )
  72. // Map is a key-value pair. Its default semantics are the same as an
  73. // associative list, but:
  74. // * It is serialized differently:
  75. // map: {"k": {"value": "v"}}
  76. // list: [{"key": "k", "value": "v"}]
  77. // * Keys must be string typed.
  78. // * Keys can't have multiple components.
  79. //
  80. // Optionally, maps may be atomic (for example, imagine representing an RGB
  81. // color value--it doesn't make sense to have different actors own the R and G
  82. // values).
  83. //
  84. // Maps may also represent a type which is composed of a number of different fields.
  85. // Each field has a name and a type.
  86. //
  87. // Fields are indexed in a map before the first search so this type
  88. // should be considered immutable.
  89. type Map struct {
  90. // Each struct field appears exactly once in this list. The order in
  91. // this list defines the canonical field ordering.
  92. Fields []StructField `yaml:"fields,omitempty"`
  93. // A Union is a grouping of fields with special rules. It may refer to
  94. // one or more fields in the above list. A given field from the above
  95. // list may be referenced in exactly 0 or 1 places in the below list.
  96. // One can have multiple unions in the same struct, but the fields can't
  97. // overlap between unions.
  98. Unions []Union `yaml:"unions,omitempty"`
  99. // ElementType is the type of the structs's unknown fields.
  100. ElementType TypeRef `yaml:"elementType,omitempty"`
  101. // ElementRelationship states the relationship between the map's items.
  102. // * `separable` (or unset) implies that each element is 100% independent.
  103. // * `atomic` implies that all elements depend on each other, and this
  104. // is effectively a scalar / leaf field; it doesn't make sense for
  105. // separate actors to set the elements. Example: an RGB color struct;
  106. // it would never make sense to "own" only one component of the
  107. // color.
  108. // The default behavior for maps is `separable`; it's permitted to
  109. // leave this unset to get the default behavior.
  110. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  111. once sync.Once
  112. m map[string]StructField
  113. }
  114. // FindField is a convenience function that returns the referenced StructField,
  115. // if it exists, or (nil, false) if it doesn't.
  116. func (m *Map) FindField(name string) (StructField, bool) {
  117. m.once.Do(func() {
  118. m.m = make(map[string]StructField, len(m.Fields))
  119. for _, field := range m.Fields {
  120. m.m[field.Name] = field
  121. }
  122. })
  123. sf, ok := m.m[name]
  124. return sf, ok
  125. }
  126. // UnionFields are mapping between the fields that are part of the union and
  127. // their discriminated value. The discriminated value has to be set, and
  128. // should not conflict with other discriminated value in the list.
  129. type UnionField struct {
  130. // FieldName is the name of the field that is part of the union. This
  131. // is the serialized form of the field.
  132. FieldName string `yaml:"fieldName"`
  133. // Discriminatorvalue is the value of the discriminator to
  134. // select that field. If the union doesn't have a discriminator,
  135. // this field is ignored.
  136. DiscriminatorValue string `yaml:"discriminatorValue"`
  137. }
  138. // Union, or oneof, means that only one of multiple fields of a structure can be
  139. // set at a time. Setting the discriminator helps clearing oher fields:
  140. // - If discriminator changed to non-nil, and a new field has been added
  141. // that doesn't match, an error is returned,
  142. // - If discriminator hasn't changed and two fields or more are set, an
  143. // error is returned,
  144. // - If discriminator changed to non-nil, all other fields but the
  145. // discriminated one will be cleared,
  146. // - Otherwise, If only one field is left, update discriminator to that value.
  147. type Union struct {
  148. // Discriminator, if present, is the name of the field that
  149. // discriminates fields in the union. The mapping between the value of
  150. // the discriminator and the field is done by using the Fields list
  151. // below.
  152. Discriminator *string `yaml:"discriminator,omitempty"`
  153. // DeduceInvalidDiscriminator indicates if the discriminator
  154. // should be updated automatically based on the fields set. This
  155. // typically defaults to false since we don't want to deduce by
  156. // default (the behavior exists to maintain compatibility on
  157. // existing types and shouldn't be used for new types).
  158. DeduceInvalidDiscriminator bool `yaml:"deduceInvalidDiscriminator,omitempty"`
  159. // This is the list of fields that belong to this union. All the
  160. // fields present in here have to be part of the parent
  161. // structure. Discriminator (if oneOf has one), is NOT included in
  162. // this list. The value for field is how we map the name of the field
  163. // to actual value for discriminator.
  164. Fields []UnionField `yaml:"fields,omitempty"`
  165. }
  166. // StructField pairs a field name with a field type.
  167. type StructField struct {
  168. // Name is the field name.
  169. Name string `yaml:"name,omitempty"`
  170. // Type is the field type.
  171. Type TypeRef `yaml:"type,omitempty"`
  172. }
  173. // List represents a type which contains a zero or more elements, all of the
  174. // same subtype. Lists may be either associative: each element is more or less
  175. // independent and could be managed by separate entities in the system; or
  176. // atomic, where the elements are heavily dependent on each other: it is not
  177. // sensible to change one element without considering the ramifications on all
  178. // the other elements.
  179. type List struct {
  180. // ElementType is the type of the list's elements.
  181. ElementType TypeRef `yaml:"elementType,omitempty"`
  182. // ElementRelationship states the relationship between the list's elements
  183. // and must have one of these values:
  184. // * `atomic`: the list is treated as a single entity, like a scalar.
  185. // * `associative`:
  186. // - If the list element is a scalar, the list is treated as a set.
  187. // - If the list element is a map, the list is treated as a map.
  188. // There is no default for this value for lists; all schemas must
  189. // explicitly state the element relationship for all lists.
  190. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  191. // Iff ElementRelationship is `associative`, and the element type is
  192. // map, then Keys must have non-zero length, and it lists the fields
  193. // of the element's map type which are to be used as the keys of the
  194. // list.
  195. //
  196. // TODO: change this to "non-atomic struct" above and make the code reflect this.
  197. //
  198. // Each key must refer to a single field name (no nesting, not JSONPath).
  199. Keys []string `yaml:"keys,omitempty"`
  200. }
  201. // FindNamedType is a convenience function that returns the referenced TypeDef,
  202. // if it exists, or (nil, false) if it doesn't.
  203. func (s *Schema) FindNamedType(name string) (TypeDef, bool) {
  204. s.once.Do(func() {
  205. s.m = make(map[string]TypeDef, len(s.Types))
  206. for _, t := range s.Types {
  207. s.m[t.Name] = t
  208. }
  209. })
  210. t, ok := s.m[name]
  211. return t, ok
  212. }
  213. // Resolve is a convenience function which returns the atom referenced, whether
  214. // it is inline or named. Returns (Atom{}, false) if the type can't be resolved.
  215. //
  216. // This allows callers to not care about the difference between a (possibly
  217. // inlined) reference and a definition.
  218. func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
  219. if tr.NamedType != nil {
  220. t, ok := s.FindNamedType(*tr.NamedType)
  221. if !ok {
  222. return Atom{}, false
  223. }
  224. return t.Atom, true
  225. }
  226. return tr.Inlined, true
  227. }