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