use super::simpletypes::{
AdjAngle, AdjCoordinate, GeomGuideFormula, GeomGuideName, PathFillMode, PositiveCoordinate, ShapeType,
TextShapeType,
};
use crate::{
error::{MissingAttributeError, MissingChildNodeError, NotGroupMemberError},
xml::{parse_xml_bool, XmlNode},
xsdtypes::{XsdChoice, XsdType},
};
use std::error::Error;
pub type Result<T> = ::std::result::Result<T, Box<dyn Error>>;
#[derive(Debug, Clone, PartialEq)]
pub struct GeomRect {
pub left: AdjCoordinate,
pub top: AdjCoordinate,
pub right: AdjCoordinate,
pub bottom: AdjCoordinate,
}
impl GeomRect {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut left = None;
let mut top = None;
let mut right = None;
let mut bottom = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"l" => left = Some(value.parse()?),
"t" => top = Some(value.parse()?),
"r" => right = Some(value.parse()?),
"b" => bottom = Some(value.parse()?),
_ => (),
}
}
let left = left.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "l"))?;
let top = top.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "t"))?;
let right = right.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "r"))?;
let bottom = bottom.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "b"))?;
Ok(Self {
left,
top,
right,
bottom,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PolarAdjustHandle {
pub guide_reference_radial: Option<GeomGuideName>,
pub guide_reference_angle: Option<GeomGuideName>,
pub min_radial: Option<AdjCoordinate>,
pub max_radial: Option<AdjCoordinate>,
pub min_angle: Option<AdjAngle>,
pub max_angle: Option<AdjAngle>,
pub position: AdjPoint2D,
}
impl PolarAdjustHandle {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut guide_reference_radial = None;
let mut guide_reference_angle = None;
let mut min_radial = None;
let mut max_radial = None;
let mut min_angle = None;
let mut max_angle = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"gdRefR" => guide_reference_radial = Some(value.clone()),
"gdRefAng" => guide_reference_angle = Some(value.clone()),
"minR" => min_radial = Some(value.parse()?),
"maxR" => max_radial = Some(value.parse()?),
"minAng" => min_angle = Some(value.parse()?),
"maxAng" => max_angle = Some(value.parse()?),
_ => (),
}
}
let position = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "pos")
.ok_or_else(|| Box::<dyn Error>::from(MissingChildNodeError::new(xml_node.name.clone(), "pos")))
.and_then(AdjPoint2D::from_xml_element)?;
Ok(Self {
guide_reference_radial,
guide_reference_angle,
min_radial,
max_radial,
min_angle,
max_angle,
position,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct XYAdjustHandle {
pub guide_reference_x: Option<GeomGuideName>,
pub guide_reference_y: Option<GeomGuideName>,
pub min_x: Option<AdjCoordinate>,
pub max_x: Option<AdjCoordinate>,
pub min_y: Option<AdjCoordinate>,
pub max_y: Option<AdjCoordinate>,
pub position: AdjPoint2D,
}
impl XYAdjustHandle {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut guide_reference_x = None;
let mut guide_reference_y = None;
let mut min_x = None;
let mut max_x = None;
let mut min_y = None;
let mut max_y = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"gdRefX" => guide_reference_x = Some(value.clone()),
"gdRefY" => guide_reference_y = Some(value.clone()),
"minX" => min_x = Some(value.parse()?),
"maxX" => max_x = Some(value.parse()?),
"minY" => min_y = Some(value.parse()?),
"maxY" => max_y = Some(value.parse()?),
_ => (),
}
}
let position = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "pos")
.ok_or_else(|| Box::<dyn Error>::from(MissingChildNodeError::new(xml_node.name.clone(), "pos")))
.and_then(AdjPoint2D::from_xml_element)?;
Ok(Self {
guide_reference_x,
guide_reference_y,
min_x,
max_x,
min_y,
max_y,
position,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum AdjustHandle {
XY(Box<XYAdjustHandle>),
Polar(Box<PolarAdjustHandle>),
}
impl XsdType for AdjustHandle {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"ahXY" => Ok(AdjustHandle::XY(Box::new(XYAdjustHandle::from_xml_element(xml_node)?))),
"ahPolar" => Ok(AdjustHandle::Polar(Box::new(PolarAdjustHandle::from_xml_element(
xml_node,
)?))),
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "AdjustHandle").into()),
}
}
}
impl XsdChoice for AdjustHandle {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"ahXY" | "ahPolar" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct AdjPoint2D {
pub x: AdjCoordinate,
pub y: AdjCoordinate,
}
impl AdjPoint2D {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut x = None;
let mut y = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"x" => x = Some(value.parse()?),
"y" => y = Some(value.parse()?),
_ => (),
}
}
let x = x.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "x"))?;
let y = y.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "y"))?;
Ok(Self { x, y })
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Path2DArcTo {
pub width_radius: AdjCoordinate,
pub height_radius: AdjCoordinate,
pub start_angle: AdjAngle,
pub swing_angle: AdjAngle,
}
impl Path2DArcTo {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut width_radius = None;
let mut height_radius = None;
let mut start_angle = None;
let mut swing_angle = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"wR" => width_radius = Some(value.parse()?),
"hR" => height_radius = Some(value.parse()?),
"stAng" => start_angle = Some(value.parse()?),
"swAng" => swing_angle = Some(value.parse()?),
_ => (),
}
}
let width_radius = width_radius.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "wR"))?;
let height_radius = height_radius.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "hR"))?;
let start_angle = start_angle.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "stAng"))?;
let swing_angle = swing_angle.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "swAng"))?;
Ok(Self {
width_radius,
height_radius,
start_angle,
swing_angle,
})
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct Path2D {
pub width: Option<PositiveCoordinate>,
pub height: Option<PositiveCoordinate>,
pub fill_mode: Option<PathFillMode>,
pub stroke: Option<bool>,
pub extrusion_ok: Option<bool>,
pub commands: Vec<Path2DCommand>,
}
impl Path2D {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.attributes
.iter()
.try_fold(Default::default(), |mut instance: Self, (attr, value)| {
match attr.as_ref() {
"w" => instance.width = Some(value.parse()?),
"h" => instance.height = Some(value.parse()?),
"fill" => instance.fill_mode = Some(value.parse()?),
"stroke" => instance.stroke = Some(parse_xml_bool(value)?),
"extrusionOk" => instance.extrusion_ok = Some(parse_xml_bool(value)?),
_ => (),
}
Ok(instance)
})
.and_then(|mut instance| {
instance.commands = xml_node
.child_nodes
.iter()
.filter_map(Path2DCommand::try_from_xml_element)
.collect::<Result<Vec<_>>>()?;
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct GeomGuide {
pub name: GeomGuideName,
pub formula: GeomGuideFormula,
}
impl GeomGuide {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let mut name = None;
let mut formula = None;
for (attr, value) in &xml_node.attributes {
match attr.as_str() {
"name" => name = Some(value.clone()),
"fmla" => formula = Some(value.clone()),
_ => (),
}
}
let name = name.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "name"))?;
let formula = formula.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "fmla"))?;
Ok(Self { name, formula })
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Path2DCommand {
Close,
MoveTo(AdjPoint2D),
LineTo(AdjPoint2D),
ArcTo(Path2DArcTo),
QuadBezierTo(AdjPoint2D, AdjPoint2D),
CubicBezTo(AdjPoint2D, AdjPoint2D, AdjPoint2D),
}
impl XsdType for Path2DCommand {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let get_point_at = |index| {
xml_node
.child_nodes
.get(index)
.ok_or_else(|| Box::<dyn Error>::from(MissingChildNodeError::new(xml_node.name.clone(), "pt")))
.and_then(AdjPoint2D::from_xml_element)
};
match xml_node.local_name() {
"close" => Ok(Path2DCommand::Close),
"moveTo" => Ok(Path2DCommand::LineTo(get_point_at(0)?)),
"lnTo" => Ok(Path2DCommand::LineTo(get_point_at(0)?)),
"arcTo" => Ok(Path2DCommand::ArcTo(Path2DArcTo::from_xml_element(xml_node)?)),
"quadBezTo" => Ok(Path2DCommand::QuadBezierTo(get_point_at(0)?, get_point_at(1)?)),
"cubicBezTo" => Ok(Path2DCommand::CubicBezTo(
get_point_at(0)?,
get_point_at(1)?,
get_point_at(2)?,
)),
_ => Err(Box::new(NotGroupMemberError::new(
xml_node.name.clone(),
"EG_Path2DCommand",
))),
}
}
}
impl XsdChoice for Path2DCommand {
fn is_choice_member<T>(name: T) -> bool
where
T: AsRef<str>,
{
match name.as_ref() {
"close" | "moveTo" | "lnTo" | "arcTo" | "quadBezTo" | "cubicBezTo" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct GeomGuideList(pub Vec<GeomGuide>);
impl GeomGuideList {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
Ok(Self(
xml_node
.child_nodes
.iter()
.filter(|child_node| child_node.local_name() == "gd")
.map(GeomGuide::from_xml_element)
.collect::<Result<Vec<_>>>()?,
))
}
}
#[derive(Default, Debug, Clone, PartialEq)]
pub struct CustomGeometry2D {
pub adjust_value_list: Option<GeomGuideList>,
pub guide_list: Option<GeomGuideList>,
pub adjust_handle_list: Option<Vec<AdjustHandle>>,
pub connection_site_list: Option<Vec<ConnectionSite>>,
pub rect: Option<Box<GeomRect>>,
pub path_list: Vec<Path2D>,
}
impl CustomGeometry2D {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
xml_node
.child_nodes
.iter()
.try_fold(Default::default(), |mut instance: Self, child_node| {
match child_node.local_name() {
"avLst" => instance.adjust_value_list = Some(GeomGuideList::from_xml_element(child_node)?),
"gdLst" => instance.guide_list = Some(GeomGuideList::from_xml_element(child_node)?),
"ahLst" => {
instance.adjust_handle_list = Some(
child_node
.child_nodes
.iter()
.filter_map(AdjustHandle::try_from_xml_element)
.collect::<Result<Vec<_>>>()?,
)
}
"cxnLst" => {
instance.connection_site_list = Some(
child_node
.child_nodes
.iter()
.filter(|cxn_node| cxn_node.local_name() == "cxn")
.map(ConnectionSite::from_xml_element)
.collect::<Result<Vec<_>>>()?,
)
}
"rect" => instance.rect = Some(Box::new(GeomRect::from_xml_element(child_node)?)),
"pathLst" => {
instance.path_list = child_node
.child_nodes
.iter()
.filter(|path_node| path_node.local_name() == "path")
.map(Path2D::from_xml_element)
.collect::<Result<Vec<_>>>()?
}
_ => (),
}
Ok(instance)
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PresetGeometry2D {
pub preset: ShapeType,
pub adjust_value_list: Option<GeomGuideList>,
}
impl PresetGeometry2D {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let preset = xml_node
.attributes
.get("prst")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "prst"))?
.parse()?;
let adjust_value_list = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "avLst")
.map(GeomGuideList::from_xml_element)
.transpose()?;
Ok(Self {
preset,
adjust_value_list,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Geometry {
Custom(Box<CustomGeometry2D>),
Preset(Box<PresetGeometry2D>),
}
impl XsdType for Geometry {
fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
match xml_node.local_name() {
"custGeom" => Ok(Geometry::Custom(Box::new(CustomGeometry2D::from_xml_element(
xml_node,
)?))),
"prstGeom" => Ok(Geometry::Preset(Box::new(PresetGeometry2D::from_xml_element(
xml_node,
)?))),
_ => Err(NotGroupMemberError::new(xml_node.name.clone(), "EG_Geometry").into()),
}
}
}
impl XsdChoice for Geometry {
fn is_choice_member<T: AsRef<str>>(name: T) -> bool {
match name.as_ref() {
"custGeom" | "prstGeom" => true,
_ => false,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PresetTextShape {
pub preset: TextShapeType,
pub adjust_value_list: Option<GeomGuideList>,
}
impl PresetTextShape {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let preset = xml_node
.attributes
.get("prst")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "prst"))?
.parse()?;
let adjust_value_list = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "avLst")
.map(GeomGuideList::from_xml_element)
.transpose()?;
Ok(Self {
preset,
adjust_value_list,
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct ConnectionSite {
pub angle: AdjAngle,
pub position: AdjPoint2D,
}
impl ConnectionSite {
pub fn from_xml_element(xml_node: &XmlNode) -> Result<Self> {
let angle = xml_node
.attributes
.get("ang")
.ok_or_else(|| MissingAttributeError::new(xml_node.name.clone(), "ang"))?
.parse()?;
let position = xml_node
.child_nodes
.iter()
.find(|child_node| child_node.local_name() == "pos")
.ok_or_else(|| Box::<dyn Error>::from(MissingChildNodeError::new(xml_node.name.clone(), "pos")))
.and_then(AdjPoint2D::from_xml_element)?;
Ok(Self { angle, position })
}
}