"stand" - a contiguous community of trees sufficiently uniform in composition, structure, age, size, class, distribution, spatial arrangement, condition, or location on a site of uniform quality to distinguish it from adjacent communities
atuin-stand
is a set of libraries that implement a generic, ordered tree. The API and data format is designed to be consistent across all the implementations.
Currently, there are implementations for:
Please see the documentation for each specific implementation for installation and usage instructions.
const tree = new Tree();
const root = tree.root();
root.id(); // => Symbol(ROOT)
const child = root.createChild("id1").setData(someData);
child.id(); // => "id1"
const child2 = child.createChild("id2").setData(otherData);
child.getData(); // => someData
let tree = Tree::new();
let root = tree.root();
root.id(); // => NodeID::Root
let child = root.create_child("id1").set_data(some_data);
child.id(); // => NodeID::id("id1");
let child2 = child.create_child("id2").set_data(other_data);
child.get_data(); // => some_data
alias AtuinStand.Tree
alias AtuinStand.Node
tree = Tree.new()
root = Tree.root(tree)
root.id # => :root
{:ok, child} = Node.create_child(root, "id1")
Node.set_data(child, some_data)
child.id # => "id1"
{:ok, child2} = Node.create_child(child, "id2")
Node.set_data(child2, other_data)
Node.get_data(child) # => some_data
Each library supports the following operations, although the exact API varies slightly based on language idioms and other factors.
getRoot()
- get the root nodehasNode(id)
- check if a node exists in the treegetNode(id)
- fetch a node by its IDgetLeaves()
- get a list of all the leaf (external) nodesgetBranches(order)
- get a list of all the branch (internal) nodessize()
- return the number of nodes in the treeonChange()
- subscribe to changes in the tree
tree()
- get access to the underlying tree objectcreateChild(id, index?)
- create a node with this node as a parent at the given indexid()
- return the ID for the nodesetData()
- associate some data with a nodegetData()
- fetch the associated data from a noderoot()
- get the root node of the tree this node is inparent()
- get this node's parentchildren()
- get a list of this node's childrensiblings()
- get a list of all the other nodes with the same parent as this nodeancestors()
- get a list of nodes from this node to the root of the treedescendents(order)
- get a list of all child nodes of this node, recursively, in the given traversal orderdepth()
- a count of a node's ancestorsmoveTo(other, index?)
- move this node so its parent is nowother
, placed at the given indexmoveBefore(other)
- move this node to be the sibling immediately preceedingother
moveAfter(other)
- move this node to be the sibling immediately afterother
reposition(node, index)
- move this node to a different location amongst its siblingsdelete(strategy)
- delete this node, specifying what to do with its children
Each implementation provides a way to traverse the nodes of the tree in either depth-first or breadth-first order. The API for this varies depending on the language:
- TypeScript:
Iterator
andIterable
- Rust:
Iterator
- Elixir:
Enumberable
Whenever you want to delete a node from the tree, you must decide what to do with its children (if it has any):
Refuse
- produce an error if the node has any childrenCascade
- delete the nodes children and all their children, recursivelyReattach
- attach this node's children to this node's parent before deleting it
Each implementation is designed so that data from one can be imported to another. For this reason, data is limited to information that can be serialized to JSON.
The exact type varies per language, but in general, the data format looks like this:
type Tree<UserData> = Map<String, TreeNode<UserData>>
type TreeNode<UserData> = {
id: String,
parent: String | null,
data: UserData | null,
index: Number
}
Serialization and deserialization is handled differently per implementation:
- Rust:
serde_json
- TypeScript: exports as an object, use
JSON
or any other JSON library for serialization - Elixir: exports as a map, use Elixir's
JSON
or any other JSON library for serialization