elements.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  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. // Schema is a list of named types.
  15. type Schema struct {
  16. Types []TypeDef `yaml:"types,omitempty"`
  17. }
  18. // A TypeSpecifier references a particular type in a schema.
  19. type TypeSpecifier struct {
  20. Type TypeRef `yaml:"type,omitempty"`
  21. Schema Schema `yaml:"schema,omitempty"`
  22. }
  23. // TypeDef represents a named type in a schema.
  24. type TypeDef struct {
  25. // Top level types should be named. Every type must have a unique name.
  26. Name string `yaml:"name,omitempty"`
  27. Atom `yaml:"atom,omitempty,inline"`
  28. }
  29. // TypeRef either refers to a named type or declares an inlined type.
  30. type TypeRef struct {
  31. // Either the name or one member of Atom should be set.
  32. NamedType *string `yaml:"namedType,omitempty"`
  33. Inlined Atom `yaml:",inline,omitempty"`
  34. }
  35. // Atom represents the smallest possible pieces of the type system.
  36. type Atom struct {
  37. // Exactly one of the below must be set.
  38. *Scalar `yaml:"scalar,omitempty"`
  39. *Struct `yaml:"struct,omitempty"`
  40. *List `yaml:"list,omitempty"`
  41. *Map `yaml:"map,omitempty"`
  42. *Untyped `yaml:"untyped,omitempty"`
  43. }
  44. // Scalar (AKA "primitive") represents a type which has a single value which is
  45. // either numeric, string, or boolean.
  46. //
  47. // TODO: split numeric into float/int? Something even more fine-grained?
  48. type Scalar string
  49. const (
  50. Numeric = Scalar("numeric")
  51. String = Scalar("string")
  52. Boolean = Scalar("boolean")
  53. )
  54. // ElementRelationship is an enum of the different possible relationships
  55. // between the elements of container types (maps, lists, structs, untyped).
  56. type ElementRelationship string
  57. const (
  58. // Associative only applies to lists (see the documentation there).
  59. Associative = ElementRelationship("associative")
  60. // Atomic makes container types (lists, maps, structs, untyped) behave
  61. // as scalars / leaf fields (which is the default for untyped data).
  62. Atomic = ElementRelationship("atomic")
  63. // Separable means the items of the container type have no particular
  64. // relationship (default behavior for maps and structs).
  65. Separable = ElementRelationship("separable")
  66. )
  67. // Struct represents a type which is composed of a number of different fields.
  68. // Each field has a name and a type.
  69. //
  70. // TODO: in the future, we will add one-of groups (sometimes called unions).
  71. type Struct struct {
  72. // Each struct field appears exactly once in this list. The order in
  73. // this list defines the canonical field ordering.
  74. Fields []StructField `yaml:"fields,omitempty"`
  75. // TODO: Implement unions, either this way or by inlining.
  76. // Unions are groupings of fields with special rules. They may refer to
  77. // one or more fields in the above list. A given field from the above
  78. // list may be referenced in exactly 0 or 1 places in the below list.
  79. // Unions []Union `yaml:"unions,omitempty"`
  80. // ElementRelationship states the relationship between the struct's items.
  81. // * `separable` (or unset) implies that each element is 100% independent.
  82. // * `atomic` implies that all elements depend on each other, and this
  83. // is effectively a scalar / leaf field; it doesn't make sense for
  84. // separate actors to set the elements. Example: an RGB color struct;
  85. // it would never make sense to "own" only one component of the
  86. // color.
  87. // The default behavior for structs is `separable`; it's permitted to
  88. // leave this unset to get the default behavior.
  89. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  90. }
  91. // StructField pairs a field name with a field type.
  92. type StructField struct {
  93. // Name is the field name.
  94. Name string `yaml:"name,omitempty"`
  95. // Type is the field type.
  96. Type TypeRef `yaml:"type,omitempty"`
  97. }
  98. // List represents a type which contains a zero or more elements, all of the
  99. // same subtype. Lists may be either associative: each element is more or less
  100. // independent and could be managed by separate entities in the system; or
  101. // atomic, where the elements are heavily dependent on each other: it is not
  102. // sensible to change one element without considering the ramifications on all
  103. // the other elements.
  104. type List struct {
  105. // ElementType is the type of the list's elements.
  106. ElementType TypeRef `yaml:"elementType,omitempty"`
  107. // ElementRelationship states the relationship between the list's elements
  108. // and must have one of these values:
  109. // * `atomic`: the list is treated as a single entity, like a scalar.
  110. // * `associative`:
  111. // - If the list element is a scalar, the list is treated as a set.
  112. // - If the list element is a struct, the list is treated as a map.
  113. // - The list element must not be a map or a list itself.
  114. // There is no default for this value for lists; all schemas must
  115. // explicitly state the element relationship for all lists.
  116. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  117. // Iff ElementRelationship is `associative`, and the element type is
  118. // struct, then Keys must have non-zero length, and it lists the fields
  119. // of the element's struct type which are to be used as the keys of the
  120. // list.
  121. //
  122. // TODO: change this to "non-atomic struct" above and make the code reflect this.
  123. //
  124. // Each key must refer to a single field name (no nesting, not JSONPath).
  125. Keys []string `yaml:"keys,omitempty"`
  126. }
  127. // Map is a key-value pair. Its default semantics are the same as an
  128. // associative list, but:
  129. // * It is serialized differently:
  130. // map: {"k": {"value": "v"}}
  131. // list: [{"key": "k", "value": "v"}]
  132. // * Keys must be string typed.
  133. // * Keys can't have multiple components.
  134. //
  135. // Although serialized the same, maps are different from structs in that each
  136. // map item must have the same type.
  137. //
  138. // Optionally, maps may be atomic (for example, imagine representing an RGB
  139. // color value--it doesn't make sense to have different actors own the R and G
  140. // values).
  141. type Map struct {
  142. // ElementType is the type of the list's elements.
  143. ElementType TypeRef `yaml:"elementType,omitempty"`
  144. // ElementRelationship states the relationship between the map's items.
  145. // * `separable` implies that each element is 100% independent.
  146. // * `atomic` implies that all elements depend on each other, and this
  147. // is effectively a scalar / leaf field; it doesn't make sense for
  148. // separate actors to set the elements.
  149. // TODO: find a simple example.
  150. // The default behavior for maps is `separable`; it's permitted to
  151. // leave this unset to get the default behavior.
  152. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  153. }
  154. // Untyped represents types that allow arbitrary content. (Think: plugin
  155. // objects.)
  156. type Untyped struct {
  157. // ElementRelationship states the relationship between the items, if
  158. // container-typed data happens to be present here.
  159. // * `atomic` implies that all elements depend on each other, and this
  160. // is effectively a scalar / leaf field; it doesn't make sense for
  161. // separate actors to set the elements.
  162. // TODO: support "guess" (guesses at associative list keys)
  163. // TODO: support "lookup" (calls a lookup function to figure out the
  164. // schema based on the data)
  165. // The default behavior for untyped data is `atomic`; it's permitted to
  166. // leave this unset to get the default behavior.
  167. ElementRelationship ElementRelationship `yaml:"elementRelationship,omitempty"`
  168. }
  169. // FindNamedType is a convenience function that returns the referenced TypeDef,
  170. // if it exists, or (nil, false) if it doesn't.
  171. func (s Schema) FindNamedType(name string) (TypeDef, bool) {
  172. for _, t := range s.Types {
  173. if t.Name == name {
  174. return t, true
  175. }
  176. }
  177. return TypeDef{}, false
  178. }
  179. // Resolve is a convenience function which returns the atom referenced, whether
  180. // it is inline or named. Returns (Atom{}, false) if the type can't be resolved.
  181. //
  182. // This allows callers to not care about the difference between a (possibly
  183. // inlined) reference and a definition.
  184. func (s *Schema) Resolve(tr TypeRef) (Atom, bool) {
  185. if tr.NamedType != nil {
  186. t, ok := s.FindNamedType(*tr.NamedType)
  187. if !ok {
  188. return Atom{}, false
  189. }
  190. return t.Atom, true
  191. }
  192. return tr.Inlined, true
  193. }