Packages
graphqexl
0.1.0-alpha-rc.25
0.1.0
0.1.0-alpha-rc.docs-test-6
0.1.0-alpha-rc.docs-test-5
0.1.0-alpha-rc.docs-test-4
0.1.0-alpha-rc.docs-test-3
0.1.0-alpha-rc.docs-test-2
0.1.0-alpha-rc.docs-test
0.1.0-alpha-rc.25
0.1.0-alpha-rc.24
0.1.0-alpha-rc.23
0.1.0-alpha-rc.22
0.1.0-alpha-rc.21
0.1.0-alpha-rc.20
0.1.0-alpha-rc.19
0.1.0-alpha-rc.18
0.1.0-alpha-rc.17
0.1.0-alpha-rc.16
0.1.0-alpha-rc.15
0.1.0-alpha-rc.14
0.1.0-alpha-rc.13
0.1.0-alpha-rc.12
0.1.0-alpha-rc.11
0.1.0-alpha-rc.10
0.1.0-alpha-rc.9
0.1.0-alpha-rc.8
0.1.0-alpha-rc.7
0.1.0-alpha-rc.6
0.1.0-alpha-rc.5
0.1.0-alpha-rc.4
0.1.0-alpha-rc.3
0.1.0-alpha-rc.2
0.1.0-alpha-rc.1
0.1.0-alpha.rc-4
0.1.0-alpha.rc.4
Fully-loaded, pure-Elixir GraphQL server implementation with developer tools
Current section
Files
Jump to
Current section
Files
lib/treex/tree.ex
defmodule Treex.Tree do
@moduledoc """
Primarily a struct for representing tree data to be processed with the `Tree.Traverse` module.
Also includes implementations necessary for the `Enumerable` protocol and conversion functions
to/from ordinary `t:Map.t/0` representations of the same data.
One import note is the notion of a "virtual root." In order to represent arbitrary maps, any map
that has multiple top-level keys (i.e. has multiple roots and is a graph, not a tree) will be
converted to a tree by inserting a root node whose value defaults to `:root` and can be specified
as the second parameter to `Treex.Tree.from_map/2`.
"""
@moduledoc since: "0.1.0"
defstruct value: nil, children: [], key: nil
@type acc:: {:cont, term} | {:halt, term} | {:suspend, term}
@type continuation:: (acc -> result)
@type element:: {term, list(term) | Map.t | term}
@type length:: pos_integer
@type reducer:: (term, term -> acc)
@type result:: {:done, term} | {:halted, term} | {:suspended, term, continuation}
@type size:: non_neg_integer
@type start:: non_neg_integer
@type slicing_fun :: (start, length -> list(term))
@type t:: %Treex.Tree{value: any, children: [t], key: term}
@doc """
Counts the number of nodes in the given tree.
Returns: `t:integer`
"""
@doc since: "0.1.0"
@spec count(t):: {:ok, size} | {:error, module}
def count(_tree), do: 42
@doc """
Checks whether the given element is a member of the tree at any depth, performed breadth-first.
Returns: `t:boolean`
"""
@doc since: "0.1.0"
@spec member?(t, term) :: {:ok, boolean} | {:error, module}
def member?(_tree, _element), do: true
@doc """
Reduces the given tree into the given accumulator by invoking the given `t:Treex.Tree.reducer/0`
function on each node, traversed breadth-first. The return is tagged tuple following the
`Enumerable` protocol.
Returns: `Treex.Tree.acc/0` (where `term` is the same type as the `acc` parameter)
"""
@doc since: "0.1.0"
@spec reduce(t, acc, reducer) :: result
def reduce(_tree, acc, _fun), do: {:cont, acc}
@doc """
Generates a function that contiguously slices the given tree. See `Enumerable.slice/1`
Returns
`{:ok, t:non_neg_integer/0, t:Treex.Tree.slicing_fun/0}` when successful
`{:error, t:module/0}` when there is an error
"""
@doc since: "0.1.0"
@spec slice(t) :: {:ok, size, slicing_fun} | {:error, module}
def slice(_tree), do: &(&1 + &2)
@doc """
List the leaf nodes of the given `t:Treex.Tree.t/0`
Returns: `[t:Treex.Tree.t/0]`
"""
@doc since: "0.1.0"
@spec leaves(t):: list(t) | []
def leaves(_tree), do: []
@doc """
Convert the given map into a `t:Treex.Tree.t/0`. If the given map has more than one top-level key,
the optional `root` parameter specifies what value to give a virtual root node that will be
inserted at the top of the key and contain the given `map`'s top-level keys as children.
Returns: `t:Treex.Tree.t/0`
"""
@doc since: "0.1.0"
@spec from_map(Map.t):: t
@spec from_map(Map.t, term):: t
def from_map(map, root \\ :root)
def from_map(map = %{}, root) when map |> map_size == 0,
do: %Treex.Tree{value: root, children: []}
def from_map(map = %{}, _root) when map |> map_size == 1,
do: map |> Enum.reduce(%Treex.Tree{}, &node_from_element/2)
def from_map(map = %{}, root), do: %{root => map} |> from_map(root)
@doc """
Convert the given tree to a `t:Map.t/0`. Optionally, remove the root node by passing
`pop_root: true` as a keyword option. This is useful, for example, when the caller knows the given
tree has a virtual root.
"""
@doc since: "0.1.0"
@spec to_map(t | list(t)):: Map.t
@spec to_map(t | list(t), [pop_root: boolean]):: Map.t
def to_map(tree, opts \\ [pop_root: false])
def to_map(%Treex.Tree{value: nil, children: []}, _), do: %{}
def to_map([], [pop_root: false]), do: nil
def to_map(trees, [pop_root: false]) when is_list(trees) do
trees
|> Enum.map(&to_map/1)
|> Enum.reduce(%{}, &Map.merge/2)
end
def to_map(tree, [pop_root: false]), do: %{tree.value => tree.children |> to_map}
def to_map(tree, [pop_root: true]), do: tree.children |> to_map
@doc """
Checks whether the given node is a leaf node or not.
Returns: `t:boolean/0`
"""
@doc since: "0.1.0"
@spec leaf_node?(t):: boolean
def leaf_node?(node), do: node.children |> Enum.empty?
@doc false
@spec node_from_element(element):: t
defp node_from_element(element)
defp node_from_element({root, children = %{}}) when children |> map_size == 0,
do: %Treex.Tree{value: root, children: []}
defp node_from_element({root, children = %{}}),
do: %Treex.Tree{value: root, children: children |> Enum.map(&node_from_element/1)}
defp node_from_element({root, children}),
do: %Treex.Tree{value: root, children: [%Treex.Tree{value: children, children: []}]}
@doc false
@spec node_from_element(element, term):: t
defp node_from_element(element, _)
defp node_from_element(pair, _), do: pair |> node_from_element
end