use crate::{error, LoadPathPattern, Result};
use indexmap::{IndexMap, IndexSet};
use ommui_string_patterns::{
freetextstring, freetextstring_maybe_empty, idstring,
idstring_maybe_empty,
};
use snafu::{OptionExt, ResultExt};
use std::path::Path;
use uuid::Uuid;
use {chrono, ommui_file_loading};
#[cfg(feature = "filesystem")]
pub mod filesystem;
#[cfg(feature = "client")]
pub mod client;
pub mod handle;
mod loadable_device;
mod storable_device;
pub use self::loadable_device::LoadableDevice;
pub use self::storable_device::StorableDevice;
#[derive(Clone, Debug)]
pub struct Measurement {
pub curves: IndexMap<String, Curve>,
pub metadata: MeasurementMetadata,
pub results: IndexMap<String, Option<f64>>,
}
#[derive(Clone, Debug)]
pub struct Sample {
pub measurements: IndexMap<usize, Measurement>,
pub metadata: SampleMetadata,
}
#[derive(Clone, Debug)]
pub struct Description {
pub device: DeviceDescription,
pub system_profiles: IndexMap<Uuid, ProfileDescription>,
pub user_profiles: IndexMap<Uuid, ProfileDescription>,
pub samplings: IndexMap<String, Sampling>,
pub units: IndexMap<String, UnitDescription>,
pub translations: IndexMap<String, IndexMap<String, String>>,
}
pub type Translation = IndexMap<String, String>;
pub type DirectoryListing = IndexMap<String, DirectoryListingEntry>;
pub type MeasurementResults = IndexMap<String, Option<f64>>;
pub type Icon = String;
impl<T: std::cmp::Eq + std::hash::Hash + std::str::FromStr> LoadPathPattern
for IndexSet<T>
{
fn load_path_pattern(pattern: &str) -> Result<Self> {
Ok(ommui_file_loading::load_directory_listing(pattern)
.context(error::OmmuiFileLoading)?)
}
fn load_path_pattern_from_dir<P: AsRef<Path>>(
dir: P,
pattern: &str,
) -> Result<Self> {
let s = dir.as_ref().to_str().context(error::InvalidFilePath {
file_path: dir.as_ref().to_string_lossy().to_string(),
})?;
let pattern = format!(
"{}/{}",
s.trim_end_matches(|c| c == std::path::MAIN_SEPARATOR),
pattern
);
Self::load_path_pattern(&pattern)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DeviceId {
#[serde(with = "freetextstring")]
pub serial_number: String,
pub device_uuid: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DeviceDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(with = "freetextstring")]
pub manufacturer: String,
#[serde(with = "freetextstring")]
pub model_number: String,
#[serde(with = "freetextstring")]
pub revision: String,
#[serde(with = "idstring")]
pub icon_id: String,
pub uuid: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProfileDescription {
#[serde(with = "idstring")]
pub sampling_id: String,
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_description", with = "freetextstring")]
pub description: String,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub parameters: IndexMap<String, ProfileParameterDescription>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub results: Vec<ResultReference>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub curves: Vec<ProfileCurveDescription>,
pub translatable: bool,
#[serde(default, skip_serializing_if = "Uuid::is_nil")]
pub parent: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProfileCurveDescription {
pub axes: ProfileCoordinateSystem,
#[serde(with = "idstring")]
pub curve_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProfileAxisDescription {
#[serde(with = "idstring")]
pub representation: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ParameterMode {
ChangeableWithDefaultValue,
ChangeableWithRememberedValue,
ChangeableWithFixedValue,
FixedValue,
FixedHiddenValue,
}
impl ParameterMode {
pub fn is_hidden(&self) -> bool {
match *self {
ParameterMode::ChangeableWithDefaultValue
| ParameterMode::ChangeableWithRememberedValue
| ParameterMode::ChangeableWithFixedValue
| ParameterMode::FixedValue => false,
ParameterMode::FixedHiddenValue => true,
}
}
pub fn is_remembered(&self) -> bool {
match *self {
ParameterMode::ChangeableWithRememberedValue => true,
ParameterMode::ChangeableWithDefaultValue
| ParameterMode::ChangeableWithFixedValue
| ParameterMode::FixedValue
| ParameterMode::FixedHiddenValue => false,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase", untagged)]
pub enum Parameter {
Numeric(f64),
Boolean(bool),
Enumeration(String),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum ParameterType {
Numeric,
Boolean,
Enumeration,
}
pub trait HasParameterType {
fn parameter_type(&self) -> ParameterType;
}
impl HasParameterType for Parameter {
fn parameter_type(&self) -> ParameterType {
match self {
Parameter::Numeric(_) => ParameterType::Numeric,
Parameter::Boolean(_) => ParameterType::Boolean,
Parameter::Enumeration(_) => ParameterType::Enumeration,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase", untagged)]
pub enum ProfileParameterDescription {
Numeric(NumericProfileParameterDescription),
Boolean(BooleanProfileParameterDescription),
Enumeration(EnumerationProfileParameterDescription),
}
impl HasParameterType for ProfileParameterDescription {
fn parameter_type(&self) -> ParameterType {
use self::ProfileParameterDescription as D;
match self {
D::Numeric(_) => ParameterType::Numeric,
D::Boolean(_) => ParameterType::Boolean,
D::Enumeration(_) => ParameterType::Enumeration,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct NumericProfileParameterDescription {
pub value: f64,
#[serde(
with = "idstring_maybe_empty",
default,
skip_serializing_if = "String::is_empty"
)]
pub representation: String,
pub mode: ParameterMode,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BooleanProfileParameterDescription {
pub value: bool,
pub mode: ParameterMode,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct EnumerationProfileParameterDescription {
pub value: String,
pub mode: ParameterMode,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct SampleMetadata {
pub profile: Uuid,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub parameters: IndexMap<String, Parameter>,
pub uuid: Uuid,
#[serde(
with = "freetextstring",
default,
skip_serializing_if = "String::is_empty"
)]
pub identifier: String,
pub source: Source,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct MeasurementMetadata {
pub active: bool,
pub timestamp: chrono::DateTime<chrono::offset::Utc>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub properties: IndexMap<String, Parameter>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Sampling {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
pub results: IndexMap<String, ResultDescription>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub curves: IndexMap<String, CurveDescription>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub parameters: IndexMap<String, ParameterDescription>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub properties: IndexMap<String, ParameterDescription>,
#[serde(with = "idstring")]
pub icon_id: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ResultDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub unit: UnitReference,
pub resolution: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct BooleanParameterDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
#[serde(rename = "_valueCaptionTrue", with = "freetextstring")]
pub value_caption_true: String,
#[serde(rename = "_valueCaptionFalse", with = "freetextstring")]
pub value_caption_false: String,
pub affects_sampling_job: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct NumericParameterDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub unit: UnitReference,
pub resolution: f64,
pub maximum: f64,
pub minimum: f64,
pub affects_sampling_job: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct EnumerationParameterDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub enumeration_values: IndexMap<String, EnumerationValueDescription>,
pub affects_sampling_job: bool,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "PascalCase", tag = "type")]
pub enum ParameterDescription {
#[serde(rename_all = "camelCase")]
Numeric(NumericParameterDescription),
#[serde(rename_all = "camelCase")]
Boolean(BooleanParameterDescription),
#[serde(rename_all = "camelCase")]
Enumeration(EnumerationParameterDescription),
}
impl HasParameterType for ParameterDescription {
fn parameter_type(&self) -> ParameterType {
use self::ParameterDescription as D;
match self {
D::Numeric(_) => ParameterType::Numeric,
D::Boolean(_) => ParameterType::Boolean,
D::Enumeration(_) => ParameterType::Enumeration,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct EnumerationValueDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<f64>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Curve {
pub points: Vec<(f64, f64)>,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub markers: IndexMap<String, (f64, f64)>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CurveDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub coordinate_system: CoordinateSystemDescription,
#[serde(default, skip_serializing_if = "IndexMap::is_empty")]
pub markers: IndexMap<String, CurveMarkerDescription>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct Cartesian2dCoordinateSystemDescription {
pub x: AxisDescription,
pub y: AxisDescription,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct PolarCoordinateSystemDescription {
pub angle: AxisDescription,
pub radius: AxisDescription,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "PascalCase", tag = "type")]
pub enum CoordinateSystemDescription {
#[serde(rename_all = "camelCase")]
Cartesian2d(Cartesian2dCoordinateSystemDescription),
#[serde(rename_all = "camelCase")]
Polar(PolarCoordinateSystemDescription),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum CoordinateSystem {
Cartesian2d,
Polar,
}
pub trait HasCoordinateSystem {
fn coordinate_system(&self) -> CoordinateSystem;
}
impl HasCoordinateSystem for CoordinateSystemDescription {
fn coordinate_system(&self) -> CoordinateSystem {
use self::CoordinateSystem as C;
use self::CoordinateSystemDescription as D;
match self {
D::Cartesian2d(_) => C::Cartesian2d,
D::Polar(_) => C::Polar,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase", untagged)]
pub enum ProfileCoordinateSystem {
Cartesian2d(ProfileCartesian2d),
Polar(ProfilePolar),
}
impl HasCoordinateSystem for ProfileCoordinateSystem {
fn coordinate_system(&self) -> CoordinateSystem {
use self::CoordinateSystem as C;
use self::ProfileCoordinateSystem as P;
match self {
P::Cartesian2d(_) => C::Cartesian2d,
P::Polar(_) => C::Polar,
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProfileCartesian2d {
pub x: ProfileAxisDescription,
pub y: ProfileAxisDescription,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ProfilePolar {
pub angle: ProfileAxisDescription,
pub radius: ProfileAxisDescription,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct AxisDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
#[serde(rename = "_shortCaption", with = "freetextstring")]
pub short_caption: String,
pub unit: UnitReference,
pub resolution: f64,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct CurveMarkerDescription {
#[serde(rename = "_caption", with = "freetextstring")]
pub caption: String,
}
#[derive(Default, Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct DirectoryListingEntry {
#[serde(
with = "freetextstring",
default,
skip_serializing_if = "String::is_empty"
)]
pub description: String,
#[serde(
with = "freetextstring_maybe_empty",
default,
skip_serializing_if = "String::is_empty"
)]
pub identifier: String,
#[serde(default, skip_serializing_if = "Uuid::is_nil")]
pub profile: Uuid,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub enum Source {
Measurement,
Demo,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UnitDescription {
pub representations: IndexMap<String, UnitRepresentationDescription>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UnitRepresentationDescription {
#[serde(rename = "_caption", with = "freetextstring_maybe_empty")]
pub caption: String,
#[serde(
rename = "_shortCaption",
with = "freetextstring_maybe_empty"
)]
pub short_caption: String,
pub factor: f64,
pub offset: f64,
pub measurement_system: MeasurementSystem,
}
impl UnitRepresentationDescription {
pub fn raw_to_representation(&self, v: f64) -> f64 {
(v - self.offset) / self.factor
}
pub fn representation_to_raw(&self, v: f64) -> f64 {
v * self.factor + self.offset
}
pub fn raw_to_representation_relative(&self, v: f64) -> f64 {
v / self.factor
}
pub fn representation_to_raw_relative(&self, v: f64) -> f64 {
v * self.factor
}
pub fn decimal_places(&self, resolution: f64) -> usize {
let resolution = self.raw_to_representation_relative(resolution);
let original = format!("{}", resolution);
const MAX_PLACES: usize = 12usize;
for places in 0usize..=MAX_PLACES {
let rounded = format!("{:.*}", places, resolution);
if rounded == original {
return places;
}
}
MAX_PLACES
}
pub fn format(&self, v: f64, resolution: f64) -> String {
format!(
"{:.*}",
self.decimal_places(resolution),
self.raw_to_representation(v)
)
}
}
#[cfg(test)]
mod tests_unit_representation_description {
use super::*;
#[test]
fn decimal_places_factor1() {
let r = UnitRepresentationDescription {
caption: "Hello".to_string(),
short_caption: "World".to_string(),
offset: 0f64,
factor: 1f64,
measurement_system: MeasurementSystem::Any,
};
assert_eq!(r.format(1.234567, 1.0), "1".to_string());
assert_eq!(r.format(1.234567, 0.1), "1.2".to_string());
assert_eq!(r.format(1.234567, 0.2), "1.2".to_string());
assert_eq!(r.format(1.234567, 0.001), "1.235".to_string());
assert_eq!(r.format(1.234567, 0.0001), "1.2346".to_string());
}
#[test]
fn decimal_places_offset0_factor1000() {
let r = UnitRepresentationDescription {
caption: "Hello".to_string(),
short_caption: "World".to_string(),
offset: 0f64,
factor: 1000f64,
measurement_system: MeasurementSystem::Any,
};
assert_eq!(
format!("{:.*}", 12, r.raw_to_representation(5678.9012)),
"5.678901200000".to_string()
);
assert_eq!(r.format(5678.9012, 1000.0), "6".to_string());
assert_eq!(r.format(5678.9012, 100.0), "5.7".to_string());
assert_eq!(r.format(5678.9012, 10.0), "5.68".to_string());
assert_eq!(r.format(5678.9012, 1.0), "5.679".to_string());
assert_eq!(r.format(5678.9012, 0.2), "5.6789".to_string());
assert_eq!(r.format(5678.9012, 0.1), "5.6789".to_string());
assert_eq!(r.format(5678.9012, 0.01), "5.67890".to_string());
assert_eq!(r.format(5678.9012, 0.001), "5.678901".to_string());
assert_eq!(
r.format(5678.9012, 0.0001),
"5.678901200000".to_string()
);
}
#[test]
fn decimal_places_offset2_factor4() {
let r = UnitRepresentationDescription {
caption: "Hello".to_string(),
short_caption: "World".to_string(),
offset: 2f64,
factor: 4f64,
measurement_system: MeasurementSystem::Any,
};
assert_eq!(
format!("{:.*}", 12, r.raw_to_representation(5.6789012)),
"0.919725300000".to_string()
);
assert_eq!(
format!("{:.*}", 12, r.raw_to_representation_relative(1.0)),
"0.250000000000".to_string()
);
assert_eq!(r.format(5.6789012, 1000.0), "1".to_string());
assert_eq!(r.format(5.6789012, 100.0), "1".to_string());
assert_eq!(r.format(5.6789012, 10.0), "0.9".to_string());
assert_eq!(r.format(5.6789012, 1.0), "0.92".to_string());
assert_eq!(r.format(5.6789012, 0.2), "0.92".to_string());
assert_eq!(r.format(5.6789012, 0.1), "0.920".to_string());
assert_eq!(r.format(5.6789012, 0.01), "0.9197".to_string());
assert_eq!(r.format(5.6789012, 0.001), "0.91973".to_string());
assert_eq!(r.format(5.6789012, 0.0001), "0.919725".to_string());
}
}
pub trait GetByUnitReference {
fn get_by_reference(
&self,
reference: &UnitReference,
) -> (
Option<&UnitDescription>,
Option<&UnitRepresentationDescription>,
);
}
impl GetByUnitReference for IndexMap<String, UnitDescription> {
fn get_by_reference(
&self,
reference: &UnitReference,
) -> (
Option<&UnitDescription>,
Option<&UnitRepresentationDescription>,
) {
let unit = self.get(&reference.unit_id);
let representation = unit
.iter()
.filter_map(|unit| {
reference
.representations
.iter()
.filter_map(|representation_id| {
unit.representations.get(representation_id)
})
.next()
})
.next();
(unit, representation)
}
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "PascalCase")]
pub enum MeasurementSystem {
Metric,
Imperial,
Any,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct UnitReference {
#[serde(with = "idstring")]
pub unit_id: String,
pub representations: Vec<String>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct ResultReference {
#[serde(with = "idstring")]
pub result_id: String,
#[serde(with = "idstring")]
pub representation: String,
}
#[cfg(test)]
mod tests {
use serde_json::from_str;
use super::*;
#[test]
fn test_device_id() {
let json = json!({
"serialNumber": "SN1234",
"deviceUuid": "e7ab4013-fadd-440b-9072-9dd88b220b59",
})
.to_string();
let deserialized: DeviceId = from_str(&json).unwrap();
let d = DeviceId {
serial_number: "SN1234".to_string(),
device_uuid: Uuid::parse_str(
"e7ab4013-fadd-440b-9072-9dd88b220b59",
)
.unwrap(),
};
assert_eq!(d, deserialized);
}
#[test]
#[should_panic]
#[allow(unused_variables)]
fn test_device_id_with_invalid_serialnumber() {
let json = json!({
"serialNumber": " SN600",
"deviceUuid": "e7ab4013-fadd-440b-9072-9dd88b220b59",
})
.to_string();
let deserialized: DeviceId = from_str(&json).unwrap();
}
#[test]
#[should_panic]
#[allow(unused_variables)]
fn test_device_id_with_invalid_uuid() {
let json = json!({
"serialNumber": "SN600",
"deviceUuid": "abcdefgh",
})
.to_string();
let deserialized: DeviceId = from_str(&json).unwrap();
}
#[test]
fn test_device_description() {
let json = json!({
"_caption": "My Super Device",
"manufacturer": "My Super Company",
"modelNumber": "SDX1000",
"revision": "v1.0",
"iconId": "myIcon",
"uuid": "2b8fb031-adab-48f4-81ff-479090858909",
})
.to_string();
let deserialized: DeviceDescription = from_str(&json).unwrap();
let d = DeviceDescription {
caption: "My Super Device".to_string(),
manufacturer: "My Super Company".to_owned(),
model_number: "SDX1000".to_owned(),
revision: "v1.0".to_owned(),
icon_id: "myIcon".to_owned(),
uuid: Uuid::parse_str("2b8fb031-adab-48f4-81ff-479090858909")
.unwrap(),
};
assert_eq!(d, deserialized);
}
}