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