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