Skip to main content

rill_core/traits/
mod.rs

1//! # Core Traits for Rill
2//!
3//! This module defines the fundamental traits that form the backbone
4//! of the Rill ecosystem.
5
6/// Action trait and types for node‑level commands.
7pub mod action;
8/// Algorithm trait and action contexts.
9pub mod algorithm;
10/// BufferView trait for backend-specific ring buffer access.
11pub mod buffer_view;
12mod error;
13/// Core node trait (`Node`) and related types.
14pub mod node;
15/// Parameter types and IDs (`ParameterId`, `ParamValue`, `ParamType`, etc.).
16pub mod param;
17/// ParameterWrite trait — polymorphic control interface for DSP engines.
18pub mod parameter_write;
19/// Port types and identifiers (`PortId`, `PortDirection`, `PortType`).
20pub mod port;
21/// Processing traits (`Processable`, `Processor`, `Source`, `Sink`).
22pub mod processable;
23/// Rack archetype — modular processing unit (Eurorack case).
24pub mod rack;
25/// Router trait for signal fan‑out / fan‑in.
26pub mod router;
27
28// Re-export all public items
29pub use action::*;
30pub use algorithm::*;
31pub use buffer_view::*;
32pub use error::*;
33pub use node::*;
34pub use param::*;
35pub use parameter_write::*;
36pub use port::*;
37pub use processable::*;
38pub use rack::*;
39pub use router::*;
40
41// ============================================================================
42// Common Type Aliases
43// ============================================================================
44
45/// Default block size for signal processing
46pub const DEFAULT_BLOCK_SIZE: usize = 64;
47
48/// Type alias for a mono signal block
49pub type MonoBlock<T, const BUF_SIZE: usize> = [T; BUF_SIZE];
50
51/// Type alias for a stereo signal block (left, right)
52pub type StereoBlock<T, const BUF_SIZE: usize> = [MonoBlock<T, BUF_SIZE>; 2];
53
54/// Type alias for a control signal value
55pub type ControlValue<T> = T;
56
57// ============================================================================
58// Prelude - Convenient imports for common use
59// ============================================================================
60
61/// Prelude module for convenient importing of common traits and types
62pub mod prelude {
63    // Re-export from parent modules
64    pub use super::{
65        // Core traits
66        BufferView,
67        Eurorack,
68        Node,
69        NodeCategory,
70        // Node types
71        NodeId,
72        NodeMetadata,
73        NodeState,
74
75        NodeTypeId,
76        ParamMetadata,
77
78        ParamRange,
79        ParamType,
80        ParamValue,
81        ParameterError,
82
83        // Parameter handling
84        ParameterId,
85        ParameterResult,
86        ParameterWrite,
87        Port,
88
89        PortDirection,
90        // Ports
91        PortId,
92        PortType,
93        ProcessError,
94        // Error types
95        ProcessResult,
96        Processor,
97        Router,
98        Sink,
99
100        Source,
101        // Constants
102        DEFAULT_BLOCK_SIZE,
103    };
104
105    // Re-export Transcendental from math module for convenience
106    pub use crate::math::Transcendental;
107}
108
109// ============================================================================
110// Common Helper Traits
111// ============================================================================
112
113/// Trait for types that can be converted to/from `ParamValue`
114pub trait IntoParamValue: Sized {
115    /// Convert this value into a `ParamValue`
116    fn into_param_value(self) -> ParamValue;
117
118    /// Try to convert a `ParamValue` back into this type
119    fn from_param_value(value: ParamValue) -> Option<Self>;
120}
121
122impl IntoParamValue for f32 {
123    fn into_param_value(self) -> ParamValue {
124        ParamValue::Float(self)
125    }
126
127    fn from_param_value(value: ParamValue) -> Option<Self> {
128        value.as_f32()
129    }
130}
131
132impl IntoParamValue for i32 {
133    fn into_param_value(self) -> ParamValue {
134        ParamValue::Int(self)
135    }
136
137    fn from_param_value(value: ParamValue) -> Option<Self> {
138        value.as_i32()
139    }
140}
141
142impl IntoParamValue for bool {
143    fn into_param_value(self) -> ParamValue {
144        ParamValue::Bool(self)
145    }
146
147    fn from_param_value(value: ParamValue) -> Option<Self> {
148        value.as_bool()
149    }
150}
151
152impl IntoParamValue for String {
153    fn into_param_value(self) -> ParamValue {
154        ParamValue::String(self)
155    }
156
157    fn from_param_value(value: ParamValue) -> Option<Self> {
158        match value {
159            ParamValue::String(s) => Some(s),
160            ParamValue::Choice(s) => Some(s),
161            _ => None,
162        }
163    }
164}
165
166// ============================================================================
167// Blanket Implementations
168// ============================================================================
169
170/// Helper trait for downcasting to concrete types
171pub trait AsAny: 'static {
172    /// Convert to `&dyn std::any::Any`
173    fn as_any(&self) -> &dyn std::any::Any;
174
175    /// Convert to `&mut dyn std::any::Any`
176    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
177}
178
179impl<T: 'static> AsAny for T {
180    fn as_any(&self) -> &dyn std::any::Any {
181        self
182    }
183
184    fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
185        self
186    }
187}
188
189// ============================================================================
190// Tests
191// ============================================================================
192
193#[cfg(test)]
194mod tests {
195    use super::prelude::*;
196    use crate::traits::IntoParamValue;
197
198    #[test]
199    fn test_prelude_imports() {
200        // Verify that all expected types are accessible
201        let _node_id = NodeId(0);
202        let _port_id = PortId::signal_in(_node_id, 0);
203        let _param_id = ParameterId::new("test").unwrap();
204
205        // Test IntoParamValue
206        let f: f32 = 42.0;
207        let pv = f.into_param_value();
208        assert_eq!(pv.as_f32(), Some(42.0));
209
210        let back = f32::from_param_value(pv);
211        assert_eq!(back, Some(42.0));
212    }
213
214    #[test]
215    fn test_param_value_conversions() {
216        let f = ParamValue::Float(42.0);
217        assert_eq!(f.as_f32(), Some(42.0));
218        assert_eq!(f.as_i32(), Some(42));
219        assert_eq!(f.as_bool(), Some(true));
220
221        let i = ParamValue::Int(0);
222        assert_eq!(i.as_f32(), Some(0.0));
223        assert_eq!(i.as_i32(), Some(0));
224        assert_eq!(i.as_bool(), Some(false));
225
226        let b = ParamValue::Bool(true);
227        assert_eq!(b.as_f32(), Some(1.0));
228        assert_eq!(b.as_i32(), Some(1));
229        assert_eq!(b.as_bool(), Some(true));
230    }
231
232    #[test]
233    fn test_parameter_id_validation() {
234        assert!(ParameterId::new("gain").is_ok());
235        assert!(ParameterId::new("cutoff_freq").is_ok());
236        assert!(ParameterId::new("delay_time_2").is_ok());
237
238        assert!(ParameterId::new("").is_err());
239        assert!(ParameterId::new("1gain").is_err());
240        assert!(ParameterId::new("_gain").is_err());
241        assert!(ParameterId::new("gain.value").is_err());
242    }
243
244    #[test]
245    fn test_port_id_creation() {
246        let node = NodeId(42);
247
248        let signal_in = PortId::signal_in(node, 0);
249        assert_eq!(signal_in.node_id(), node);
250        assert_eq!(signal_in.port_type(), PortType::Signal);
251        assert_eq!(signal_in.direction(), PortDirection::Input);
252        assert_eq!(signal_in.index(), 0);
253        assert!(signal_in.is_input());
254        assert!(signal_in.is_signal());
255
256        let clock_out = PortId::clock_out(node, 0);
257        assert_eq!(clock_out.port_type(), PortType::Clock);
258        assert!(clock_out.is_output());
259        assert!(clock_out.is_clock());
260
261        let feedback_in = PortId::feedback_in(node, 0);
262        assert_eq!(feedback_in.port_type(), PortType::Feedback);
263        assert!(feedback_in.is_input());
264        assert!(feedback_in.is_feedback());
265    }
266
267    #[test]
268    fn test_node_metadata() {
269        let metadata = NodeMetadata {
270            name: "TestNode".to_string(),
271            type_name: None,
272            category: NodeCategory::Processor,
273            description: "A test node".to_string(),
274            author: "Rill".to_string(),
275            version: "1.0".to_string(),
276            signal_inputs: 2,
277            signal_outputs: 2,
278            control_inputs: 1,
279            control_outputs: 0,
280            clock_inputs: 1,
281            clock_outputs: 0,
282            feedback_ports: 0,
283            parameters: vec![],
284        };
285
286        assert_eq!(metadata.name, "TestNode");
287        assert_eq!(metadata.category, NodeCategory::Processor);
288        assert_eq!(metadata.signal_inputs, 2);
289    }
290
291    #[test]
292    fn test_param_range() {
293        let range = ParamRange::new().with_min(0.0).with_max(1.0).with_step(0.1);
294
295        assert!(range.contains(0.5));
296        assert!(!range.contains(1.5));
297        assert_eq!(range.clamp(1.5), 1.0);
298        assert_eq!(range.clamp(-0.5), 0.0);
299    }
300
301    #[test]
302    fn test_default_block_size() {
303        assert_eq!(DEFAULT_BLOCK_SIZE, 64);
304    }
305}