#[doc(hidden)]
#[cfg(feature = "std")]
extern crate std;
use core::ops::Range;
#[cfg(feature = "mock_behavior")]
use crate::behavior::{MockBehavior, MockBehaviorConfig};
use crate::{
BehaviorExecution, ConstString,
behavior::{BehaviorCreationFn, BehaviorPtr, behavior_description::BehaviorDescription},
port::PortDirection,
};
use alloc::{boxed::Box, collections::btree_map::BTreeMap, sync::Arc, vec::Vec};
#[cfg(feature = "std")]
use libloading::Library;
use tinyscript::Runtime;
use super::error::Error;
#[cfg(doc)]
use super::BehaviorTreeFactory;
#[cfg(feature = "mock_behavior")]
#[derive(Clone, Debug)]
pub enum SubstitutionRule {
StringRule(ConstString),
ConfigRule(MockBehaviorConfig),
}
#[derive(Debug)]
pub(crate) struct TreeNodesModelEntry {
pub(crate) _port_type: PortDirection,
pub(crate) key: ConstString,
pub(crate) remapping: ConstString,
}
#[derive(Default)]
pub struct BehaviorRegistry {
behaviors: BTreeMap<ConstString, (BehaviorDescription, Arc<BehaviorCreationFn>)>,
tree_definitions: BTreeMap<ConstString, (ConstString, Range<usize>)>,
tree_nodes_models: BTreeMap<ConstString, TreeNodesModelEntry>,
#[cfg(feature = "mock_behavior")]
substitution_rules: BTreeMap<ConstString, SubstitutionRule>,
main_tree_id: Option<ConstString>,
runtime: Runtime,
#[cfg(feature = "std")]
libraries: Vec<Arc<Library>>,
}
impl BehaviorRegistry {
pub fn add_behavior<F>(&mut self, bhvr_description: BehaviorDescription, bhvr_creation_fn: F) -> Result<(), Error>
where
F: Fn() -> BehaviorPtr + Send + Sync + 'static,
{
if self
.behaviors
.contains_key(bhvr_description.name())
{
return Err(Error::AlreadyRegistered {
name: bhvr_description.name().clone(),
});
}
self.behaviors.insert(
bhvr_description.name().clone(),
(bhvr_description, Arc::from(bhvr_creation_fn)),
);
Ok(())
}
pub(crate) const fn behaviors(&self) -> &BTreeMap<ConstString, (BehaviorDescription, Arc<BehaviorCreationFn>)> {
&self.behaviors
}
#[cfg(feature = "std")]
pub fn add_library(&mut self, library: Library) {
self.libraries.push(Arc::new(library));
}
#[cfg(feature = "mock_behavior")]
pub fn add_substitution_rule(&mut self, pattern: &str, rule: SubstitutionRule) -> Result<(), Error> {
self.substitution_rules
.insert(pattern.into(), rule);
Ok(())
}
#[cfg(feature = "mock_behavior")]
#[inline]
pub fn clear_substitution_rules(&mut self) {
self.substitution_rules.clear();
}
pub(crate) fn add_tree_nodes_model_entry(&mut self, key: ConstString, entry: TreeNodesModelEntry) -> Result<(), Error> {
if self.tree_nodes_models.contains_key(&key) {
return Err(Error::AlreadyRegistered { name: key });
}
self.tree_nodes_models.insert(key, entry);
Ok(())
}
pub(crate) const fn tree_nodes_models(&self) -> &BTreeMap<ConstString, TreeNodesModelEntry> {
&self.tree_nodes_models
}
pub fn set_main_tree_id(&mut self, id: &str) {
self.main_tree_id = Some(id.into());
}
pub fn clear_registered_trees(&mut self) {
self.main_tree_id = None;
self.tree_definitions.clear();
}
#[must_use]
pub fn main_tree_id(&self) -> Option<ConstString> {
self.main_tree_id.clone()
}
pub(crate) fn add_tree_defintion(
&mut self,
id: &str,
tree_definition: ConstString,
range: Range<usize>,
) -> Result<(), Error> {
let key: ConstString = id.into();
if let alloc::collections::btree_map::Entry::Vacant(e) = self.tree_definitions.entry(key) {
e.insert((tree_definition, range));
Ok(())
} else {
Err(Error::AlreadyRegistered { name: id.into() })
}
}
#[allow(clippy::option_if_let_else)]
pub(crate) fn fetch_behavior(
&self,
id: &str,
#[cfg(feature = "mock_behavior")] path: &str,
) -> Result<(BehaviorDescription, Box<dyn BehaviorExecution>), Error> {
#[cfg(feature = "mock_behavior")]
{
let mut result: Option<SubstitutionRule> = None;
for (pattern, rule) in &self.substitution_rules {
let sub_patterns = pattern.split('*');
let mut pos = 0_usize;
let mut found = true;
for p in sub_patterns {
if let Some(pattern_pos) = path[pos..].find(p) {
pos = pattern_pos;
} else {
found = false;
break;
}
}
if found {
result = Some(rule.clone());
break;
}
}
if let Some(substitution) = result {
match substitution {
SubstitutionRule::StringRule(id) => {
self.behaviors.get(&id).map_or_else(
|| Err(Error::NotRegistered { name: id }),
|(desc, creation_fn)| {
let bhvr = creation_fn();
Ok((desc.clone(), bhvr))
},
)
}
SubstitutionRule::ConfigRule(mock_behavior_config) => {
self.behaviors.get(id).map_or_else(
|| Err(Error::NotRegistered { name: id.into() }),
|(desc, creation_fn)| {
let old_behavior = creation_fn();
let port_list = old_behavior.static_provided_ports();
let bhvr_fn = MockBehavior::create_fn(mock_behavior_config.clone(), port_list);
Ok((desc.clone(), bhvr_fn()))
},
)
}
}
} else {
self.behaviors.get(id).map_or_else(
|| Err(Error::NotRegistered { name: id.into() }),
|(desc, creation_fn)| {
let bhvr = creation_fn();
Ok((desc.clone(), bhvr))
},
)
}
}
#[cfg(not(feature = "mock_behavior"))]
{
self.behaviors.get(id).map_or_else(
|| Err(Error::NotRegistered { name: id.into() }),
|(desc, creation_fn)| {
let bhvr = creation_fn();
Ok((desc.clone(), bhvr))
},
)
}
}
#[must_use]
pub(crate) fn find_tree_definition(&self, name: &str) -> Option<(ConstString, Range<usize>)> {
self.tree_definitions.get(name).cloned()
}
#[cfg(feature = "std")]
pub fn list_behaviors(&self) {
let iter = self.behaviors.iter();
for (key, _) in iter {
std::println!("{key}");
}
std::println!();
}
#[cfg(feature = "std")]
#[must_use]
pub(crate) const fn libraries(&self) -> &Vec<Arc<Library>> {
&self.libraries
}
#[must_use]
pub fn registered_behavior_trees(&self) -> Vec<ConstString> {
let mut res = Vec::new();
for id in self.tree_definitions.keys() {
res.push(id.clone());
}
res
}
#[must_use]
pub const fn runtime(&self) -> &Runtime {
&self.runtime
}
pub const fn runtime_mut(&mut self) -> &mut Runtime {
&mut self.runtime
}
pub(crate) fn register_enum_tuple(&mut self, key: &str, value: i8) -> Result<(), Error> {
self.runtime.register_enum_tuple(key, value)?;
Ok(())
}
}