1use crate::ProfileFuncs;
2use pyo3::prelude::*;
3
4use crate::error::TypeError;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7use std::fmt;
8use std::fmt::Display;
9use std::fmt::Formatter;
10
11#[pyclass]
12#[derive(Clone, Debug, Serialize)]
13pub enum EntityType {
14 Feature,
15 Metric,
16}
17
18#[pyclass]
19#[derive(Clone, Serialize, Deserialize, Debug)]
20pub struct IntFeature {
21 pub name: String,
22 pub value: i64,
23}
24
25#[pymethods]
26impl IntFeature {
27 pub fn __str__(&self) -> String {
28 ProfileFuncs::__str__(self)
29 }
30}
31
32impl IntFeature {
33 pub fn to_float(&self) -> f64 {
34 self.value as f64
35 }
36}
37
38#[pyclass]
39#[derive(Clone, Serialize, Deserialize, Debug)]
40pub struct FloatFeature {
41 pub name: String,
42 pub value: f64,
43}
44
45#[pymethods]
46impl FloatFeature {
47 pub fn __str__(&self) -> String {
48 ProfileFuncs::__str__(self)
49 }
50}
51
52#[pyclass]
53#[derive(Clone, Serialize, Deserialize, Debug)]
54pub struct StringFeature {
55 pub name: String,
56 pub value: String,
57}
58
59#[pymethods]
60impl StringFeature {
61 pub fn __str__(&self) -> String {
62 ProfileFuncs::__str__(self)
63 }
64}
65
66impl StringFeature {
67 pub fn to_float(&self, feature_map: &FeatureMap) -> Result<f64, TypeError> {
68 feature_map
69 .features
70 .get(&self.name)
71 .and_then(|feat_map| {
72 feat_map
73 .get(&self.value)
74 .or_else(|| feat_map.get("missing"))
75 })
76 .map(|&val| val as f64)
77 .ok_or(TypeError::MissingStringValueError)
78 }
79}
80
81#[pyclass]
82#[derive(Clone, Serialize, Deserialize, Debug)]
83pub enum Feature {
84 Int(IntFeature),
85 Float(FloatFeature),
86 String(StringFeature),
87}
88
89#[pymethods]
90impl Feature {
91 #[staticmethod]
92 pub fn int(name: String, value: i64) -> Self {
93 Feature::Int(IntFeature { name, value })
94 }
95
96 #[staticmethod]
97 pub fn float(name: String, value: f64) -> Self {
98 Feature::Float(FloatFeature { name, value })
99 }
100
101 #[staticmethod]
102 pub fn string(name: String, value: String) -> Self {
103 Feature::String(StringFeature { name, value })
104 }
105
106 #[staticmethod]
107 pub fn categorical(name: String, value: String) -> Self {
108 Feature::String(StringFeature { name, value })
109 }
110
111 pub fn __str__(&self) -> String {
112 ProfileFuncs::__str__(self)
113 }
114}
115
116impl Feature {
117 pub fn to_float(&self, feature_map: &FeatureMap) -> Result<f64, TypeError> {
118 match self {
119 Feature::Int(feature) => Ok(feature.to_float()),
120 Feature::Float(feature) => Ok(feature.value),
121 Feature::String(feature) => feature.to_float(feature_map),
122 }
123 }
124
125 pub fn name(&self) -> &str {
126 match self {
127 Feature::Int(feature) => &feature.name,
128 Feature::Float(feature) => &feature.name,
129 Feature::String(feature) => &feature.name,
130 }
131 }
132
133 pub fn to_usize(&self, feature_map: &FeatureMap) -> Result<usize, TypeError> {
134 match self {
135 Feature::Int(f) => Ok(f.value as usize),
136 Feature::Float(f) => Ok(f.value as usize),
137 Feature::String(f) => Ok(f.to_float(feature_map)? as usize),
138 }
139 }
140}
141
142impl Display for Feature {
143 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
144 match self {
145 Feature::Int(feature) => write!(f, "{}", feature.value),
146 Feature::Float(feature) => write!(f, "{}", feature.value),
147 Feature::String(feature) => write!(f, "{}", feature.value),
148 }
149 }
150}
151
152#[pyclass]
153#[derive(Clone, Debug, Serialize)]
154pub struct Features {
155 #[pyo3(get)]
156 pub features: Vec<Feature>,
157
158 #[pyo3(get)]
159 pub entity_type: EntityType,
160}
161
162#[pymethods]
163impl Features {
164 #[new]
165 pub fn new(features: Vec<Feature>) -> Self {
166 Features {
167 features,
168 entity_type: EntityType::Feature,
169 }
170 }
171
172 pub fn __str__(&self) -> String {
173 ProfileFuncs::__str__(self)
174 }
175}
176
177impl Features {
178 pub fn iter(&self) -> std::slice::Iter<'_, Feature> {
179 self.features.iter()
180 }
181}
182
183#[pyclass]
184#[derive(Debug, Serialize, Deserialize, Clone, Default, PartialEq)]
185pub struct FeatureMap {
186 #[pyo3(get)]
187 pub features: HashMap<String, HashMap<String, usize>>,
188}
189
190#[pymethods]
191impl FeatureMap {
192 pub fn __str__(&self) -> String {
193 ProfileFuncs::__str__(self)
195 }
196}
197
198#[pyclass]
199#[derive(Clone, Serialize, Debug)]
200pub struct Metric {
201 pub name: String,
202 pub value: f64,
203}
204
205#[pymethods]
206impl Metric {
207 #[new]
208 pub fn new(name: String, value: f64) -> Self {
209 Metric { name, value }
210 }
211 pub fn __str__(&self) -> String {
212 ProfileFuncs::__str__(self)
213 }
214}
215
216#[pyclass]
217#[derive(Clone, Serialize, Debug)]
218pub struct Metrics {
219 #[pyo3(get)]
220 pub metrics: Vec<Metric>,
221
222 #[pyo3(get)]
223 pub entity_type: EntityType,
224}
225
226#[pymethods]
227impl Metrics {
228 #[new]
229 pub fn new(metrics: Vec<Metric>) -> Self {
230 Metrics {
231 metrics,
232 entity_type: EntityType::Metric,
233 }
234 }
235 pub fn __str__(&self) -> String {
236 ProfileFuncs::__str__(self)
237 }
238}
239
240impl Metrics {
241 pub fn iter(&self) -> std::slice::Iter<'_, Metric> {
242 self.metrics.iter()
243 }
244}
245
246#[derive(Debug)]
247pub enum QueueItem {
248 Features(Features),
249 Metrics(Metrics),
250}
251
252impl QueueItem {
253 pub fn from_py_entity(entity: &Bound<'_, PyAny>) -> Result<Self, TypeError> {
255 let entity_type = entity.getattr("entity_type")?.extract::<EntityType>()?;
256
257 match entity_type {
258 EntityType::Feature => {
259 let features = entity.extract::<Features>()?;
260 Ok(QueueItem::Features(features))
261 }
262 EntityType::Metric => {
263 let metrics = entity.extract::<Metrics>()?;
264 Ok(QueueItem::Metrics(metrics))
265 }
266 }
267 }
268}
269
270pub trait QueueExt: Send + Sync {
271 fn metrics(&self) -> &Vec<Metric>;
272 fn features(&self) -> &Vec<Feature>;
273}
274
275impl QueueExt for Features {
276 fn metrics(&self) -> &Vec<Metric> {
277 static EMPTY: Vec<Metric> = Vec::new();
280 &EMPTY
281 }
282
283 fn features(&self) -> &Vec<Feature> {
284 &self.features
285 }
286}
287
288impl QueueExt for Metrics {
289 fn metrics(&self) -> &Vec<Metric> {
290 &self.metrics
291 }
292
293 fn features(&self) -> &Vec<Feature> {
294 static EMPTY: Vec<Feature> = Vec::new();
297 &EMPTY
298 }
299}