diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a882442 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 diff --git a/.rustfmt.toml b/.rustfmt.toml index c9524ed..911149d 100644 --- a/.rustfmt.toml +++ b/.rustfmt.toml @@ -54,7 +54,6 @@ use_field_init_shorthand = false force_explicit_abi = true condense_wildcard_suffixes = false color = "Auto" -required_version = "1.4.36" unstable_features = false disable_all_formatting = false skip_children = false diff --git a/Cargo.toml b/Cargo.toml index bf988f3..c670cf2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,30 +1,6 @@ -[package] -name = "indextree" -version = "4.7.0" -license = "MIT" -readme = "README.md" -keywords = ["tree", "arena", "index", "indextree", "trie"] -authors = ["Sascha Grunert "] -repository = "https://github.com/saschagrunert/indextree" -homepage = "https://github.com/saschagrunert/indextree" -documentation = "https://docs.rs/indextree" -description = "Arena based tree structure by using indices instead of reference counted pointers" -categories = ["data-structures"] -edition = "2021" - -[features] -default = ["std"] -deser = ["serde"] -par_iter = ["rayon"] -std = [] - -[dependencies] -rayon = { version = "1.7.0", optional = true } -serde = { version = "1.0.154", features = ["derive"], optional = true } - -[[example]] -name = "parallel_iteration" -required-features = ["par_iter"] - -[[example]] -name = "simple" +[workspace] +resolver = "2" +members = [ + "indextree", + "indextree-macros", +] diff --git a/indextree-macros/Cargo.toml b/indextree-macros/Cargo.toml new file mode 100644 index 0000000..235399d --- /dev/null +++ b/indextree-macros/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "indextree-macros" +version = "0.1.0" +edition = "2021" + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0.86" +quote = "1.0.36" +thiserror = "1.0.61" +either = "1.13.0" +syn = { version = "2.0.71", features = ["extra-traits", "full", "visit"] } +itertools = "0.13.0" +strum = { version = "0.26.3", features = ["derive"] } + +[dev-dependencies] +indextree = { path = "../indextree" } diff --git a/indextree-macros/src/lib.rs b/indextree-macros/src/lib.rs new file mode 100644 index 0000000..b82c9b7 --- /dev/null +++ b/indextree-macros/src/lib.rs @@ -0,0 +1,309 @@ +use either::Either; +use itertools::Itertools; +use proc_macro2::TokenStream; +use quote::{quote, ToTokens}; +use strum::EnumDiscriminants; +use syn::{ + braced, + parse::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + Expr, Token, +}; + +#[derive(Clone, Debug)] +struct IndexNode { + node: Expr, + children: Punctuated, +} + +impl Parse for IndexNode { + fn parse(input: ParseStream) -> syn::Result { + let node = input.parse::()?; + + if input.parse::]>().is_err() { + return Ok(IndexNode { + node, + children: Punctuated::new(), + }); + } + + let children_stream; + braced!(children_stream in input); + let children = children_stream.parse_terminated(Self::parse, Token![,])?; + + Ok(IndexNode { node, children }) + } +} + +#[derive(Clone, Debug)] +struct IndexTree { + arena: Expr, + root_node: Expr, + nodes: Punctuated, +} + +impl Parse for IndexTree { + fn parse(input: ParseStream) -> syn::Result { + let arena = input.parse::()?; + + input.parse::()?; + + let root_node = input.parse::()?; + + let nodes = if input.parse::]>().is_ok() { + let braced_nodes; + braced!(braced_nodes in input); + braced_nodes.parse_terminated(IndexNode::parse, Token![,])? + } else { + Punctuated::new() + }; + + let _ = input.parse::(); + + Ok(IndexTree { + arena, + root_node, + nodes, + }) + } +} + +#[derive(Clone, EnumDiscriminants, Debug)] +#[strum_discriminants(name(ActionKind))] +enum Action { + Append(Expr), + Parent, + Nest, +} + +impl ToTokens for Action { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.to_stream()) + } +} + +impl Action { + fn to_stream(&self) -> TokenStream { + match self { + Action::Append(expr) => quote! { + __last = __node.append_value(#expr, __arena); + }, + Action::Parent => quote! { + let __temp = ::indextree::Arena::get(__arena, __node); + let __temp = ::core::option::Option::unwrap(__temp); + let __temp = ::indextree::Node::parent(__temp); + let __temp = ::core::option::Option::unwrap(__temp); + __node = __temp; + }, + Action::Nest => quote! { + __node = __last; + }, + } + } +} + +#[derive(Clone, Debug)] +struct NestingLevelMarker; + +#[derive(Clone, Debug)] +struct ActionStream { + count: usize, + kind: ActionKind, + stream: TokenStream, +} + +impl ToTokens for ActionStream { + fn to_tokens(&self, tokens: &mut TokenStream) { + tokens.extend(self.stream.clone()); + } +} + +/// Construct a tree for a given arena. +/// +/// This macro creates a tree in an [`Arena`] with a pre-defined layout. If the root node is of +/// type [`NodeId`], then that [`NodeId`] is used for the root node, but if it's any other type, +/// then it creates a new root node on-the-fly. The macro returns [`NodeId`] of the root node. +/// +/// # Examples +/// +/// ``` +/// # use indextree::{Arena, macros::tree}; +/// # let mut arena = Arena::new(); +/// let root_node = arena.new_node("root node"); +/// tree!( +/// &mut arena, +/// root_node => { +/// "1", +/// "2" => { +/// "2_1" => { "2_1_1" }, +/// "2_2", +/// }, +/// "3", +/// } +/// ); +/// +/// let automagical_root_node = tree!( +/// &mut arena, +/// "root node, but automagically created" => { +/// "1", +/// "2" => { +/// "2_1" => { "2_1_1" }, +/// "2_2", +/// }, +/// "3", +/// } +/// ); +/// ``` +/// +/// Note that you can anchor the root node in the macro to any node at any nesting. So you can take +/// an already existing node of a tree and attach another tree to it: +/// ``` +/// # use indextree::{Arena, macros::tree}; +/// # let mut arena = Arena::new(); +/// let root_node = tree!( +/// &mut arena, +/// "root node" => { +/// "1", +/// "2", +/// "3", +/// } +/// ); +/// +/// let node_1 = arena.get(root_node).unwrap().first_child().unwrap(); +/// let node_2 = arena.get(node_1).unwrap().next_sibling().unwrap(); +/// tree!( +/// &mut arena, +/// node_2 => { +/// "2_1" => { "2_1_1" }, +/// "2_2", +/// } +/// ); +/// ``` +/// +/// It is also possible to create an empty root_node, although, I'm not sure why you'd want to do +/// that. +/// ``` +/// # use indextree::{Arena, macros::tree}; +/// # let mut arena = Arena::new(); +/// let root_node = tree!( +/// &mut arena, +/// "my root node", +/// ); +/// ``` +/// Empty nodes can also be defined as `=> {}` +/// ``` +/// # use indextree::{Arena, macros::tree}; +/// # let mut arena = Arena::new(); +/// let root_node = tree!( +/// &mut arena, +/// "my root node" => {}, +/// ); +/// ``` +/// +/// [`Arena`]: https://docs.rs/indextree/latest/indextree/struct.Arena.html +/// [`NodeId`]: https://docs.rs/indextree/latest/indextree/struct.NodeId.html +#[proc_macro] +pub fn tree(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + let IndexTree { + arena, + root_node, + nodes, + } = parse_macro_input!(input as IndexTree); + + let mut stack: Vec> = + nodes.into_iter().map(Either::Left).rev().collect(); + + let mut action_buffer: Vec = Vec::new(); + + while let Some(item) = stack.pop() { + let Either::Left(IndexNode { node, children }) = item else { + action_buffer.push(Action::Parent); + continue; + }; + + action_buffer.push(Action::Append(node)); + + if children.is_empty() { + continue; + } + + // going one level deeper + stack.push(Either::Right(NestingLevelMarker)); + action_buffer.push(Action::Nest); + stack.extend(children.into_iter().map(Either::Left).rev()); + } + + let mut actions: Vec = action_buffer + .into_iter() + .map(|action| ActionStream { + count: 1, + kind: ActionKind::from(&action), + stream: action.to_stream(), + }) + .coalesce(|action1, action2| { + if action1.kind != action2.kind { + return Err((action1, action2)); + } + + let count = action1.count + action2.count; + let kind = action1.kind; + let mut stream = action1.stream; + stream.extend(action2.stream); + Ok(ActionStream { + count, + kind, + stream, + }) + }) + .collect(); + + let is_last_action_useless = actions + .last() + .map(|last| last.kind == ActionKind::Parent) + .unwrap_or(false); + if is_last_action_useless { + actions.pop(); + } + + // HACK(alexmozaidze): Due to the fact that specialization is unstable, we must resort to + // autoref specialization trick. + // https://github.com/dtolnay/case-studies/blob/master/autoref-specialization/README.md + quote! {{ + let mut __arena: &mut ::indextree::Arena<_> = #arena; + + #[repr(transparent)] + struct __Wrapping<__T>(::core::mem::ManuallyDrop<__T>); + + trait __ToNodeId<__T> { + fn __to_node_id(&mut self, __arena: &mut ::indextree::Arena<__T>) -> ::indextree::NodeId; + } + + trait __NodeIdToNodeId<__T> { + fn __to_node_id(&mut self, __arena: &mut ::indextree::Arena<__T>) -> ::indextree::NodeId; + } + + impl<__T> __NodeIdToNodeId<__T> for __Wrapping<::indextree::NodeId> { + fn __to_node_id(&mut self, __arena: &mut ::indextree::Arena<__T>) -> ::indextree::NodeId { + unsafe { ::core::mem::ManuallyDrop::take(&mut self.0) } + } + } + + impl<__T> __ToNodeId<__T> for &mut __Wrapping<__T> { + fn __to_node_id(&mut self, __arena: &mut ::indextree::Arena<__T>) -> ::indextree::NodeId { + ::indextree::Arena::new_node(__arena, unsafe { ::core::mem::ManuallyDrop::take(&mut self.0) }) + } + } + + let __root_node: ::indextree::NodeId = { + let mut __root_node = __Wrapping(::core::mem::ManuallyDrop::new(#root_node)); + (&mut __root_node).__to_node_id(__arena) + }; + let mut __node: ::indextree::NodeId = __root_node; + let mut __last: ::indextree::NodeId; + + #(#actions)* + + __root_node + }}.into() +} diff --git a/indextree-macros/tests/regular_usage.rs b/indextree-macros/tests/regular_usage.rs new file mode 100644 index 0000000..f5ce6cf --- /dev/null +++ b/indextree-macros/tests/regular_usage.rs @@ -0,0 +1,92 @@ +use std::fmt::{Debug, Display}; + +use indextree::{Arena, NodeId}; +use indextree_macros::tree; + +pub fn compare_nodes(arena: &Arena, n1: NodeId, n2: NodeId) +where + T: Debug + Display + Clone + PartialEq, +{ + let get_val = |id: NodeId| -> T { arena.get(id).unwrap().get().clone() }; + + let n1_iter = n1.descendants(arena).skip(1).map(get_val); + let n2_iter = n2.descendants(arena).skip(1).map(get_val); + + assert!( + Iterator::eq(n1_iter, n2_iter), + r#"Tree equality assertion failed! + +### Left Tree ### + +{} + +### Right Tree ### + +{}"#, + n1.debug_pretty_print(arena), + n2.debug_pretty_print(arena), + ); +} + +#[test] +fn no_nesting() { + let mut arena = Arena::new(); + + let root_macro = tree! { + &mut arena, + "macro root node" => { + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + } + }; + + let root_proc = arena.new_node("procedural root node"); + root_proc.append_value("1", &mut arena); + root_proc.append_value("2", &mut arena); + root_proc.append_value("3", &mut arena); + root_proc.append_value("4", &mut arena); + root_proc.append_value("5", &mut arena); + root_proc.append_value("6", &mut arena); + root_proc.append_value("7", &mut arena); + root_proc.append_value("8", &mut arena); + root_proc.append_value("9", &mut arena); + root_proc.append_value("10", &mut arena); + + compare_nodes(&arena, root_proc, root_macro); +} + +#[test] +fn mild_nesting() { + let mut arena = Arena::new(); + + let root_macro = arena.new_node("macro root node"); + tree!( + &mut arena, + root_macro => { + "1", + "2" => { + "2_1" => { "2_1_1" }, + "2_2", + }, + "3", + } + ); + + let root_proc = arena.new_node("proc root node"); + root_proc.append_value("1", &mut arena); + let node_2 = root_proc.append_value("2", &mut arena); + let node_2_1 = node_2.append_value("2_1", &mut arena); + node_2_1.append_value("2_1_1", &mut arena); + node_2.append_value("2_2", &mut arena); + root_proc.append_value("3", &mut arena); + + compare_nodes(&arena, root_proc, root_macro); +} diff --git a/indextree-macros/tests/stress_test.rs b/indextree-macros/tests/stress_test.rs new file mode 100644 index 0000000..4ed0645 --- /dev/null +++ b/indextree-macros/tests/stress_test.rs @@ -0,0 +1,146 @@ +mod regular_usage; + +use indextree::Arena; +use indextree_macros::tree; +use regular_usage::compare_nodes; + +#[test] +fn outragous_nesting() { + let mut arena = Arena::new(); + + let root_macro = tree!(&mut arena, "macro root node"); + tree!( + &mut arena, + root_macro => { + "1"=>{"2"=>{"3"=>{"4"=>{"5"=>{"6"=>{"7"=>{"8"=>{"9"=>{"10"=>{"11"=>{"12"=>{"13"=>{"14"=>{"15"=>{"16"=>{"17"=>{"18"=>{"19"=>{"20"=>{"21"=>{"22"=>{"23"=>{"24"=>{"25"=>{"26"=>{"27"=>{"28"=>{"29"=>{"30"=>{"31"=>{"32"=>{"33"=>{"34"=>{"35"=>{"36"=>{"37"=>{"38"=>{"39"=>{"40"=>{"41"=>{"42"=>{"43"=>{"44"=>{"45"=>{"46"=>{"47"=>{"48"=>{"49"=>{"50"=>{"51"=>{"52"=>{"53"=>{"54"=>{"55"=>{"56"=>{"57"=>{"58"=>{"59"=>{"60"=>{"61"=>{"62"=>{"63"=>{"64"=>{"65"=>{"66"=>{"67"=>{"68"=>{"69"=>{"70"=>{"71"=>{"72"=>{"73"=>{"74"=>{"75"=>{"76"=>{"77"=>{"78"=>{"79"=>{"80"=>{"81"=>{"82"=>{"83"=>{"84"=>{"85"=>{"86"=>{"87"=>{"88"=>{"89"=>{"90"=>{"91"=>{"92"=>{"93"=>{"94"=>{"95"=>{"96"=>{"97"=>{"98"=>{"99"=>{"100"}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} + } + ); + + let root_proc = arena.new_node("proc root node"); + let mut deepest_node = root_proc.append_value("1", &mut arena); + let owned_strings: Vec = (2..=100).map(|x| x.to_string()).collect(); + for i in &owned_strings { + deepest_node = deepest_node.append_value(i.as_str(), &mut arena); + } + + compare_nodes(&arena, root_macro, root_proc); +} + +#[test] +fn very_long() { + let mut arena = Arena::new(); + + let root_macro = tree!( + &mut arena, + "macro root node" => { + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "15", + "16", + "17", + "18", + "19", + "20", + "21", + "22", + "23", + "24", + "25", + "26", + "27", + "28", + "29", + "30", + "31", + "32", + "33", + "34", + "35", + "36", + "37", + "38", + "39", + "40", + "41", + "42", + "43", + "44", + "45", + "46", + "47", + "48", + "49", + "50", + "51", + "52", + "53", + "54", + "55", + "56", + "57", + "58", + "59", + "60", + "61", + "62", + "63", + "64", + "65", + "66", + "67", + "68", + "69", + "70", + "71", + "72", + "73", + "74", + "75", + "76", + "77", + "78", + "79", + "80", + "81", + "82", + "83", + "84", + "85", + "86", + "87", + "88", + "89", + "90", + "91", + "92", + "93", + "94", + "95", + "96", + "97", + "98", + "99", + "100", + } + ); + + let root_proc = arena.new_node("proc root node"); + let owned_strings: Vec = (1..=100).map(|x| x.to_string()).collect(); + for i in &owned_strings { + root_proc.append_value(i.as_str(), &mut arena); + } + + compare_nodes(&arena, root_macro, root_proc); +} diff --git a/indextree/Cargo.toml b/indextree/Cargo.toml new file mode 100644 index 0000000..19050dd --- /dev/null +++ b/indextree/Cargo.toml @@ -0,0 +1,36 @@ +[package] +name = "indextree" +version = "4.7.0" +license = "MIT" +readme = "README.md" +keywords = ["tree", "arena", "index", "indextree", "trie"] +authors = ["Sascha Grunert "] +repository = "https://github.com/saschagrunert/indextree" +homepage = "https://github.com/saschagrunert/indextree" +documentation = "https://docs.rs/indextree" +description = "Arena based tree structure by using indices instead of reference counted pointers" +categories = ["data-structures"] +edition = "2021" + +[features] +default = ["std", "macros"] +deser = ["serde"] +par_iter = ["rayon"] +std = [] +macros = ["indextree-macros"] + +[dependencies] +rayon = { version = "1.7.0", optional = true } +serde = { version = "1.0.154", features = ["derive"], optional = true } +indextree-macros = { path = "../indextree-macros", optional = true } + +[[example]] +name = "parallel_iteration" +required-features = ["par_iter"] + +[[example]] +name = "simple" + +[[example]] +name = "tree-macro" +required-features = ["macros"] diff --git a/examples/parallel_iteration.rs b/indextree/examples/parallel_iteration.rs similarity index 100% rename from examples/parallel_iteration.rs rename to indextree/examples/parallel_iteration.rs diff --git a/examples/simple.rs b/indextree/examples/simple.rs similarity index 100% rename from examples/simple.rs rename to indextree/examples/simple.rs diff --git a/indextree/examples/tree-macro.rs b/indextree/examples/tree-macro.rs new file mode 100644 index 0000000..6f57f69 --- /dev/null +++ b/indextree/examples/tree-macro.rs @@ -0,0 +1,36 @@ +use indextree::{macros::tree, Arena}; + +fn main() { + let mut arena = Arena::new(); + + // It works with existing nodes + let root_node = arena.new_node("my root node"); + tree!( + &mut arena, + root_node => { + "1", + "2" => { + "2_1" => { "2_1_1" }, + "2_2", + }, + "3" => {}, + } + ); + + println!("{}", root_node.debug_pretty_print(&arena)); + + // It can also create a root node for you! + let root_node = tree!( + &mut arena, + "my root node, but automagically created" => { + "1", + "2" => { + "2_1" => { "2_1_1" }, + "2_2", + }, + "3", + } + ); + + println!("{}", root_node.debug_pretty_print(&arena)); +} diff --git a/src/arena.rs b/indextree/src/arena.rs similarity index 96% rename from src/arena.rs rename to indextree/src/arena.rs index 9124ab2..11a3a6a 100644 --- a/src/arena.rs +++ b/indextree/src/arena.rs @@ -8,6 +8,7 @@ use core::{ mem, num::NonZeroUsize, ops::{Index, IndexMut}, + slice, }; #[cfg(feature = "par_iter")] @@ -21,6 +22,7 @@ use std::{ mem, num::NonZeroUsize, ops::{Index, IndexMut}, + slice, }; use crate::{node::NodeData, Node, NodeId}; @@ -280,7 +282,7 @@ impl Arena { /// ``` /// /// [`is_removed()`]: struct.Node.html#method.is_removed - pub fn iter(&self) -> impl Iterator> { + pub fn iter(&self) -> slice::Iter> { self.nodes.iter() } @@ -307,7 +309,7 @@ impl Arena { /// assert_eq!(node_refs, vec![5, 6]); /// ``` /// [`is_removed()`]: struct.Node.html#method.is_removed - pub fn iter_mut(&mut self) -> impl Iterator> { + pub fn iter_mut(&mut self) -> slice::IterMut> { self.nodes.iter_mut() } @@ -327,6 +329,14 @@ impl Arena { self.last_free_slot = None; } + /// Returns a slice of the inner nodes collection. + /// + /// Note that this **does not** return root elements, it simply + /// returns a slice into the internal representation of the arena. + pub fn as_slice(&self) -> &[Node] { + self.nodes.as_slice() + } + pub(crate) fn free_node(&mut self, id: NodeId) { let node = &mut self[id]; node.data = NodeData::NextFree(None); diff --git a/src/debug_pretty_print.rs b/indextree/src/debug_pretty_print.rs similarity index 100% rename from src/debug_pretty_print.rs rename to indextree/src/debug_pretty_print.rs diff --git a/src/error.rs b/indextree/src/error.rs similarity index 100% rename from src/error.rs rename to indextree/src/error.rs diff --git a/src/id.rs b/indextree/src/id.rs similarity index 99% rename from src/id.rs rename to indextree/src/id.rs index 5108036..8cd3ea3 100644 --- a/src/id.rs +++ b/indextree/src/id.rs @@ -363,6 +363,10 @@ impl NodeId { /// assert_eq!(iter.next(), None); /// ``` #[allow(deprecated)] + #[deprecated( + since = "4.7.0", + note = "please, use `NodeId::children().rev()` instead if you want to iterate in reverse" + )] pub fn reverse_children(self, arena: &Arena) -> ReverseChildren<'_, T> { ReverseChildren::new(arena, self) } diff --git a/src/lib.rs b/indextree/src/lib.rs similarity index 95% rename from src/lib.rs rename to indextree/src/lib.rs index 2b7623e..c0c89b7 100644 --- a/src/lib.rs +++ b/indextree/src/lib.rs @@ -41,6 +41,9 @@ pub use crate::{ }, }; +#[cfg(feature = "macros")] +pub use indextree_macros as macros; + #[macro_use] pub(crate) mod relations; diff --git a/src/node.rs b/indextree/src/node.rs similarity index 100% rename from src/node.rs rename to indextree/src/node.rs diff --git a/src/relations.rs b/indextree/src/relations.rs similarity index 100% rename from src/relations.rs rename to indextree/src/relations.rs diff --git a/src/siblings_range.rs b/indextree/src/siblings_range.rs similarity index 100% rename from src/siblings_range.rs rename to indextree/src/siblings_range.rs diff --git a/src/traverse.rs b/indextree/src/traverse.rs similarity index 95% rename from src/traverse.rs rename to indextree/src/traverse.rs index 62fa5d9..806bc7c 100644 --- a/src/traverse.rs +++ b/indextree/src/traverse.rs @@ -163,13 +163,35 @@ new_iterator!( new_iterator!( /// An iterator of the IDs of the siblings before a given node. PrecedingSiblings, - next = |node| node.previous_sibling, + new = |arena, node| { + let first = arena + .get(node) + .unwrap() + .parent + .and_then(|parent_id| arena.get(parent_id)) + .and_then(|parent| parent.first_child); + + DoubleEndedIter::new(arena, node, first) + }, + next = |head| head.previous_sibling, + next_back = |tail| tail.next_sibling, ); new_iterator!( /// An iterator of the IDs of the siblings after a given node. FollowingSiblings, - next = |node| node.next_sibling, + new = |arena, node| { + let last = arena + .get(node) + .unwrap() + .parent + .and_then(|parent_id| arena.get(parent_id)) + .and_then(|parent| parent.last_child); + + DoubleEndedIter::new(arena, node, last) + }, + next = |head| head.next_sibling, + next_back = |tail| tail.previous_sibling, ); new_iterator!( diff --git a/tests/debug_pretty_print.rs b/indextree/tests/debug_pretty_print.rs similarity index 100% rename from tests/debug_pretty_print.rs rename to indextree/tests/debug_pretty_print.rs diff --git a/tests/insert-error.rs b/indextree/tests/insert-error.rs similarity index 100% rename from tests/insert-error.rs rename to indextree/tests/insert-error.rs diff --git a/tests/lib.rs b/indextree/tests/lib.rs similarity index 100% rename from tests/lib.rs rename to indextree/tests/lib.rs diff --git a/tests/remove.rs b/indextree/tests/remove.rs similarity index 100% rename from tests/remove.rs rename to indextree/tests/remove.rs