use serde::{Deserialize, Serialize};
use crate::model::graphics::{CtColor, CtVectorG};
use crate::types::{StArray, StId, StLoc};
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct Res {
#[serde(rename = "@BaseLoc")]
pub base_loc: StLoc,
#[serde(rename = "$value", default)]
pub children: Vec<ResChild>,
}
#[derive(Debug, Clone, Deserialize, Serialize)]
pub enum ResChild {
ColorSpaces(ColorSpaces),
DrawParams(DrawParams),
Fonts(Fonts),
MultiMedias(MultiMedias),
CompositeGraphicUnits(CompositeGraphicUnits),
}
impl Res {
pub fn color_spaces(&self) -> impl Iterator<Item = &CtColorSpace> {
self.children
.iter()
.filter_map(|c| match c {
ResChild::ColorSpaces(g) => Some(g),
_ => None,
})
.flat_map(|g| g.color_spaces.iter())
}
pub fn draw_params(&self) -> impl Iterator<Item = &CtDrawParam> {
self.children
.iter()
.filter_map(|c| match c {
ResChild::DrawParams(g) => Some(g),
_ => None,
})
.flat_map(|g| g.draw_params.iter())
}
pub fn fonts(&self) -> impl Iterator<Item = &CtFont> {
self.children
.iter()
.filter_map(|c| match c {
ResChild::Fonts(g) => Some(g),
_ => None,
})
.flat_map(|g| g.fonts.iter())
}
pub fn multi_medias(&self) -> impl Iterator<Item = &CtMultiMedia> {
self.children
.iter()
.filter_map(|c| match c {
ResChild::MultiMedias(g) => Some(g),
_ => None,
})
.flat_map(|g| g.multi_medias.iter())
}
pub fn composite_graphic_units(&self) -> impl Iterator<Item = &CtVectorG> {
self.children
.iter()
.filter_map(|c| match c {
ResChild::CompositeGraphicUnits(g) => Some(g),
_ => None,
})
.flat_map(|g| g.units.iter())
}
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CompositeGraphicUnits {
#[serde(rename = "CompositeGraphicUnit", default)]
pub units: Vec<CtVectorG>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct ColorSpaces {
#[serde(rename = "ColorSpace", default)]
pub color_spaces: Vec<CtColorSpace>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CtColorSpace {
#[serde(rename = "@ID")]
pub id: StId,
#[serde(rename = "@Type")]
pub cs_type: String,
#[serde(rename = "@BitsPerComponent")]
pub bits_per_component: Option<u32>,
#[serde(rename = "@Profile")]
pub profile: Option<StLoc>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct DrawParams {
#[serde(rename = "DrawParam", default)]
pub draw_params: Vec<CtDrawParam>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CtDrawParam {
#[serde(rename = "@ID")]
pub id: StId,
#[serde(rename = "@Relative")]
pub relative: Option<StId>,
#[serde(rename = "@LineWidth")]
pub line_width: Option<f64>,
#[serde(rename = "@Join")]
pub join: Option<String>,
#[serde(rename = "@Cap")]
pub cap: Option<String>,
#[serde(rename = "@MiterLimit")]
pub miter_limit: Option<f64>,
#[serde(rename = "@DashOffset")]
pub dash_offset: Option<f64>,
#[serde(rename = "@DashPattern")]
pub dash_pattern: Option<StArray<f64>>,
#[serde(rename = "FillColor")]
pub fill_color: Option<CtColor>,
#[serde(rename = "StrokeColor")]
pub stroke_color: Option<CtColor>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct Fonts {
#[serde(rename = "Font", default)]
pub fonts: Vec<CtFont>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CtFont {
#[serde(rename = "@ID")]
pub id: StId,
#[serde(rename = "@FontName")]
pub font_name: String,
#[serde(rename = "@FamilyName")]
pub family_name: Option<String>,
#[serde(rename = "@Charset")]
pub charset: Option<String>,
#[serde(rename = "@Italic")]
pub italic: Option<bool>,
#[serde(rename = "@Bold")]
pub bold: Option<bool>,
#[serde(rename = "@Serif")]
pub serif: Option<bool>,
#[serde(rename = "@FixedWidth")]
pub fixed_width: Option<bool>,
#[serde(rename = "FontFile")]
pub font_file: Option<StLoc>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct MultiMedias {
#[serde(rename = "MultiMedia", default)]
pub multi_medias: Vec<CtMultiMedia>,
}
#[derive(Debug, Clone, Default, Deserialize, Serialize)]
pub struct CtMultiMedia {
#[serde(rename = "@ID")]
pub id: StId,
#[serde(rename = "@Type")]
pub media_type: String,
#[serde(rename = "@Format")]
pub format: Option<String>,
#[serde(rename = "MediaFile")]
pub media_file: StLoc,
}
#[cfg(test)]
mod tests {
use super::*;
fn full_res() -> Res {
Res {
base_loc: StLoc::from("Res"),
children: vec![
ResChild::ColorSpaces(ColorSpaces {
color_spaces: vec![CtColorSpace {
id: StId(1),
cs_type: "RGB".into(),
..Default::default()
}],
}),
ResChild::DrawParams(DrawParams {
draw_params: vec![CtDrawParam {
id: StId(2),
..Default::default()
}],
}),
ResChild::Fonts(Fonts {
fonts: vec![CtFont {
id: StId(3),
font_name: "宋体".into(),
..Default::default()
}],
}),
ResChild::MultiMedias(MultiMedias {
multi_medias: vec![CtMultiMedia {
id: StId(4),
media_type: "Image".into(),
media_file: StLoc::from("a.png"),
..Default::default()
}],
}),
ResChild::CompositeGraphicUnits(CompositeGraphicUnits {
units: vec![CtVectorG::default()],
}),
],
}
}
#[test]
fn iterators_flatten_each_group() {
let res = full_res();
assert_eq!(res.color_spaces().count(), 1);
assert_eq!(res.draw_params().count(), 1);
assert_eq!(res.fonts().count(), 1);
assert_eq!(res.multi_medias().count(), 1);
assert_eq!(res.composite_graphic_units().count(), 1);
assert_eq!(res.fonts().next().unwrap().font_name, "宋体");
assert_eq!(res.color_spaces().next().unwrap().cs_type, "RGB");
}
#[test]
fn iterators_skip_when_group_absent() {
let res = Res {
base_loc: StLoc::default(),
children: vec![ResChild::Fonts(Fonts::default())],
};
assert_eq!(res.color_spaces().count(), 0);
assert_eq!(res.draw_params().count(), 0);
assert_eq!(res.multi_medias().count(), 0);
assert_eq!(res.composite_graphic_units().count(), 0);
}
}