types.lua 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277
  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 fields
  44. if type(config.fields) == 'function' then
  45. fields = util.compose(util.bind1(initFields, 'Object'), config.fields)
  46. else
  47. fields = initFields('Object', config.fields)
  48. end
  49. local instance = {
  50. __type = 'Object',
  51. name = config.name,
  52. isTypeOf = config.isTypeOf,
  53. fields = fields,
  54. interfaces = config.interfaces
  55. }
  56. instance.nonNull = types.nonNull(instance)
  57. return instance
  58. end
  59. function types.interface(config)
  60. assert(type(config.name) == 'string', 'type name must be provided as a string')
  61. assert(type(config.fields) == 'table', 'fields table must be provided')
  62. if config.resolveType then
  63. assert(type(config.resolveType) == 'function', 'must provide resolveType as a function')
  64. end
  65. local fields
  66. if type(config.fields) == 'function' then
  67. fields = util.compose(util.bind1(initFields, 'Interface'), config.fields)
  68. else
  69. fields = initFields('Interface', config.fields)
  70. end
  71. local instance = {
  72. __type = 'Interface',
  73. name = config.name,
  74. description = config.description,
  75. fields = fields,
  76. resolveType = config.resolveType
  77. }
  78. instance.nonNull = types.nonNull(instance)
  79. return instance
  80. end
  81. function initFields(kind, fields)
  82. assert(type(fields) == 'table', 'fields table must be provided')
  83. local result = {}
  84. for fieldName, field in pairs(fields) do
  85. field = field.__type and { kind = field } or field
  86. result[fieldName] = {
  87. name = fieldName,
  88. kind = field.kind,
  89. arguments = field.arguments or {},
  90. resolve = kind == 'Object' and field.resolve or nil
  91. }
  92. end
  93. return result
  94. end
  95. function types.enum(config)
  96. assert(type(config.name) == 'string', 'type name must be provided as a string')
  97. assert(type(config.values) == 'table', 'values table must be provided')
  98. local instance = {
  99. __type = 'Enum',
  100. name = config.name,
  101. description = config.description,
  102. values = config.values
  103. }
  104. instance.nonNull = types.nonNull(instance)
  105. return instance
  106. end
  107. function types.union(config)
  108. assert(type(config.name) == 'string', 'type name must be provided as a string')
  109. assert(type(config.types) == 'table', 'types table must be provided')
  110. local instance = {
  111. __type = 'Union',
  112. name = config.name,
  113. types = config.types
  114. }
  115. instance.nonNull = types.nonNull(instance)
  116. return instance
  117. end
  118. function types.inputObject(config)
  119. assert(type(config.name) == 'string', 'type name must be provided as a string')
  120. local fields = {}
  121. for fieldName, field in pairs(config.fields) do
  122. field = field.__type and { kind = field } or field
  123. fields[fieldName] = {
  124. name = fieldName,
  125. kind = field.kind
  126. }
  127. end
  128. local instance = {
  129. __type = 'InputObject',
  130. name = config.name,
  131. description = config.description,
  132. fields = fields
  133. }
  134. return instance
  135. end
  136. local coerceInt = function(value)
  137. value = tonumber(value)
  138. if not value then return end
  139. if value == value and value < 2 ^ 32 and value >= -2 ^ 32 then
  140. return value < 0 and math.ceil(value) or math.floor(value)
  141. end
  142. end
  143. types.int = types.scalar({
  144. name = 'Int',
  145. serialize = coerceInt,
  146. parseValue = coerceInt,
  147. parseLiteral = function(node)
  148. if node.kind == 'int' then
  149. return coerceInt(node.value)
  150. end
  151. end
  152. })
  153. types.float = types.scalar({
  154. name = 'Float',
  155. serialize = tonumber,
  156. parseValue = tonumber,
  157. parseLiteral = function(node)
  158. if node.kind == 'float' or node.kind == 'int' then
  159. return tonumber(node.value)
  160. end
  161. end
  162. })
  163. types.string = types.scalar({
  164. name = 'String',
  165. serialize = tostring,
  166. parseValue = tostring,
  167. parseLiteral = function(node)
  168. if node.kind == 'string' then
  169. return node.value
  170. end
  171. end
  172. })
  173. local function toboolean(x)
  174. return (x and x ~= 'false') and true or false
  175. end
  176. types.boolean = types.scalar({
  177. name = 'Boolean',
  178. serialize = toboolean,
  179. parseValue = toboolean,
  180. parseLiteral = function(node)
  181. if node.kind == 'boolean' then
  182. return toboolean(node.value)
  183. else
  184. return nil
  185. end
  186. end
  187. })
  188. types.id = types.scalar({
  189. name = 'ID',
  190. serialize = tostring,
  191. parseValue = tostring,
  192. parseLiteral = function(node)
  193. return node.kind == 'string' or node.kind == 'int' and node.value or nil
  194. end
  195. })
  196. function types.directive(config)
  197. assert(type(config.name) == 'string', 'type name must be provided as a string')
  198. local instance = {
  199. __type = 'Directive',
  200. name = config.name,
  201. description = config.description,
  202. arguments = config.arguments,
  203. onOperation = config.onOperation or false,
  204. onFragment = config.onOperation or false,
  205. onField = config.onField or false
  206. }
  207. return instance
  208. end
  209. types.include = types.directive({
  210. name = 'include',
  211. arguments = {
  212. ['if'] = types.boolean.nonNull
  213. },
  214. onOperation = false,
  215. onFragment = true,
  216. onField = true
  217. })
  218. types.skip = types.directive({
  219. name = 'skip',
  220. arguments = {
  221. ['if'] = types.boolean.nonNull
  222. },
  223. onOperation = false,
  224. onFragment = true,
  225. onField = true
  226. })
  227. return types