Newer
Older
use plotters::prelude::LogScalable;
use serde_derive::{Deserialize, Serialize};
use std::{collections::HashMap, mem};
error::{OpmResult, OpossumError},
///
/// The property system is used for storing node specific parameters (such as focal length, splitting ratio, filter curve, etc ...).
/// Properties have to be created once before they can be set and used.
///
/// ## Example
/// ```rust
/// use opossum::properties::Properties;
/// let mut props = Properties::default();
/// props.create("my float", "my floating point value", None, 3.14.into()).unwrap();
/// props.set("my float", 2.71.into()).unwrap();
/// ```
#[derive(Default, Serialize, Deserialize, Debug, Clone)]
/// Create a new property with the given name.
///
/// # Errors
///
/// This function will return an [`OpossumError`] if a property with the same name was already created before.
pub fn create(
&mut self,
name: &str,
description: &str,
conditions: Option<Vec<PropCondition>>,
value: Proptype,
) -> OpmResult<()> {
let new_property = Property {
prop: value,
description: description.into(),
};
if self.props.insert(name.into(), new_property).is_some() {
Err(OpossumError::Properties(format!(
"property {} already created",
name
)))
/// Set the value of the property with the given name.
///
/// # Errors
///
/// This function will return an [`OpossumError`] if
/// - the property with the given name does not exist (i.e. has not been created before).
/// - property conditions defined during creation are not met.
pub fn set(&mut self, name: &str, value: Proptype) -> OpmResult<()> {
let mut property = self
.get(name)
.ok_or(OpossumError::Properties(format!(
"property {} does not exist",
name
property.set_value(value)?;
self.props.insert(name.into(), property);
Ok(())
pub fn set_internal(&mut self, name: &str, value: Proptype) -> OpmResult<()> {
let mut property = self
.props
.get(name)
.ok_or(OpossumError::Properties(format!(
"property {} does not exist",
name
)))?
.clone();
property.set_value_internal(value)?;
self.props.insert(name.into(), property);
Ok(())
}
/// Returns the iter of this [`Properties`].
pub fn iter(&self) -> std::collections::hash_map::Iter<'_, String, Property> {
self.props.iter()
}
/// Return `true`if a property with the given name exists.
pub fn contains(&self, key: &str) -> bool {
self.props.contains_key(key)
}
/// Return the value of the given property.
///
/// # Errors
///
/// This function will return an error if the property with the given name does not exist.
pub fn get(&self, name: &str) -> OpmResult<&Proptype> {
if let Some(prop) = self.props.get(name) {
Ok(prop.prop())
} else {
Err(OpossumError::Properties(format!(
"property {} does not exist",
name
)))
}
/// Return the value of a boolean property.
///
/// This is convenience function for easier access.
///
/// # Errors
///
/// This function will return an error if the property with the given name does not exist.
pub fn get_bool(&self, name: &str) -> OpmResult<Option<bool>> {
if let Some(property) = self.props.get(name) {
if let Proptype::Bool(value) = property.prop {
Ok(Some(value))
} else {
Err(OpossumError::Other("not a bool property".into()))
}
} else {
Ok(None)
}
/// (optical) Property
///
/// A property consists of the actual value (stored as [`Proptype`]), a description and optionally a list of value conditions
/// (such as "GreaterThan", "NonEmptyString", etc.)
#[derive(Debug, Serialize, Deserialize, Clone)]
#[serde(skip)]
description: String,
#[serde(skip)]
conditions: Option<Vec<PropCondition>>,
/// Returns a reference to the actual property value (expressed as [`Proptype`] prop of this [`Property`].
pub fn prop(&self) -> &Proptype {
&self.prop
}
/// Returns a reference to the description of this [`Property`].
pub fn description(&self) -> &str {
self.description.as_ref()
}
/// Sets the value of this [`Property`].
///
/// # Errors
///
/// This function will return an error if the property conditions are not met.
pub fn set_value(&mut self, prop: Proptype) -> OpmResult<()> {
if let Some(conditions) = &self.conditions {
if conditions.contains(&PropCondition::InternalOnly) {
return Err(OpossumError::Properties(
"property is internally used and public read-only".into(),
));
if mem::discriminant(&self.prop) != mem::discriminant(&prop) {
return Err(OpossumError::Properties("incompatible value types".into()));
}
self.check_conditions(&prop)?;
self.prop = prop;
Ok(())
}
pub fn set_value_internal(&mut self, prop: Proptype) -> OpmResult<()> {
self.check_conditions(&prop)?;
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
Ok(())
}
fn check_conditions(&self, prop: &Proptype) -> OpmResult<()> {
if let Some(conditions) = &self.conditions {
for condition in conditions.iter() {
match condition {
PropCondition::NonEmptyString => {
if let Proptype::String(s) = prop.clone() {
if s.is_empty() {
return Err(OpossumError::Properties(
"string value must not be empty".into(),
));
}
}
}
PropCondition::GreaterThan(limit) => match prop {
Proptype::I32(val) => {
if val.as_f64() <= *limit {
return Err(OpossumError::Properties(format!(
"value must be > {}",
limit
)));
}
}
Proptype::F64(val) => {
if val <= limit {
return Err(OpossumError::Properties(format!(
"value must be > {}",
limit
)));
}
}
_ => {}
},
PropCondition::LessThan(limit) => match prop {
Proptype::I32(val) => {
if val.as_f64() >= *limit {
return Err(OpossumError::Properties(format!(
"value must be < {}",
limit
)));
}
}
Proptype::F64(val) => {
if val >= limit {
return Err(OpossumError::Properties(format!(
"value must be < {}",
limit
)));
}
}
_ => {}
},
PropCondition::GreaterThanEqual(limit) => match prop {
Proptype::I32(val) => {
if val.as_f64() < *limit {
return Err(OpossumError::Properties(format!(
"value must be >= {}",
limit
)));
}
}
Proptype::F64(val) => {
if val < limit {
return Err(OpossumError::Properties(format!(
"value must be >= {}",
limit
)));
}
}
_ => {}
},
PropCondition::LessThanEqual(limit) => match prop {
Proptype::I32(val) => {
if val.as_f64() > *limit {
return Err(OpossumError::Properties(format!(
"value must be <= {}",
limit
)));
}
}
Proptype::F64(val) => {
if val > limit {
return Err(OpossumError::Properties(format!(
"value must be <= {}",
limit
)));
}
}
_ => {}
},
PropCondition::InternalOnly => {}
}
}
}
Ok(())

Udo Eisenbarth
committed
fn from(value: bool) -> Self {

Udo Eisenbarth
committed
}
}

Udo Eisenbarth
committed
fn from(value: f64) -> Self {

Udo Eisenbarth
committed
}
}
impl From<String> for Proptype {

Udo Eisenbarth
committed
fn from(value: String) -> Self {

Udo Eisenbarth
committed
}
}

Udo Eisenbarth
committed
fn from(value: &str) -> Self {
Proptype::String(value.to_string())

Udo Eisenbarth
committed
}
}

Udo Eisenbarth
committed
fn from(value: i32) -> Self {

Udo Eisenbarth
committed
}
}
fn from(value: Uuid) -> Self {
#[derive(Serialize, Deserialize, Debug, Clone)]
String(String),
I32(i32),
F64(f64),
Bool(bool),
LightData(Option<LightData>),
OpticGraph(OpticGraph),
FilterType(FilterType),
SpectrometerType(SpectrometerType),
#[derive(Debug, Clone, PartialEq)]
pub enum PropCondition {
NonEmptyString,
InternalOnly, // DO NOT USE YET (deserialization problems)
GreaterThan(f64),
LessThan(f64),
GreaterThanEqual(f64),
LessThanEqual(f64),
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn property_set_different_type() {
let mut prop = Property {
prop: Proptype::Bool(true),
description: "".into(),
conditions: None,
};
assert!(prop.set_value(Proptype::Bool(false)).is_ok());
assert!(prop.set_value(Proptype::F64(3.14)).is_err());
}
}