transform.ex (4902B)
1 defmodule Absinthe.Blueprint.Transform do 2 @moduledoc false 3 4 alias Absinthe.Blueprint 5 6 @doc """ 7 Apply `fun` to a node, then walk to its children and do the same 8 """ 9 @spec prewalk( 10 Blueprint.node_t(), 11 (Blueprint.node_t() -> Blueprint.node_t() | {:halt, Blueprint.node_t()}) 12 ) :: Blueprint.node_t() 13 def prewalk(node, fun) when is_function(fun, 1) do 14 {node, _} = 15 prewalk(node, nil, fn x, nil -> 16 case fun.(x) do 17 {:halt, x} -> {:halt, x, nil} 18 x -> {x, nil} 19 end 20 end) 21 22 node 23 end 24 25 @doc """ 26 Same as `prewalk/2` but takes and returns an accumulator 27 28 The supplied function must be arity 2. 29 """ 30 @spec prewalk( 31 Blueprint.node_t(), 32 acc, 33 (Blueprint.node_t(), acc -> 34 {Blueprint.node_t(), acc} | {:halt, Blueprint.node_t(), acc}) 35 ) :: {Blueprint.node_t(), acc} 36 when acc: var 37 def prewalk(node, acc, fun) when is_function(fun, 2) do 38 walk(node, acc, fun, &pass/2) 39 end 40 41 @doc """ 42 Apply `fun` to all children of a node, then apply `fun` to node 43 """ 44 @spec postwalk(Blueprint.node_t(), (Blueprint.node_t() -> Blueprint.node_t())) :: 45 Blueprint.node_t() 46 def postwalk(node, fun) when is_function(fun, 1) do 47 {node, _} = postwalk(node, nil, fn x, nil -> {fun.(x), nil} end) 48 node 49 end 50 51 @doc """ 52 Same as `postwalk/2` but takes and returns an accumulator 53 """ 54 @spec postwalk(Blueprint.node_t(), acc, (Blueprint.node_t(), acc -> {Blueprint.node_t(), acc})) :: 55 {Blueprint.node_t(), acc} 56 when acc: var 57 def postwalk(node, acc, fun) when is_function(fun, 2) do 58 walk(node, acc, &pass/2, fun) 59 end 60 61 defp pass(x, acc), do: {x, acc} 62 63 nodes_with_children = %{ 64 Blueprint => [:fragments, :operations, :schema_definitions, :directives], 65 Blueprint.Directive => [:arguments], 66 Blueprint.Document.Field => [:selections, :arguments, :directives], 67 Blueprint.Document.Operation => [:selections, :variable_definitions, :directives], 68 Blueprint.TypeReference.List => [:of_type], 69 Blueprint.TypeReference.NonNull => [:of_type], 70 Blueprint.Document.Fragment.Inline => [:selections, :directives], 71 Blueprint.Document.Fragment.Named => [:selections, :directives], 72 Blueprint.Document.Fragment.Spread => [:directives], 73 Blueprint.Document.VariableDefinition => [:type, :default_value, :directives], 74 Blueprint.Input.Argument => [:input_value], 75 Blueprint.Input.Field => [:input_value], 76 Blueprint.Input.Object => [:fields], 77 Blueprint.Input.List => [:items], 78 Blueprint.Input.RawValue => [:content], 79 Blueprint.Input.Value => [:normalized], 80 Blueprint.Schema.DirectiveDefinition => [:directives, :arguments], 81 Blueprint.Schema.EnumTypeDefinition => [:directives, :values], 82 Blueprint.Schema.EnumValueDefinition => [:directives], 83 Blueprint.Schema.FieldDefinition => [:type, :arguments, :directives], 84 Blueprint.Schema.InputObjectTypeDefinition => [:fields, :directives], 85 Blueprint.Schema.InputValueDefinition => [:type, :default_value, :directives], 86 Blueprint.Schema.InterfaceTypeDefinition => [:interfaces, :fields, :directives], 87 Blueprint.Schema.ObjectTypeDefinition => [:interfaces, :fields, :directives], 88 Blueprint.Schema.ScalarTypeDefinition => [:directives], 89 Blueprint.Schema.SchemaDefinition => [:directive_definitions, :type_definitions, :directives], 90 Blueprint.Schema.UnionTypeDefinition => [:directives, :types] 91 } 92 93 @spec walk( 94 Blueprint.node_t(), 95 acc, 96 (Blueprint.node_t(), acc -> 97 {Blueprint.node_t(), acc} | {:halt, Blueprint.node_t(), acc}), 98 (Blueprint.node_t(), acc -> {Blueprint.node_t(), acc}) 99 ) :: {Blueprint.node_t(), acc} 100 when acc: var 101 def walk(blueprint, acc, pre, post) 102 103 def walk(nodes, acc, pre, post) when is_list(nodes) do 104 Enum.map_reduce(nodes, acc, &walk(&1, &2, pre, post)) 105 end 106 107 def walk(node, acc, pre, post) do 108 {node, acc} = 109 case pre.(node, acc) do 110 {:halt, node, acc} -> 111 {node, acc} 112 113 {node, acc} -> 114 maybe_walk_children(node, acc, pre, post) 115 end 116 117 post.(node, acc) 118 end 119 120 for {node_name, children} <- nodes_with_children do 121 def maybe_walk_children(%unquote(node_name){} = node, acc, pre, post) do 122 node_with_children(node, unquote(children), acc, pre, post) 123 end 124 end 125 126 def maybe_walk_children(node, acc, _, _) do 127 {node, acc} 128 end 129 130 defp node_with_children(node, children, acc, pre, post) do 131 walk_children(node, children, acc, pre, post) 132 end 133 134 defp walk_children(node, children, acc, pre, post) do 135 Enum.reduce(children, {node, acc}, fn child_key, {node, acc} -> 136 {children, acc} = 137 node 138 |> Map.fetch!(child_key) 139 |> walk(acc, pre, post) 140 141 {Map.put(node, child_key, children), acc} 142 end) 143 end 144 end