AtuinStand.Node (atuin_stand v0.1.0)

View Source

A node in an AtuinStand.Tree.

You can access the node's ID via the id property, and the tree it belongs to via the tree property.

Since Node structs only hold a reference to their containing tree, nodes might be invalidated if the tree is manipulated such that the node is removed. In this case, the Node functions will return {:error, :not_found}.

For a more detailed overview of the API, see AtuinStand.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
%AtuinStand.Node{id: :root, tree: tree}
iex> AtuinStand.Node.create_child(root, "node1")
%AtuinStand.Node{id: "node1", tree: tree}

Summary

Functions

Returns a list of all ancestors of the given node, starting at the node's parent and ending at the root node (inclusive).

Returns a list of all children of the given node.

Creates a new child node with the given ID.

Deletes the node from the tree.

Returns the depth of the given node.

Returns a list of all descendants of the given node.

Returns the user-defined data associated with the node.

Moves the node after the given node.

Moves the node before the given node.

Moves the node to a new parent node.

Returns the parent of the given node.

Moves the node to a new position amongst its siblings.

Sets the user-defined data associated with the node. Returns the node.

Returns a list of all siblings (other nodes with the same parent) of the given node.

Types

t()

@type t() :: %AtuinStand.Node{id: atom() | String.t(), tree: AtuinStand.Tree.t()}

Functions

ancestors(node)

@spec ancestors(node :: t()) :: [t()] | {:error, atom()}

Returns a list of all ancestors of the given node, starting at the node's parent and ending at the root node (inclusive).

Returns {:error, :not_found} if the node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(node1, "node2")
iex> node3 = AtuinStand.Node.create_child(node2, "node3")
iex> AtuinStand.Node.ancestors(node3)
[node2, node1, root]

children(node)

Returns a list of all children of the given node.

Returns {:error, :not_found} if the node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(root, "node2")
iex> AtuinStand.Node.children(root)
[node1, node2]
iex> AtuinStand.Node.children(node1)
[]

create_child(parent_node, id)

@spec create_child(node :: t(), id :: String.t()) :: t() | {:error, atom()}

Creates a new child node with the given ID.

User-created nodes must have unique, string IDs. Returns {:error, :duplicate_id} if a node with the given ID already exists in the tree. Returns {:error, :not_found} if the parent node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> AtuinStand.Node.create_child(root, "node1")
%AtuinStand.Node{id: "node1", tree: tree}
iex> AtuinStand.Node.create_child(root, "node1")
{:error, :duplicate_id}

delete(node, strategy \\ :refuse)

@spec delete(node :: t(), strategy :: :refuse | :cascade | :reattach) ::
  t() | {:error, atom()}

Deletes the node from the tree.

Returns {:error, :invalid_operation} if the node is the root node. Returns {:error, :not_found} if the node is not found in the tree.

Provide a strategy to specify what to do with the node's children:

  • :refuse - return {:error, :has_children} if the node being deleted has children
  • :cascade - recursively delete the node and all of its children
  • :reattach - move the node's children to the node's parent before deleting it

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(node1, "node2")
iex> node3 = AtuinStand.Node.create_child(node2, "node3")
iex> AtuinStand.Node.delete(node1, :refuse)
{:error, :has_children}
iex> AtuinStand.Node.delete(node1, :reattach)
iex> AtuinStand.Node.descendants(root)
[node2, node3]
iex> AtuinStand.Node.delete(node2, :cascade)
iex> AtuinStand.Node.descendants(root)
[]

depth(node)

@spec depth(node :: t()) :: non_neg_integer() | {:error, atom()}

Returns the depth of the given node.

For any node, the depth is the number of edges on the path to the root node. The root node has a depth of 0, and every other node has a depth of 1 + its parent's depth.

Equivalent to length(AtuinStand.Node.ancestors(node)).

Returns {:error, :not_found} if the node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> AtuinStand.Node.depth(root)
0
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> AtuinStand.Node.depth(node1)
1
iex> node2 = AtuinStand.Node.create_child(node1, "node2")
iex> AtuinStand.Node.depth(node2)
2

descendants(node, order \\ :dfs)

@spec descendants(node :: t(), order :: :dfs | :bfs) :: [t()] | {:error, atom()}

Returns a list of all descendants of the given node.

Provide :dfs or :bfs as an optional argument to return the results in depth-first or breadth-first order, respectively. Defaults to :dfs.

Returns {:error, :not_found} if the node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(node1, "node2")
iex> node3 = AtuinStand.Node.create_child(node2, "node3")
iex> node4 = AtuinStand.Node.create_child(root, "node4")
iex> AtuinStand.Node.descendants(node1, :dfs)
[node2, node3]
iex> AtuinStand.Node.descendants(root, :bfs)
[node1, node4, node2, node3]

get_data(node)

@spec get_data(node :: t()) :: map() | {:error, atom()}

Returns the user-defined data associated with the node.

If the node is not found, returns {:error, :not_found}.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> AtuinStand.Node.create_child(root, "node1")
iex> AtuinStand.Tree.node(tree, "node1")
...> |> AtuinStand.Node.set_data(%{"name" => "Node 1"})
...> |> AtuinStand.Node.get_data()
%{"name" => "Node 1"}

move_after(node, other)

@spec move_after(node :: t(), other :: t()) :: t() | {:error, atom()}

Moves the node after the given node.

Returns {:error, :invalid_operation} if the node is the root node or if the move would create a cycle in the tree. Returns {:error, :not_found} if either node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(root, "node2")
iex> node3 = AtuinStand.Node.create_child(root, "node3")
iex> AtuinStand.Node.move_after(node1, node3)
iex> AtuinStand.Node.children(root)
[node2, node3, node1]

move_before(node, other)

@spec move_before(node :: t(), other :: t()) :: t() | {:error, atom()}

Moves the node before the given node.

Returns {:error, :invalid_operation} if the node is the root node or if the move would create a cycle in the tree. Returns {:error, :not_found} if either node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(root, "node2")
iex> node3 = AtuinStand.Node.create_child(root, "node3")
iex> AtuinStand.Node.move_before(node3, node1)
iex> AtuinStand.Node.children(root)
[node3, node1, node2]

move_to(node, new_parent, index \\ nil)

@spec move_to(node :: t(), new_parent :: t(), index :: non_neg_integer() | nil) ::
  t() | {:error, atom()}

Moves the node to a new parent node.

Returns {:error, :invalid_operation} if the node is the root node or if the move would create a cycle in the tree. Returns {:error, :not_found} if the either node is not found in the tree.

Provide an optional index to specify the position of the node in the new parent's child list. The node will be inserted at the end if no index is provided.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(node1, "node2")
iex> node3 = AtuinStand.Node.create_child(node2, "node3")
iex> AtuinStand.Node.move_to(node1, node3)
{:error, :invalid_operation}
iex> AtuinStand.Node.move_to(node2, root)
iex> AtuinStand.Node.children(root)
[node1, node2]
iex> AtuinStand.Node.move_to(node3, root, 1)
iex> AtuinStand.Node.children(root)
[node1, node3, node2]
iex> AtuinStand.Node.move_to(node2, root, 1)
iex> AtuinStand.Node.children(root)
[node1, node2, node3]

parent(node)

@spec parent(node :: t()) :: t() | {:error, atom()}

Returns the parent of the given node.

Returns {:error, :not_found} if the node is not found in the tree. Returns {:error, :invalid_node} if the node is the root node.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> AtuinStand.Node.parent(root)
{:error, :invalid_node}
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> AtuinStand.Node.parent(node1)
%AtuinStand.Node{id: :root, tree: tree}
iex> fake_node = %AtuinStand.Node{id: "fake", tree: tree}
iex> AtuinStand.Node.parent(fake_node)
{:error, :not_found}

reposition(node, index)

@spec reposition(node :: t(), index :: non_neg_integer()) :: t() | {:error, atom()}

Moves the node to a new position amongst its siblings.

Returns {:error, :invalid_operation} if the node is the root node. Returns {:error, :not_found} if the node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(root, "node2")
iex> node3 = AtuinStand.Node.create_child(root, "node3")
iex> AtuinStand.Node.reposition(node2, 0)
iex> AtuinStand.Node.children(root)
[node2, node1, node3]
iex> AtuinStand.Node.reposition(node2, 2)
iex> AtuinStand.Node.children(root)
[node1, node3, node2]

set_data(node, data)

@spec set_data(node :: t(), data :: map()) :: t() | {:error, atom()}

Sets the user-defined data associated with the node. Returns the node.

The data must be a map, otherwise returns {:error, :invalid_data}. When the tree is serialized to JSON, the data is serialized as well, so any atom keys will be converted to strings.

If the node is not found, returns {:error, :not_found}.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> AtuinStand.Node.create_child(root, "node1")
iex> AtuinStand.Tree.node(tree, "node1")
...> |> AtuinStand.Node.set_data(%{"name" => "Node 1"})
...> |> AtuinStand.Node.get_data()
%{"name" => "Node 1"}

siblings(node)

@spec siblings(node :: t()) :: [t()] | {:error, atom()}

Returns a list of all siblings (other nodes with the same parent) of the given node.

Returns {:error, :not_found} if the node is not found in the tree.

Examples

iex> tree = AtuinStand.Tree.new()
iex> root = AtuinStand.Tree.root(tree)
iex> node1 = AtuinStand.Node.create_child(root, "node1")
iex> node2 = AtuinStand.Node.create_child(root, "node2")
iex> node3 = AtuinStand.Node.create_child(root, "node3")
iex> AtuinStand.Node.siblings(node1)
[node2, node3]