rill_patchbay/automaton/
factory.rs1use std::collections::HashMap;
4use std::fmt;
5use std::sync::Arc;
6
7use rill_core::queues::CommandEnum;
8use rill_core::traits::{NodeId, ParamValue, Params};
9use rill_core_actor::ActorRef;
10
11use crate::engine::{BoxedModule, ParameterMapping};
12#[derive(Debug, Clone)]
14pub enum FactoryError {
15 UnknownType(String),
17}
18
19impl fmt::Display for FactoryError {
20 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21 match self {
22 Self::UnknownType(t) => write!(f, "unknown automaton type: {t}"),
23 }
24 }
25}
26#[derive(Debug, Clone)]
28pub struct ServoTarget {
29 pub target_node: NodeId,
31 pub target_param: String,
33 pub mapping: ParameterMapping,
35 pub min: f64,
37 pub max: f64,
39 pub table: Option<Vec<ParamValue>>,
41}
42pub trait AutomatonConstructor: Send + Sync {
44 fn type_name(&self) -> &'static str;
46 fn construct(&self, id: &str, params: &Params, target: &ServoTarget) -> BoxedModule;
48 fn spawn_async(
50 &self,
51 _id: &str,
52 _params: &Params,
53 _target: &ServoTarget,
54 _interval_ms: f64,
55 _command_queue: ActorRef<CommandEnum>,
56 ) -> Option<tokio::task::JoinHandle<()>> {
57 None
58 }
59 fn clone_box(&self) -> Box<dyn AutomatonConstructor>;
61}
62pub struct AutomatonFactory {
64 entries: HashMap<&'static str, Box<dyn AutomatonConstructor>>,
65}
66
67impl AutomatonFactory {
68 pub fn new() -> Self {
70 Self {
71 entries: HashMap::new(),
72 }
73 }
74 pub fn register(&mut self, ctor: impl AutomatonConstructor + 'static) {
76 self.entries.insert(ctor.type_name(), Box::new(ctor));
77 }
78 pub fn register_fn(
80 &mut self,
81 type_name: &'static str,
82 f: impl Fn(&str, &Params, &ServoTarget) -> BoxedModule + Send + Sync + 'static,
83 ) {
84 self.entries
85 .insert(type_name, Box::new(ClosureCtor::new(type_name, f)));
86 }
87 pub fn construct(
89 &self,
90 type_name: &str,
91 id: &str,
92 params: &Params,
93 target: &ServoTarget,
94 ) -> Result<BoxedModule, FactoryError> {
95 self.entries
96 .get(type_name)
97 .ok_or_else(|| FactoryError::UnknownType(type_name.to_string()))
98 .map(|ctor| ctor.construct(id, params, target))
99 }
100 pub fn spawn_async(
102 &self,
103 type_name: &str,
104 id: &str,
105 params: &Params,
106 target: &ServoTarget,
107 interval_ms: f64,
108 command_queue: ActorRef<CommandEnum>,
109 ) -> Result<Option<tokio::task::JoinHandle<()>>, FactoryError> {
110 self.entries
111 .get(type_name)
112 .ok_or_else(|| FactoryError::UnknownType(type_name.to_string()))
113 .map(|ctor| ctor.spawn_async(id, params, target, interval_ms, command_queue))
114 }
115 pub fn contains(&self, type_name: &str) -> bool {
117 self.entries.contains_key(type_name)
118 }
119 pub fn list_types(&self) -> Vec<&'static str> {
121 self.entries.keys().copied().collect()
122 }
123 pub fn len(&self) -> usize {
125 self.entries.len()
126 }
127 pub fn is_empty(&self) -> bool {
129 self.entries.is_empty()
130 }
131}
132
133impl Default for AutomatonFactory {
134 fn default() -> Self {
135 Self::new()
136 }
137}
138
139type ClosureCtorFn = Arc<dyn Fn(&str, &Params, &ServoTarget) -> BoxedModule + Send + Sync>;
140
141struct ClosureCtor {
142 type_name: &'static str,
143 f: ClosureCtorFn,
144}
145
146impl ClosureCtor {
147 fn new(
148 type_name: &'static str,
149 f: impl Fn(&str, &Params, &ServoTarget) -> BoxedModule + Send + Sync + 'static,
150 ) -> Self {
151 Self {
152 type_name,
153 f: Arc::new(f),
154 }
155 }
156}
157
158impl AutomatonConstructor for ClosureCtor {
159 fn type_name(&self) -> &'static str {
160 self.type_name
161 }
162 fn construct(&self, id: &str, params: &Params, target: &ServoTarget) -> BoxedModule {
163 (self.f)(id, params, target)
164 }
165 fn clone_box(&self) -> Box<dyn AutomatonConstructor> {
166 Box::new(ClosureCtor {
167 type_name: self.type_name,
168 f: self.f.clone(),
169 })
170 }
171}