essentia_core/algorithm/
introspection.rs

1use std::collections::HashMap;
2
3use essentia_sys::ffi;
4
5use crate::data::DataType;
6
7#[derive(Debug, Clone)]
8pub struct Introspection {
9    name: String,
10    category: String,
11    description: String,
12    input_infos: HashMap<String, InputOutputInfo>,
13    output_infos: HashMap<String, InputOutputInfo>,
14    parameter_infos: HashMap<String, ParameterInfo>,
15}
16
17impl Introspection {
18    pub fn from_algorithm_bridge(algorithm_bridge: &ffi::AlgorithmBridge) -> Self {
19        let input_info = algorithm_bridge
20            .get_input_infos()
21            .into_iter()
22            .map(|info| {
23                let info: InputOutputInfo = info.into();
24                (info.name.clone(), info)
25            })
26            .collect();
27
28        let output_info = algorithm_bridge
29            .get_output_infos()
30            .into_iter()
31            .map(|info| {
32                let info: InputOutputInfo = info.into();
33                (info.name.clone(), info)
34            })
35            .collect();
36
37        let parameter_info = algorithm_bridge
38            .get_parameter_infos()
39            .into_iter()
40            .map(|info| {
41                let info: ParameterInfo = info.into();
42                (info.name.clone(), info)
43            })
44            .collect();
45
46        Self {
47            name: algorithm_bridge.get_name(),
48            category: algorithm_bridge.get_category(),
49            description: algorithm_bridge.get_description(),
50            input_infos: input_info,
51            output_infos: output_info,
52            parameter_infos: parameter_info,
53        }
54    }
55
56    pub fn name(&self) -> &str {
57        &self.name
58    }
59    pub fn category(&self) -> &str {
60        &self.category
61    }
62    pub fn description(&self) -> &str {
63        &self.description
64    }
65    pub fn inputs(&self) -> impl Iterator<Item = &InputOutputInfo> {
66        self.input_infos.values()
67    }
68    pub fn outputs(&self) -> impl Iterator<Item = &InputOutputInfo> {
69        self.output_infos.values()
70    }
71    pub fn parameters(&self) -> impl Iterator<Item = &ParameterInfo> {
72        self.parameter_infos.values()
73    }
74
75    pub fn get_parameter(&self, name: &str) -> Option<&ParameterInfo> {
76        self.parameter_infos.get(name)
77    }
78    pub fn get_input(&self, name: &str) -> Option<&InputOutputInfo> {
79        self.input_infos.get(name)
80    }
81    pub fn get_output(&self, name: &str) -> Option<&InputOutputInfo> {
82        self.output_infos.get(name)
83    }
84}
85
86#[derive(Debug, Clone)]
87pub struct InputOutputInfo {
88    name: String,
89    data_type: DataType,
90    description: String,
91}
92
93impl InputOutputInfo {
94    pub fn name(&self) -> &str {
95        &self.name
96    }
97    pub fn input_output_type(&self) -> DataType {
98        self.data_type
99    }
100    pub fn description(&self) -> &str {
101        &self.description
102    }
103}
104
105impl From<ffi::InputOutputInfo> for InputOutputInfo {
106    fn from(value: ffi::InputOutputInfo) -> Self {
107        let data_type = DataType::from(value.data_type);
108
109        InputOutputInfo {
110            name: value.name,
111            data_type,
112            description: value.description,
113        }
114    }
115}
116
117#[derive(Debug, Clone, PartialEq)]
118pub enum Constraint {
119    Any,
120    PositiveReal,
121    NonNegativeReal,
122    IntRange { min: i32, max: i32 },
123    NonNegativeInt,
124    PositiveInt,
125    OneOf(Vec<String>),
126    Custom(String),
127}
128
129impl From<&str> for Constraint {
130    fn from(s: &str) -> Self {
131        if s.is_empty() {
132            return Constraint::Any;
133        }
134        match s {
135            "(0,inf)" => Constraint::PositiveReal,
136            "[0,inf)" => Constraint::NonNegativeReal,
137            s if s.starts_with('{') && s.ends_with('}') => Self::parse_one_of_constraint(s),
138            s if s.starts_with('[') && s.ends_with(']') => {
139                Self::parse_int_range_constraint(s).unwrap_or_else(|| Self::Custom(s.to_string()))
140            }
141            _ => Constraint::Custom(s.to_string()),
142        }
143    }
144}
145
146impl Constraint {
147    fn parse_one_of_constraint(s: &str) -> Self {
148        let inner = &s[1..s.len() - 1];
149        let values = inner.split(',').map(|v| v.trim().to_string()).collect();
150        Self::OneOf(values)
151    }
152    fn parse_int_range_constraint(s: &str) -> Option<Self> {
153        let inner = &s[1..s.len() - 1];
154        let (min_str, max_str) = inner.split_once(',')?;
155        let min = min_str.trim().parse::<i32>().ok()?;
156        let max = max_str.trim().parse::<i32>().ok()?;
157        Some(Self::IntRange { min, max })
158    }
159}
160
161#[derive(Debug, Clone)]
162pub struct ParameterInfo {
163    name: String,
164    data_type: DataType,
165    description: String,
166    constraint: Constraint,
167    default_value: String,
168}
169
170impl ParameterInfo {
171    pub fn name(&self) -> &str {
172        &self.name
173    }
174    pub fn parameter_type(&self) -> DataType {
175        self.data_type
176    }
177    pub fn description(&self) -> &str {
178        &self.description
179    }
180    pub fn constraint(&self) -> &Constraint {
181        &self.constraint
182    }
183    pub fn default_value(&self) -> &str {
184        &self.default_value
185    }
186    pub fn optional(&self) -> bool {
187        !self.default_value.is_empty()
188    }
189}
190
191impl From<ffi::ParameterInfo> for ParameterInfo {
192    fn from(value: ffi::ParameterInfo) -> Self {
193        let data_type = DataType::from(value.data_type);
194
195        ParameterInfo {
196            name: value.name,
197            data_type,
198            description: value.description,
199            constraint: Constraint::from(value.constraint.as_str()),
200            default_value: value.default_value,
201        }
202    }
203}