use std::collections::BTreeMap;
use openstep_plist::{Dictionary, Plist};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use serde_with::{serde_as, OneOrMany};
use crate::{
common::{
Color, CustomParameter, Feature, FeatureClass, FeaturePrefix, InstanceFactors, Kerning,
NodeType, Orientation, SmartComponentSetting, Version,
},
serde::{
bool_true, deserialize_export_type, int_to_bool, is_default, is_false, is_scale_unit,
is_true, scale_unit, SerializeAsTuple,
},
};
pub(crate) fn version_two() -> i32 {
2
}
fn component_alignment_disabled() -> i8 {
-1
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
struct ComponentSerde {
#[serde(default, skip_serializing_if = "Option::is_none")]
alignment: Option<i8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
anchor: Option<String>,
#[serde(default, rename = "anchorTo", skip_serializing_if = "Option::is_none")]
anchor_to: Option<String>,
#[serde(default, skip_serializing_if = "is_default")]
angle: f32,
#[serde(default, skip_serializing_if = "is_default")]
attr: Dictionary,
#[serde(
default,
skip_serializing_if = "is_default",
deserialize_with = "int_to_bool"
)]
locked: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
master_id: Option<String>,
#[serde(default, skip_serializing_if = "is_default")]
orientation: Orientation,
#[serde(rename = "piece", default, skip_serializing_if = "is_default")]
smart_component_location: BTreeMap<String, f32>,
#[serde(default, rename = "pos", skip_serializing_if = "is_default")]
position: (f32, f32),
#[serde(rename = "ref")]
component_glyph: String,
#[serde(default = "scale_unit", skip_serializing_if = "is_scale_unit")]
scale: (f32, f32),
#[serde(default, skip_serializing_if = "is_default")]
slant: (f32, f32),
#[serde(default, rename = "userData", skip_serializing_if = "is_default")]
user_data: Dictionary,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Glyphs3 {
#[serde(
rename = ".appVersion",
default,
skip_serializing_if = "String::is_empty"
)]
pub app_version: String,
#[serde(rename = ".formatVersion", default = "version_two")]
pub format_version: i32,
#[serde(rename = "DisplayStrings", skip_serializing_if = "is_default", default)]
pub display_strings: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub axes: Vec<Axis>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub classes: Vec<FeatureClass>,
#[serde(
default,
rename = "customParameters",
skip_serializing_if = "Vec::is_empty"
)]
pub custom_parameters: Vec<CustomParameter>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub date: String,
#[serde(rename = "familyName")]
pub family_name: String,
#[serde(
default,
rename = "featurePrefixes",
skip_serializing_if = "Vec::is_empty"
)]
pub feature_prefixes: Vec<FeaturePrefix>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub features: Vec<Feature>,
#[serde(rename = "fontMaster", skip_serializing_if = "Vec::is_empty", default)]
pub masters: Vec<Master>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub glyphs: Vec<Glyph>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub instances: Vec<Instance>,
#[serde(rename = "kerningLTR", default, skip_serializing_if = "is_default")]
pub kerning: Kerning,
#[serde(rename = "kerningRTL", default, skip_serializing_if = "is_default")]
pub kerning_rtl: Kerning,
#[serde(
rename = "kerningVertical",
default,
skip_serializing_if = "is_default"
)]
pub kerning_vertical: Kerning,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub metrics: Vec<Metric>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub note: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub numbers: Vec<Number>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub properties: Vec<Property>,
#[serde(default, skip_serializing_if = "is_default")]
pub settings: Settings,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub stems: Vec<Stem>,
#[serde(rename = "unitsPerEm")]
pub units_per_em: i32,
#[serde(rename = "userData", default, skip_serializing_if = "is_default")]
pub user_data: Dictionary,
#[serde(flatten, default, skip_serializing_if = "is_default")]
pub version: Version,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Number {
pub name: String,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Metric {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub filter: Option<String>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none", rename = "type")]
pub metric_type: Option<MetricType>,
}
#[derive(Serialize, Debug, Clone, Copy, PartialEq)]
pub enum MetricType {
#[serde(rename = "ascender")]
Ascender,
#[serde(rename = "cap height")]
CapHeight,
#[serde(rename = "slant height")]
SlantHeight,
#[serde(rename = "x-height")]
XHeight,
#[serde(rename = "midHeight")]
MidHeight,
#[serde(rename = "topHeight")]
TopHeight,
#[serde(rename = "bodyHeight")]
BodyHeight,
#[serde(rename = "descender")]
Descender,
#[serde(rename = "baseline")]
Baseline,
#[serde(rename = "italic angle")]
ItalicAngle,
#[serde(rename = "italic slope")]
ItalicSlope,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Settings {
#[serde(
rename = "disablesAutomaticAlignment",
default,
skip_serializing_if = "is_default"
)]
pub disables_automatic_alignment: bool,
#[serde(
rename = "disablesNiceNames",
default,
skip_serializing_if = "is_default"
)]
pub disables_nice_names: bool,
#[serde(rename = "gridLength", skip_serializing_if = "Option::is_none")]
pub grid_length: Option<i32>,
#[serde(rename = "gridSubDivision", skip_serializing_if = "Option::is_none")]
pub grid_sub_division: Option<i32>,
#[serde(rename = "keyboardIncrement", skip_serializing_if = "Option::is_none")]
pub keyboard_increment: Option<f32>,
#[serde(
rename = "keyboardIncrementBig",
skip_serializing_if = "Option::is_none"
)]
pub keyboard_increment_big: Option<f32>,
#[serde(
rename = "keyboardIncrementHuge",
skip_serializing_if = "Option::is_none"
)]
pub keyboard_increment_huge: Option<f32>,
#[serde(
rename = "keepAlternatesTogether",
default,
skip_serializing_if = "is_default"
)]
pub keep_alternates_together: bool,
#[serde(
rename = "previewRemoveOverlap",
default,
skip_serializing_if = "is_default"
)]
pub preview_remove_overlap: bool,
#[serde(rename = "snapToObjects", default, skip_serializing_if = "is_default")]
pub snap_to_objects: bool,
#[serde(rename = "fontType", default, skip_serializing_if = "is_default")]
pub font_type: String,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Axis {
#[serde(default, skip_serializing_if = "is_default")]
pub hidden: bool,
pub name: String,
pub tag: String,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Master {
#[serde(rename = "axesValues", default, skip_serializing_if = "Vec::is_empty")]
pub axes_values: Vec<f32>,
#[serde(
rename = "customParameters",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub custom_parameters: Vec<CustomParameter>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub guides: Vec<Guide>,
#[serde(rename = "iconName", default, skip_serializing_if = "String::is_empty")]
pub icon_name: String,
pub id: String,
#[serde(
rename = "metricValues",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub metric_values: Vec<MetricValue>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub name: String,
#[serde(
rename = "numberValues",
default,
skip_serializing_if = "Vec::is_empty"
)]
pub number_values: Vec<f32>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub properties: Vec<Property>,
#[serde(rename = "stemValues", default, skip_serializing_if = "Vec::is_empty")]
pub stem_values: Vec<f32>,
#[serde(rename = "tempData", default, skip_serializing_if = "is_default")]
pub temp_data: Dictionary,
#[serde(rename = "userData", default, skip_serializing_if = "is_default")]
pub user_data: Dictionary,
#[serde(default, skip_serializing_if = "is_default")]
pub visible: bool,
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct MetricValue {
#[serde(default, skip_serializing_if = "is_default")]
pub over: f32,
#[serde(default, skip_serializing_if = "is_default")]
pub pos: f32,
}
#[serde_as]
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Glyph {
#[serde(rename = "kernBottom", skip_serializing_if = "Option::is_none")]
pub kern_bottom: Option<String>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub case: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub category: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub color: Option<Color>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub direction: Option<String>,
#[serde(default = "bool_true", skip_serializing_if = "is_true")]
pub export: bool,
#[serde(rename = "glyphname")]
pub name: String,
#[serde(rename = "kernLeft", skip_serializing_if = "Option::is_none")]
pub kern_left: Option<String>,
#[serde(rename = "kernRight", skip_serializing_if = "Option::is_none")]
pub kern_right: Option<String>,
#[serde(rename = "kernTop", skip_serializing_if = "Option::is_none")]
pub kern_top: Option<String>,
#[serde(rename = "lastChange", skip_serializing_if = "Option::is_none")]
pub last_change: Option<String>,
pub layers: Vec<Layer>,
#[serde(default, skip_serializing_if = "is_default")]
pub locked: bool,
#[serde(default, skip_serializing_if = "is_default", rename = "metricBottom")]
pub metric_bottom: Option<String>,
#[serde(default, skip_serializing_if = "is_default", rename = "metricLeft")]
pub metric_left: Option<String>,
#[serde(default, skip_serializing_if = "is_default", rename = "metricRight")]
pub metric_right: Option<String>,
#[serde(default, skip_serializing_if = "is_default", rename = "metricTop")]
pub metric_top: Option<String>,
#[serde(
default,
skip_serializing_if = "is_default",
rename = "metricVertWidth"
)]
pub metric_vert_width: Option<String>,
#[serde(default, skip_serializing_if = "is_default", rename = "metricWidth")]
pub metric_width: Option<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub note: String,
#[serde(
default,
skip_serializing_if = "Vec::is_empty",
rename = "partsSettings"
)]
pub smart_component_settings: Vec<SmartComponentSetting>,
#[serde(default, skip_serializing_if = "is_default")]
pub production: Option<String>,
#[serde(default, skip_serializing_if = "is_default")]
pub script: Option<String>,
#[serde(default, skip_serializing_if = "is_default", rename = "sortName")]
pub sort_name: Option<String>,
#[serde(default, skip_serializing_if = "is_default", rename = "sortNameKeep")]
pub sort_name_keep: Option<String>,
#[serde(default, skip_serializing_if = "is_default", rename = "subCategory")]
pub subcategory: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub tags: Vec<String>,
#[serde_as(
deserialize_as = "OneOrMany<_>",
serialize_as = "SerializeAsTuple<u32>"
)]
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub unicode: Vec<u32>,
#[serde(rename = "userData", default, skip_serializing_if = "is_default")]
pub user_data: Dictionary,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
pub struct Layer {
#[serde(default)]
pub anchors: Vec<Anchor>,
#[serde(default)]
pub annotations: Vec<Dictionary>,
#[serde(rename = "associatedMasterId", default)]
pub associated_master_id: Option<String>,
#[serde(default)]
pub attr: Dictionary,
#[serde(default)]
pub background: Option<Box<Layer>>,
#[serde(rename = "backgroundImage", default)]
pub background_image: Option<BackgroundImage>,
#[serde(default)]
pub color: Option<Color>,
#[serde(default)]
pub guides: Vec<Guide>,
#[serde(default)]
pub hints: Vec<Dictionary>, #[serde(rename = "layerId", default)]
pub layer_id: String,
#[serde(rename = "metricBottom", default)]
pub metric_bottom: Option<String>,
#[serde(rename = "metricLeft", default)]
pub metric_left: Option<String>,
#[serde(rename = "metricRight", default)]
pub metric_right: Option<String>,
#[serde(rename = "metricTop", default)]
pub metric_top: Option<String>,
#[serde(rename = "metricVertOrigin", default)]
pub metric_vert_origin: Option<String>,
#[serde(
rename = "metricVertWidth",
default,
skip_serializing_if = "is_default"
)]
pub metric_vert_width: Option<String>,
#[serde(rename = "metricWidth", default)]
pub metric_width: Option<String>,
#[serde(default)]
pub name: Option<String>,
#[serde(rename = "partSelection", default)]
pub part_selection: BTreeMap<String, u8>,
#[serde(default)]
pub shapes: Vec<Shape>,
#[serde(rename = "userData", default)]
pub user_data: Dictionary,
#[serde(rename = "vertOrigin", default)]
pub vert_origin: Option<f32>,
#[serde(rename = "vertWidth", default)]
pub vert_width: Option<f32>,
#[serde(default = "bool_true")]
pub visible: bool,
#[serde(default)]
pub width: f32,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Anchor {
#[serde(default, skip_serializing_if = "is_default")]
pub locked: bool,
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub orientation: Orientation,
#[serde(default, skip_serializing_if = "is_default")]
pub pos: (f32, f32),
#[serde(default, rename = "userData", skip_serializing_if = "is_default")]
pub user_data: Option<Dictionary>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct BackgroundImage {
#[serde(default, skip_serializing_if = "is_default")]
pub angle: f32,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub crop: Option<(f32, f32, f32, f32)>,
#[serde(rename = "imagePath")]
pub image_path: String,
#[serde(default, skip_serializing_if = "is_default")]
pub locked: bool,
#[serde(default, skip_serializing_if = "is_default")]
pub pos: (f32, f32),
#[serde(default = "scale_unit", skip_serializing_if = "is_scale_unit")]
pub scale: (f32, f32),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, Copy)]
pub enum GuideType {
#[default]
Line,
Circle,
Rect,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default)]
pub struct Guide {
#[serde(default, skip_serializing_if = "is_default")]
pub angle: f32,
#[serde(default, skip_serializing_if = "is_default")]
pub filter: String,
#[serde(default, skip_serializing_if = "is_default")]
pub grid: f32,
#[serde(default, skip_serializing_if = "is_default")]
pub length: f32,
#[serde(default, skip_serializing_if = "is_default", rename = "lockAngle")]
pub lock_angle: bool,
#[serde(default, skip_serializing_if = "is_default")]
pub locked: bool,
#[serde(default, skip_serializing_if = "is_default")]
pub name: String,
#[serde(default, skip_serializing_if = "is_default")]
pub orientation: Orientation,
#[serde(default, skip_serializing_if = "is_default")]
pub pos: (f32, f32),
#[serde(
default,
skip_serializing_if = "is_default",
rename = "showMeasurement"
)]
pub show_measurement: bool,
#[serde(default = "scale_unit", skip_serializing_if = "is_scale_unit")]
pub size: (f32, f32),
#[serde(default, skip_serializing_if = "is_default", rename = "type")]
pub guide_type: GuideType,
#[serde(default, rename = "userData", skip_serializing_if = "Option::is_none")]
pub user_data: Option<Dictionary>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum Shape {
Component(Component),
Path(Path),
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Path {
#[serde(default, skip_serializing_if = "is_default")]
pub attr: Dictionary,
#[serde(default, deserialize_with = "int_to_bool")]
pub closed: bool,
pub nodes: Vec<Node>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Node {
pub x: f32,
pub y: f32,
pub node_type: NodeType,
pub user_data: Option<Dictionary>,
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Component {
pub alignment: i8,
pub anchor: Option<String>,
pub anchor_to: Option<String>,
pub angle: f32,
pub attr: Dictionary,
pub locked: bool,
pub master_id: Option<String>,
pub orientation: Orientation,
pub smart_component_location: BTreeMap<String, f32>,
pub position: (f32, f32),
pub component_glyph: String,
pub scale: (f32, f32),
pub slant: (f32, f32),
pub user_data: Dictionary,
pub alignment_explicit: bool,
}
impl Serialize for Component {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let alignment =
if self.alignment != component_alignment_disabled() || self.alignment_explicit {
Some(self.alignment)
} else {
None
};
ComponentSerde {
alignment,
anchor: self.anchor.clone(),
anchor_to: self.anchor_to.clone(),
angle: self.angle,
attr: self.attr.clone(),
locked: self.locked,
master_id: self.master_id.clone(),
orientation: self.orientation,
smart_component_location: self.smart_component_location.clone(),
position: self.position,
component_glyph: self.component_glyph.clone(),
scale: self.scale,
slant: self.slant,
user_data: self.user_data.clone(),
}
.serialize(serializer)
}
}
impl<'de> Deserialize<'de> for Component {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>,
{
let component = ComponentSerde::deserialize(deserializer)?;
Ok(Component {
alignment: component
.alignment
.unwrap_or_else(component_alignment_disabled),
anchor: component.anchor,
anchor_to: component.anchor_to,
angle: component.angle,
attr: component.attr,
locked: component.locked,
master_id: component.master_id,
orientation: component.orientation,
smart_component_location: component.smart_component_location,
position: component.position,
component_glyph: component.component_glyph,
scale: component.scale,
slant: component.slant,
user_data: component.user_data,
alignment_explicit: component.alignment.is_some(),
})
}
}
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)]
pub struct Instance {
#[serde(default, rename = "axesValues", skip_serializing_if = "is_default")]
pub axes_values: Vec<f32>,
#[serde(
default,
rename = "customParameters",
skip_serializing_if = "Vec::is_empty"
)]
pub custom_parameters: Vec<CustomParameter>,
#[serde(default = "bool_true", skip_serializing_if = "is_true")]
pub exports: bool,
#[serde(
default,
rename = "instanceInterpolations",
skip_serializing_if = "BTreeMap::is_empty"
)]
pub instance_interpolations: BTreeMap<String, InstanceFactors>,
#[serde(default, rename = "isBold", skip_serializing_if = "is_false")]
pub is_bold: bool,
#[serde(default, rename = "isItalic", skip_serializing_if = "is_false")]
pub is_italic: bool,
#[serde(default, rename = "linkStyle", skip_serializing_if = "Option::is_none")]
pub link_style: Option<String>,
#[serde(
default,
rename = "manualInterpolation",
skip_serializing_if = "is_false"
)]
pub manual_interpolation: bool,
pub name: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub properties: Vec<Property>,
#[serde(
rename = "type",
default,
skip_serializing_if = "is_default",
deserialize_with = "deserialize_export_type"
)]
pub export_type: ExportType,
#[serde(default, rename = "userData", skip_serializing_if = "is_default")]
pub user_data: Dictionary,
#[serde(
default,
rename = "weightClass",
skip_serializing_if = "Option::is_none"
)]
pub weight_class: Option<i32>,
#[serde(
default,
rename = "widthClass",
skip_serializing_if = "Option::is_none"
)]
pub width_class: Option<i32>,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, Copy)]
pub enum ExportType {
#[default]
Static,
#[serde(rename = "variable")]
Variable,
#[serde(rename = "icon")]
Icon,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
#[serde(untagged)]
pub enum Property {
SingularProperty {
key: SingularPropertyKey,
value: String,
},
LocalizedProperty {
key: LocalizedPropertyKey,
values: Vec<LocalizedValue>,
},
Junk(Plist),
}
impl Property {
pub fn singular(key: SingularPropertyKey, value: String) -> Self {
Property::SingularProperty { key, value }
}
pub fn localized_with_default(key: LocalizedPropertyKey, value: String) -> Self {
Property::LocalizedProperty {
key,
values: vec![LocalizedValue {
language: "dflt".to_string(),
value,
}],
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub enum LocalizedPropertyKey {
#[serde(rename = "familyNames")]
FamilyNames,
#[serde(rename = "copyrights")]
Copyrights,
#[serde(rename = "designers")]
Designers,
#[serde(rename = "manufacturers")]
Manufacturers,
#[serde(rename = "licenses")]
Licenses,
#[serde(rename = "trademarks")]
Trademarks,
#[serde(rename = "descriptions")]
Descriptions,
#[serde(rename = "sampleTexts")]
SampleTexts,
#[serde(rename = "compatibleFullNames")]
CompatibleFullNames,
#[serde(rename = "styleNames")]
StyleNames,
#[serde(rename = "postscriptFullNames")]
PostscriptFullNames,
}
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq)]
pub enum SingularPropertyKey {
#[serde(rename = "designer")]
Designer,
#[serde(rename = "manufacturer")]
Manufacturer,
#[serde(rename = "designerURL")]
DesignerUrl,
#[serde(rename = "manufacturerURL")]
ManufacturerUrl,
#[serde(rename = "licenseURL")]
LicenseUrl,
#[serde(rename = "postscriptFullName")]
PostscriptFullName,
#[serde(rename = "postscriptFontName")]
PostscriptFontName,
#[serde(rename = "WWSFamilyName")]
WwsFamilyName,
#[serde(rename = "versionString")]
VersionString,
#[serde(rename = "vendorID")]
VendorID,
#[serde(rename = "uniqueID")]
UniqueID,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct LocalizedValue {
pub language: String,
pub value: String,
}
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Stem {
#[serde(default, skip_serializing_if = "is_default")]
pub horizontal: bool,
pub name: String,
}
#[cfg(test)]
mod tests {
use openstep_plist::de::Deserializer;
use super::*;
#[test]
fn test_guide() {
let data = r#"
{
angle = 45.0;
lockAngle = 1;
locked = 0;
orientation = left;
pos = (100.0, 200.0);
showMeasurement = 1;
size = (300.0, 400.0);
type = "Line";
userData = {
customKey = "customValue";
};
}
"#;
let plist = Plist::parse(data).expect("Failed to parse plist");
let deserializer = &mut Deserializer::from_plist(&plist);
let guide: Guide =
serde_path_to_error::deserialize(deserializer).expect("Failed to deserialize Guide");
assert_eq!(guide.angle, 45.0);
assert!(guide.lock_angle);
assert!(!guide.locked);
assert_eq!(guide.orientation, Orientation::Left);
assert_eq!(guide.pos, (100.0, 200.0));
assert!(guide.show_measurement);
assert_eq!(guide.size, (300.0, 400.0));
assert_eq!(guide.guide_type, GuideType::Line);
assert!(guide.user_data.is_some());
}
}