use crate::ShrubberyResult;
use super::control_nodes::*;
use super::*;
use decorators::*;
#[derive(Clone)]
pub struct CTreeBuilder<D: Decorator> {
pub(crate) inner: ControlTree<D>,
}
impl<D: Decorator> ControlTree<D> {
pub fn into_builder(self) -> CTreeBuilder<D> {
CTreeBuilder::from(self)
}
pub fn builder() -> CTreeBuilder<D> {
CTreeBuilder::new()
}
}
impl<D: Decorator> From<ControlTree<D>> for CTreeBuilder<D> {
fn from(value: ControlTree<D>) -> Self {
CTreeBuilder { inner: value }
}
}
impl<D: Decorator> Default for CTreeBuilder<D> {
fn default() -> Self {
Self::new()
}
}
pub type CTreeLayerFn<O, D> = fn(CTreeLayerBuilder<'_, D>) -> O;
impl<D: Decorator> CTreeBuilder<D> {
pub fn new() -> Self {
Self {
inner: ControlTree::new(),
}
}
pub fn layer<O>(&mut self, f: CTreeLayerFn<O, D>) -> O {
f(CTreeLayerBuilder {
builder: self,
layer_id: ROOT_ID,
layer_depth: 0,
})
}
pub fn build(self) -> ShrubberyResult<ControlTree<D>> {
self.inner.validate_bt_rules()?;
Ok(self.inner)
}
#[cfg(test)]
pub fn inject_cycle(&mut self) {
let parent = ROOT_ID;
let child = self
.inner
.add_child_unchecked(parent, ControlNode::sequence());
self.inner.tree.entry(child).or_default().push(parent);
}
}
pub struct CTreeLayerBuilder<'a, D: Decorator> {
pub(crate) builder: &'a mut CTreeBuilder<D>,
pub layer_id: CTreeNodeID,
pub layer_depth: usize,
}
impl<'a, D: Decorator> CTreeLayerBuilder<'a, D> {
pub fn new(
builder: &'a mut CTreeBuilder<D>,
layer_id: CTreeNodeID,
) -> CTreeLayerBuilder<'a, D> {
Self {
builder,
layer_id,
layer_depth: 0,
}
}
pub fn sequence<O>(&mut self, layer_fn: CTreeLayerFn<O, D>) -> O {
self.control_node(ControlNode::sequence(), layer_fn)
}
pub fn fallback<O>(&mut self, layer_fn: CTreeLayerFn<O, D>) -> O {
self.control_node(ControlNode::fallback(), layer_fn)
}
pub fn parallel<O>(&mut self, layer_fn: CTreeLayerFn<O, D>) -> O {
self.control_node(ControlNode::parallel(), layer_fn)
}
pub fn decorator<O>(&mut self, decorator: impl Into<D>, layer_fn: CTreeLayerFn<O, D>) -> O {
let node = ControlNode::decorator(decorator.into());
self.control_node(node, layer_fn)
}
pub fn map<O>(&mut self, f: fn(&mut Self) -> O) -> O {
f(self)
}
pub fn next_layer(&mut self, node: impl Into<ControlNode<D>>) -> CTreeLayerBuilder<'_, D> {
let parent_id = self
.builder
.inner
.add_child_unchecked(self.layer_id, node.into());
CTreeLayerBuilder {
builder: self.builder,
layer_id: parent_id,
layer_depth: self.layer_depth + 1,
}
}
pub fn control_node<O>(
&mut self,
node: impl Into<ControlNode<D>>,
layer_fn: CTreeLayerFn<O, D>,
) -> O {
let layer_builder = self.next_layer(node);
layer_fn(layer_builder)
}
pub fn leaf_node(&mut self, node: impl Into<LeafNode>) -> CTreeNodeID {
self.builder
.inner
.add_child_unchecked(self.layer_id, node.into())
}
}
impl<'a, D: Decorator + From<StandardDecorator>> CTreeLayerBuilder<'a, D> {
pub fn repeat<O>(&mut self, retries: usize, layer_fn: CTreeLayerFn<O, D>) -> O {
let decorator = D::from(Repeater::new(retries).into());
self.decorator(decorator, layer_fn)
}
pub fn invert<O>(&mut self, layer_fn: CTreeLayerFn<O, D>) -> O {
let decorator = D::from(Inverter::default().into());
self.decorator(decorator, layer_fn)
}
pub fn subtree_named<O>(&mut self, name: &str, layer_fn: CTreeLayerFn<O, D>) -> O {
let decorator = D::from(Subtree::new(name.to_string()).into());
self.decorator(decorator, layer_fn)
}
pub fn subtree<O>(&mut self, layer_fn: CTreeLayerFn<O, D>) -> O {
let decorator = D::from(Subtree::default().into());
self.decorator(decorator, layer_fn)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn cyclic_nobuild() {
let mut builder = ControlTree::<StandardDecorator>::builder();
builder.layer(|mut root| {
root.sequence(|mut sequence| {
sequence.leaf_node(LeafNode::default());
sequence.leaf_node(LeafNode::default());
});
});
builder.inject_cycle();
let err = builder.build().unwrap_err();
assert!(matches!(err, ShrubberyError::CycleDetected(_)));
}
#[test]
fn dangling_nobuild() {
let mut builder = ControlTree::<StandardDecorator>::builder();
builder.layer(|mut root_layer| {
root_layer.leaf_node(LeafNode::default());
});
builder.layer(|root_layer| {
root_layer
.builder
.inner
.add_child_unchecked(root_layer.layer_id, ControlNode::sequence());
});
let err = builder.build().unwrap_err();
assert!(matches!(err, ShrubberyError::DanglingControlNode(_)));
}
#[test]
fn multiple_decorator_children_nobuild() {
let mut builder = ControlTree::<StandardDecorator>::builder();
builder.layer(|mut root_layer| {
root_layer.decorator(StandardDecorator::inverter(), |mut decorator_layer| {
decorator_layer.leaf_node(LeafNode::default());
decorator_layer.leaf_node(LeafNode::default());
});
});
let err = builder.build().unwrap_err();
assert!(matches!(err, ShrubberyError::InvalidDecorator { .. }));
}
}