types.lua 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. local path = (...):gsub('%.[^%.]+$', '')
  2. local util = require(path .. '.util')
  3. local types = {}
  4. function types.nonNull(kind)
  5. assert(kind, 'Must provide a type')
  6. return {
  7. __type = 'NonNull',
  8. ofType = kind
  9. }
  10. end
  11. function types.list(kind)
  12. assert(kind, 'Must provide a type')
  13. return {
  14. __type = 'List',
  15. ofType = kind
  16. }
  17. end
  18. function types.scalar(config)
  19. assert(type(config.name) == 'string', 'type name must be provided as a string')
  20. assert(type(config.serialize) == 'function', 'serialize must be a function')
  21. if config.parseValue or config.parseLiteral then
  22. assert(
  23. type(config.parseValue) == 'function' and type(config.parseLiteral) == 'function',
  24. 'must provide both parseValue and parseLiteral to scalar type'
  25. )
  26. end
  27. local instance = {
  28. __type = 'Scalar',
  29. name = config.name,
  30. description = config.description,
  31. serialize = config.serialize,
  32. parseValue = config.parseValue,
  33. parseLiteral = config.parseLiteral
  34. }
  35. instance.nonNull = types.nonNull(instance)
  36. return instance
  37. end
  38. function types.object(config)
  39. assert(type(config.name) == 'string', 'type name must be provided as a string')
  40. if config.isTypeOf then
  41. assert(type(config.isTypeOf) == 'function', 'must provide isTypeOf as a function')
  42. end
  43. local instance = {
  44. __type = 'Object',
  45. name = config.name,
  46. isTypeOf = config.isTypeOf,
  47. fields = type(config.fields) == 'function' and util.compose(util.bind1(initFields, 'Object'), config.fields) or initFields('Object', config.fields),
  48. interfaces = config.interfaces
  49. }
  50. instance.nonNull = types.nonNull(instance)
  51. return instance
  52. end
  53. function types.interface(config)
  54. assert(type(config.name) == 'string', 'type name must be provided as a string')
  55. assert(type(config.fields) == 'table', 'fields table must be provided')
  56. if config.resolveType then
  57. assert(type(config.resolveType) == 'function', 'must provide resolveType as a function')
  58. end
  59. local instance = {
  60. __type = 'Interface',
  61. name = config.name,
  62. description = config.description,
  63. fields = type(config.fields) == 'function' and util.compose(util.bind1(initFields, 'Interface'), config.fields) or initFields('Interface', config.fields),
  64. resolveType = config.resolveType
  65. }
  66. instance.nonNull = types.nonNull(instance)
  67. return instance
  68. end
  69. function initFields(kind, flds)
  70. assert(type(flds) == 'table', 'fields table must be provided')
  71. local fields = {}
  72. for fieldName, field in pairs(flds) do
  73. field = field.__type and { kind = field } or field
  74. fields[fieldName] = {
  75. name = fieldName,
  76. kind = field.kind,
  77. arguments = field.arguments or {},
  78. resolve = kind == 'Object' and field.resolve or nil
  79. }
  80. end
  81. return fields
  82. end
  83. function types.enum(config)
  84. assert(type(config.name) == 'string', 'type name must be provided as a string')
  85. assert(type(config.values) == 'table', 'values table must be provided')
  86. local instance = {
  87. __type = 'Enum',
  88. name = config.name,
  89. description = config.description,
  90. values = config.values
  91. }
  92. instance.nonNull = types.nonNull(instance)
  93. return instance
  94. end
  95. function types.union(config)
  96. assert(type(config.name) == 'string', 'type name must be provided as a string')
  97. assert(type(config.types) == 'table', 'types table must be provided')
  98. local instance = {
  99. __type = 'Union',
  100. name = config.name,
  101. types = config.types
  102. }
  103. instance.nonNull = types.nonNull(instance)
  104. return instance
  105. end
  106. function types.inputObject(config)
  107. assert(type(config.name) == 'string', 'type name must be provided as a string')
  108. local fields = {}
  109. for fieldName, field in pairs(config.fields) do
  110. field = field.__type and { kind = field } or field
  111. fields[fieldName] = {
  112. name = fieldName,
  113. kind = field.kind
  114. }
  115. end
  116. local instance = {
  117. __type = 'InputObject',
  118. name = config.name,
  119. description = config.description,
  120. fields = fields
  121. }
  122. return instance
  123. end
  124. local coerceInt = function(value)
  125. value = tonumber(value)
  126. if not value then return end
  127. if value == value and value < 2 ^ 32 and value >= -2 ^ 32 then
  128. return value < 0 and math.ceil(value) or math.floor(value)
  129. end
  130. end
  131. types.int = types.scalar({
  132. name = 'Int',
  133. serialize = coerceInt,
  134. parseValue = coerceInt,
  135. parseLiteral = function(node)
  136. if node.kind == 'int' then
  137. return coerceInt(node.value)
  138. end
  139. end
  140. })
  141. types.float = types.scalar({
  142. name = 'Float',
  143. serialize = tonumber,
  144. parseValue = tonumber,
  145. parseLiteral = function(node)
  146. if node.kind == 'float' or node.kind == 'int' then
  147. return tonumber(node.value)
  148. end
  149. end
  150. })
  151. types.string = types.scalar({
  152. name = 'String',
  153. serialize = tostring,
  154. parseValue = tostring,
  155. parseLiteral = function(node)
  156. if node.kind == 'string' then
  157. return node.value
  158. end
  159. end
  160. })
  161. local function toboolean(x)
  162. return (x and x ~= 'false') and true or false
  163. end
  164. types.boolean = types.scalar({
  165. name = 'Boolean',
  166. serialize = toboolean,
  167. parseValue = toboolean,
  168. parseLiteral = function(node)
  169. if node.kind == 'boolean' then
  170. return toboolean(node.value)
  171. else
  172. return nil
  173. end
  174. end
  175. })
  176. types.id = types.scalar({
  177. name = 'ID',
  178. serialize = tostring,
  179. parseValue = tostring,
  180. parseLiteral = function(node)
  181. return node.kind == 'string' or node.kind == 'int' and node.value or nil
  182. end
  183. })
  184. function types.directive(config)
  185. assert(type(config.name) == 'string', 'type name must be provided as a string')
  186. local instance = {
  187. __type = 'Directive',
  188. name = config.name,
  189. description = config.description,
  190. arguments = config.arguments,
  191. onOperation = config.onOperation or false,
  192. onFragment = config.onOperation or false,
  193. onField = config.onField or false
  194. }
  195. return instance
  196. end
  197. types.include = types.directive({
  198. name = 'include',
  199. arguments = {
  200. ['if'] = types.boolean.nonNull
  201. },
  202. onOperation = false,
  203. onFragment = true,
  204. onField = true
  205. })
  206. types.skip = types.directive({
  207. name = 'skip',
  208. arguments = {
  209. ['if'] = types.boolean.nonNull
  210. },
  211. onOperation = false,
  212. onFragment = true,
  213. onField = true
  214. })
  215. return types