use lightningcss::media_query::MediaList;
#[cfg(feature = "parse")]
use oxvg_parse::{error::Error, Parse, Parser};
#[cfg(feature = "serialize")]
use oxvg_serialize::{error::PrinterError, Printer, ToValue};
use crate::{atom::Atom, enum_attr};
use super::{
core_attrs::{Angle, Anything, Number, Percentage},
presentation::{LengthOrNumber, LengthPercentage},
transform::SVGTransform,
};
enum_attr!(
#[derive(Default)]
BlendMode {
#[default]
Normal: "normal",
Multiply: "multiply",
ColorBurn: "color-burn",
Screen: "screen",
ColorDodge: "color-dodge",
HardLight: "hard-light",
Overlay: "overlay",
SoftLight: "soft-light",
Darken: "darken",
Lighten: "lighten",
Difference: "difference",
Exclusion: "exclusion",
Hue: "hue",
Saturation: "saturation",
Color: "color",
Luminosity: "luminosity",
}
);
enum_attr!(
CrossOrigin {
Anonymous: "anonymous",
UseCredentials: "use-credentials",
}
);
#[derive(Clone, Debug, PartialEq, Eq)]
#[derive(Default)]
pub struct PreserveAspectRatio {
pub align: PreserveAspectRatioAlign,
pub meet_or_slice: PreserveAspectRatioMeetOrSlice,
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for PreserveAspectRatio {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
let align = PreserveAspectRatioAlign::parse(input)?;
input.skip_whitespace();
let mut result = Self {
align,
meet_or_slice: input
.try_parse(PreserveAspectRatioMeetOrSlice::parse)
.unwrap_or_default(),
};
if result.align == PreserveAspectRatioAlign::None {
result.meet_or_slice = PreserveAspectRatioMeetOrSlice::Meet;
}
Ok(result)
}
}
#[cfg(feature = "serialize")]
impl ToValue for PreserveAspectRatio {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
self.align.write_value(dest)?;
if self.meet_or_slice != PreserveAspectRatioMeetOrSlice::default() {
dest.write_char(' ')?;
self.meet_or_slice.write_value(dest)?;
}
Ok(())
}
}
#[test]
fn preserve_aspect_ratio() {
use oxvg_parse::Parse as _;
assert_eq!(
PreserveAspectRatio::parse_string("none"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::None,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("none slice"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::None,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMinYMin"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMinYMin,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMidYMin meet"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMidYMin,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMidYMin slice"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMidYMin,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Slice
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMaxYMin"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMaxYMin,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMinYMid meet"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMinYMid,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMidYMid slice"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMidYMid,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Slice
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMaxYMid"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMaxYMid,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMinYMax meet"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMinYMax,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMidYMax slice"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMidYMax,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Slice
})
);
assert_eq!(
PreserveAspectRatio::parse_string("xMaxYMax"),
Ok(PreserveAspectRatio {
align: PreserveAspectRatioAlign::XMaxYMax,
meet_or_slice: PreserveAspectRatioMeetOrSlice::Meet
})
);
assert!(PreserveAspectRatio::parse_string("meet").is_err());
assert_eq!(
PreserveAspectRatio::parse_string("xMinYMin unknown"),
Err(Error::ExpectedDone)
);
}
enum_attr!(
#[derive(Default)]
PreserveAspectRatioAlign {
None: "none",
XMinYMin: "xMinYMin",
XMidYMin: "xMidYMin",
XMaxYMin: "xMaxYMin",
XMinYMid: "xMinYMid",
#[default]
XMidYMid: "xMidYMid",
XMaxYMid: "xMaxYMid",
XMinYMax: "xMinYMax",
XMidYMax: "xMidYMax",
XMaxYMax: "xMaxYMax",
}
);
enum_attr!(
#[derive(Default)]
PreserveAspectRatioMeetOrSlice {
#[default]
Meet: "meet",
Slice: "slice",
}
);
enum_attr!(
#[derive(Default)]
Playbackorder {
Forwardonly: "forwardonly",
#[default]
All: "all",
}
);
enum_attr!(
Timelinebegin {
Loadend: "loadend",
Loadbegin: "loadbegin",
}
);
enum_attr!(
#[derive(Default)]
Units {
#[default]
UserSpaceOnUse: "userSpaceOnUse",
ObjectBoundingBox: "objectBoundingBox",
}
);
enum_attr!(
#[derive(Default)]
MarkerUnits {
#[default]
UserSpaceOnUse: "userSpaceOnUse",
StrokeWidth: "strokeWidth",
}
);
enum_attr!(
LengthAdjust {
Spacing: "spacing",
SpacingAndGlyphs: "spacingAndGlyphs",
}
);
enum_attr!(
LinkType {
Alternate: "alternate",
Author: "author",
Bookmark: "bookmark",
Canonical: "canonical",
CompressionDictionary: "compression-dictionary",
DnsPrefetch: "dns-prefetch",
External: "external",
Expect: "expect",
Help: "help",
Icon: "icon",
License: "license",
Manifest: "manifest",
Me: "me",
Modulepreload: "modulepreload",
Next: "next",
Nofollow: "nofollow",
Noopener: "noopener",
Noreferrer: "noreferrer",
Opener: "opener",
Pingback: "pingback",
Preconnect: "preconnect",
Prefetch: "prefetch",
Preload: "preload",
Prerender: "prerender",
Prev: "prev",
PrivacyPolicy: "privacy-policy",
Search: "search",
Stylesheet: "stylesheet",
Tag: "tag",
TermsOfService: "terms-of-service",
}
);
pub type MediaType<'i> = Anything<'i>;
#[derive(Debug, Clone, PartialEq)]
pub struct MediaQueryList<'i>(pub MediaList<'i>);
#[cfg(feature = "parse")]
impl<'input> oxvg_parse::Parse<'input> for MediaQueryList<'input> {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
MediaList::parse(
&mut cssparser_lightningcss::Parser::new(
&mut cssparser_lightningcss::ParserInput::new(input.take_slice()),
),
&lightningcss::stylesheet::ParserOptions::default(),
)
.map(Self)
.map_err(Error::Lightningcss)
}
}
#[cfg(feature = "serialize")]
impl ToValue for MediaQueryList<'_> {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
self.0.write_value(dest)
}
}
#[test]
fn media_query_list() {
use oxvg_parse::Parse as _;
MediaQueryList::parse_string("screen and (width >= 900px)").unwrap();
}
#[derive(Clone, Debug, PartialEq)]
pub enum NumberPercentage {
Number(Number),
Percentage(Percentage),
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for NumberPercentage {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input
.try_parse(|input| Percentage::parse(input).map(Self::Percentage))
.or_else(|_| Number::parse(input).map(Self::Number))
}
}
#[cfg(feature = "serialize")]
impl ToValue for NumberPercentage {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::Number(number) => number.write_value(dest),
Self::Percentage(percentage) => percentage.write_value(dest),
}
}
}
#[test]
fn number_percentage() {
use oxvg_parse::Parse as _;
assert_eq!(
NumberPercentage::parse_string("10"),
Ok(NumberPercentage::Number(10.0))
);
assert_eq!(
NumberPercentage::parse_string("10%"),
Ok(NumberPercentage::Percentage(Percentage(0.1)))
);
assert_eq!(
NumberPercentage::parse_string("10px"),
Err(Error::ExpectedDone)
);
}
#[derive(Clone, Debug, PartialEq)]
pub enum Orient {
Auto,
AutoStartReverse,
Angle(Angle),
Number(Number),
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for Orient {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input
.try_parse(|input| {
input.expect_ident().map_err(|_| ()).and_then(|ident| {
let ident: &str = ident;
Ok(match ident {
"auto" => Self::Auto,
"auto-start-reverse" => Self::AutoStartReverse,
_ => return Err(()),
})
})
})
.or_else(|()| input.try_parse(|input| Angle::parse(input).map(Self::Angle)))
.or_else(|_| Number::parse(input).map(Self::Number))
}
}
#[cfg(feature = "serialize")]
impl ToValue for Orient {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::Auto => dest.write_str("auto"),
Self::AutoStartReverse => dest.write_str("auto-start-reverse"),
Self::Angle(angle) => angle.write_value(dest),
Self::Number(number) => number.write_value(dest),
}
}
}
#[test]
fn orient() {
use oxvg_parse::Parse as _;
assert_eq!(Orient::parse_string("auto"), Ok(Orient::Auto));
assert_eq!(
Orient::parse_string("auto-start-reverse"),
Ok(Orient::AutoStartReverse)
);
assert_eq!(
Orient::parse_string("90deg"),
Ok(Orient::Angle(Angle::Deg(90.0)))
);
assert_eq!(Orient::parse_string("90"), Ok(Orient::Number(90.0)));
assert_eq!(Orient::parse_string("90px"), Err(Error::ExpectedDone));
}
enum_attr!(
Origin {
Default: "default"
}
);
#[derive(Clone, Debug, PartialEq)]
pub enum Radius {
LengthPercentage(LengthPercentage),
Auto,
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for Radius {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input
.try_parse(|input| input.expect_ident_matching("auto").map(|()| Self::Auto))
.or_else(|_| LengthPercentage::parse(input).map(Self::LengthPercentage))
}
}
#[cfg(feature = "serialize")]
impl ToValue for Radius {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::Auto => dest.write_str("auto"),
Self::LengthPercentage(v) => v.write_value(dest),
}
}
}
#[test]
fn radius() {
use oxvg_parse::Parse as _;
assert_eq!(Radius::parse_string("auto"), Ok(Radius::Auto));
assert_eq!(
Radius::parse_string("20px"),
Ok(Radius::LengthPercentage(LengthPercentage(
lightningcss::values::percentage::DimensionPercentage::Dimension(
lightningcss::values::length::LengthValue::Px(20.0)
)
)))
);
}
#[derive(Clone, Debug, PartialEq, Default)]
pub enum ColorProfileName<'input> {
#[default]
Srgb,
Name(Atom<'input>),
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for ColorProfileName<'input> {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
Ok(input
.try_parse(|input| {
let ident = input.expect_ident().map_err(|_| ())?.to_lowercase();
if ident == "srgb" {
Ok(Self::Srgb)
} else {
Err(())
}
})
.unwrap_or_else(|()| Self::Name(input.take_slice().into())))
}
}
#[cfg(feature = "serialize")]
impl ToValue for ColorProfileName<'_> {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::Srgb => dest.write_str("sRGB"),
Self::Name(name) => name.write_value(dest),
}
}
}
#[test]
fn color_profile_name() {
use oxvg_parse::Parse as _;
assert_eq!(
ColorProfileName::parse_string("sRGB"),
Ok(ColorProfileName::Srgb)
);
assert_eq!(
ColorProfileName::parse_string("srgb"),
Ok(ColorProfileName::Srgb)
);
assert_eq!(
ColorProfileName::parse_string("name"),
Ok(ColorProfileName::Name("name".into()))
);
}
enum_attr!(
#[derive(Default)]
RenderingIntent {
#[default]
Auto: "auto",
Perceptual: "perceptual",
RelativeColorimetric: "relative-colorimetric",
Saturation: "saturation",
AbsoluteColorimetric: "absolute-colorimetric",
}
);
#[derive(Clone, Debug, PartialEq)]
pub enum RefX {
LengthOrNumber(LengthOrNumber),
Left,
Center,
Right,
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for RefX {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input
.try_parse(|input| {
let ident: &str = input.expect_ident()?;
Ok(match ident {
"left" => Self::Left,
"center" => Self::Center,
"right" => Self::Right,
received => {
return Err(Error::ExpectedIdent {
expected: "one of `left` `center` `right` or length",
received,
})
}
})
})
.or_else(|e| {
input.skip_whitespace();
LengthOrNumber::parse(input)
.map(Self::LengthOrNumber)
.map_err(|_| e)
})
}
}
#[cfg(feature = "serialize")]
impl ToValue for RefX {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::LengthOrNumber(length_or_number) => length_or_number.write_value(dest),
Self::Left => dest.write_str("left"),
Self::Center => dest.write_str("center"),
Self::Right => dest.write_str("right"),
}
}
}
#[test]
fn ref_x() {
use oxvg_parse::Parse as _;
assert_eq!(RefX::parse_string("left"), Ok(RefX::Left));
assert_eq!(RefX::parse_string("center"), Ok(RefX::Center));
assert_eq!(RefX::parse_string("right"), Ok(RefX::Right));
assert_eq!(
RefX::parse_string("10"),
Ok(RefX::LengthOrNumber(LengthOrNumber::Number(10.0)))
);
assert_eq!(
RefX::parse_string("10px"),
Ok(RefX::LengthOrNumber(LengthOrNumber::Length(
lightningcss::values::length::Length::Value(
lightningcss::values::length::LengthValue::Px(10.0)
)
)))
);
assert_eq!(
RefX::parse_string("top"),
Err(Error::ExpectedIdent {
expected: "one of `left` `center` `right` or length",
received: "top"
})
);
}
#[derive(Clone, Debug, PartialEq)]
pub enum RefY {
LengthOrNumber(LengthOrNumber),
Top,
Center,
Bottom,
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for RefY {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input
.try_parse(|input| {
let ident: &str = input.expect_ident()?;
Ok(match ident {
"top" => Self::Top,
"center" => Self::Center,
"bottom" => Self::Bottom,
received => {
return Err(Error::ExpectedIdent {
expected: "one of `top` `center` `bottom` or length",
received,
})
}
})
})
.or_else(|e| {
input.skip_whitespace();
LengthOrNumber::parse(input)
.map(Self::LengthOrNumber)
.map_err(|_| e)
})
}
}
#[cfg(feature = "serialize")]
impl ToValue for RefY {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::LengthOrNumber(length_or_number) => length_or_number.write_value(dest),
Self::Top => dest.write_str("top"),
Self::Center => dest.write_str("center"),
Self::Bottom => dest.write_str("bottom"),
}
}
}
#[test]
fn ref_y() {
use oxvg_parse::Parse as _;
assert_eq!(RefY::parse_string("top"), Ok(RefY::Top));
assert_eq!(RefY::parse_string("center"), Ok(RefY::Center));
assert_eq!(RefY::parse_string("bottom"), Ok(RefY::Bottom));
assert_eq!(
RefY::parse_string("10"),
Ok(RefY::LengthOrNumber(LengthOrNumber::Number(10.0)))
);
assert_eq!(
RefY::parse_string("10px"),
Ok(RefY::LengthOrNumber(LengthOrNumber::Length(
lightningcss::values::length::Length::Value(
lightningcss::values::length::LengthValue::Px(10.0)
)
)))
);
assert_eq!(
RefY::parse_string("left"),
Err(Error::ExpectedIdent {
expected: "one of `top` `center` `bottom` or length",
received: "left"
})
);
}
enum_attr!(
ReferrerPolicy {
NoReferrer: "no-referrer",
NoReferrerWhenDowngrade: "no-referrer-when-downgrade",
SameOrigin: "same-origin",
Origin: "origin",
StrictOrigin: "strict-origin",
OriginWhenCrossOrigin: "origin-when-cross-origin",
StrictOriginWhenCrossOrigin: "strict-origin-when-cross-origin",
UnsafeUrl: "unsafe-url",
}
);
#[derive(Clone, Debug, PartialEq)]
pub enum Rotate {
Number(Number),
Auto,
AutoReverse,
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for Rotate {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input
.try_parse(|input| {
let ident: &str = input.expect_ident().map_err(|_| ())?;
Ok(match ident {
"auto" => Self::Auto,
"auto-reverse" => Self::AutoReverse,
_ => return Err(()),
})
})
.or_else(|()| {
input.skip_whitespace();
Number::parse(input).map(Self::Number)
})
}
}
#[cfg(feature = "serialize")]
impl ToValue for Rotate {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::Auto => dest.write_str("auto"),
Self::AutoReverse => dest.write_str("auto-reverse"),
Self::Number(number) => number.write_value(dest),
}
}
}
#[test]
fn rotate() {
use oxvg_parse::Parse as _;
assert_eq!(Rotate::parse_string("auto"), Ok(Rotate::Auto));
assert_eq!(
Rotate::parse_string("auto-reverse"),
Ok(Rotate::AutoReverse)
);
assert_eq!(Rotate::parse_string("10"), Ok(Rotate::Number(10.0)));
}
enum_attr!(
SpreadMethod {
Pad: "pad",
Reflect: "reflect",
Repeat: "repeat",
}
);
#[derive(Clone, Debug, PartialEq, Eq, Default)]
pub enum Target<'input> {
#[default]
_Self,
_Parent,
_Top,
_Blank,
XMLName(Atom<'input>),
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for Target<'input> {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
Ok(input
.try_parse(|input| {
let ident: &str = input.expect_ident().map_err(|_| ())?;
Ok(match ident {
"_self" => Self::_Self,
"_parent" => Self::_Parent,
"_top" => Self::_Top,
"_blank" => Self::_Blank,
_ => return Err(()),
})
})
.unwrap_or_else(|()| Self::XMLName(input.take_slice().into())))
}
}
#[cfg(feature = "serialize")]
impl ToValue for Target<'_> {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match self {
Self::_Self => dest.write_str("_self"),
Self::_Parent => dest.write_str("_parent"),
Self::_Top => dest.write_str("_top"),
Self::_Blank => dest.write_str("_blank"),
Self::XMLName(name) => name.write_value(dest),
}
}
}
#[test]
fn target() {
use oxvg_parse::Parse as _;
assert_eq!(Target::parse_string("_self"), Ok(Target::_Self));
assert_eq!(Target::parse_string("_parent"), Ok(Target::_Parent));
assert_eq!(Target::parse_string("_top"), Ok(Target::_Top));
assert_eq!(Target::parse_string("_blank"), Ok(Target::_Blank));
assert_eq!(
Target::parse_string("_Self"),
Ok(Target::XMLName("_Self".into()))
);
assert_eq!(
Target::parse_string("name"),
Ok(Target::XMLName("name".into()))
);
}
enum_attr!(
TextPathMethod {
Align: "align",
Stretch: "stretch",
}
);
enum_attr!(
TextPathSpacing {
Auto: "auto",
Exact: "exact",
}
);
enum_attr!(
TextPathSide {
Left: "left",
Right: "right",
}
);
pub type Transform = SVGTransform;
#[derive(Clone, Debug, PartialEq)]
pub struct TrueFalse(pub bool);
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for TrueFalse {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
let str = input.expect_ident()?;
Ok(Self(match str {
"true" => true,
"false" => false,
received => {
return Err(Error::ExpectedIdent {
expected: "one of `true` `false`",
received,
})
}
}))
}
}
#[cfg(feature = "serialize")]
impl ToValue for TrueFalse {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
if self.0 {
dest.write_str("true")
} else {
dest.write_str("false")
}
}
}
#[test]
fn true_false() {
use oxvg_parse::Parse as _;
assert_eq!(TrueFalse::parse_string("true"), Ok(TrueFalse(true)));
assert_eq!(TrueFalse::parse_string("false"), Ok(TrueFalse(false)));
assert_eq!(
TrueFalse::parse_string("other"),
Err(Error::ExpectedIdent {
expected: "one of `true` `false`",
received: "other"
})
);
}
#[derive(Clone, Debug, PartialEq)]
pub struct TrueFalseUndefined(pub Option<TrueFalse>);
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for TrueFalseUndefined {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input
.try_parse(|input| {
input
.expect_ident_matching("undefined")
.map(|()| Self(None))
})
.or_else(|_| {
TrueFalse::parse(input)
.map(Some)
.map(Self)
.map_err(|e| match e {
Error::ExpectedIdent { received, .. } => Error::ExpectedIdent {
expected: "one of `true` `false` `undefined`",
received,
},
e => e,
})
})
}
}
#[cfg(feature = "serialize")]
impl ToValue for TrueFalseUndefined {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
match &self.0 {
Some(true_false) => true_false.write_value(dest),
None => dest.write_str("undefined"),
}
}
}
#[test]
fn true_false_undefined() {
use oxvg_parse::Parse as _;
assert_eq!(
TrueFalseUndefined::parse_string("true"),
Ok(TrueFalseUndefined(Some(TrueFalse(true))))
);
assert_eq!(
TrueFalseUndefined::parse_string("false"),
Ok(TrueFalseUndefined(Some(TrueFalse(false))))
);
assert_eq!(
TrueFalseUndefined::parse_string("undefined"),
Ok(TrueFalseUndefined(None))
);
assert_eq!(TrueFalseUndefined::parse_string(""), Err(Error::EndOfInput));
assert_eq!(
TrueFalseUndefined::parse_string("other"),
Err(Error::ExpectedIdent {
expected: "one of `true` `false` `undefined`",
received: "other"
})
);
}
enum_attr!(
TypeAnimateTransform {
Translate: "translate",
Scale: "scale",
Rotate: "rotate",
SkewX: "skewX",
SkewY: "skewY",
}
);
#[derive(Clone, Debug, PartialEq)]
pub struct ViewBox {
pub min_x: Number,
pub min_y: Number,
pub width: Number,
pub height: Number,
}
#[cfg(feature = "parse")]
impl<'input> Parse<'input> for ViewBox {
fn parse<'t>(input: &mut Parser<'input>) -> Result<Self, Error<'input>> {
input.skip_whitespace();
let min_x = f32::parse(input)?;
input.skip_whitespace();
input.skip_char(',');
input.skip_whitespace();
let min_y = f32::parse(input)?;
input.skip_whitespace();
input.skip_char(',');
input.skip_whitespace();
let width = f32::parse(input)?;
input.skip_whitespace();
input.skip_char(',');
input.skip_whitespace();
let height = f32::parse(input)?;
input.skip_whitespace();
Ok(Self {
min_x,
min_y,
width,
height,
})
}
}
#[cfg(feature = "serialize")]
impl ToValue for ViewBox {
fn write_value<W>(&self, dest: &mut Printer<W>) -> Result<(), PrinterError>
where
W: std::fmt::Write,
{
self.min_x.write_value(dest)?;
dest.write_char(' ')?;
self.min_y.write_value(dest)?;
dest.write_char(' ')?;
self.width.write_value(dest)?;
dest.write_char(' ')?;
self.height.write_value(dest)
}
}
#[test]
fn view_box() {
use oxvg_parse::Parse as _;
assert_eq!(
ViewBox::parse_string("1 2 3 4"),
Ok(ViewBox {
min_x: 1.0,
min_y: 2.0,
width: 3.0,
height: 4.0
})
);
assert_eq!(ViewBox::parse_string("1 2 3"), Err(Error::InvalidNumber));
assert_eq!(ViewBox::parse_string("1 2 3 4 5"), Err(Error::ExpectedDone));
}
enum_attr!(
ZoomAndPan {
Disable: "disable",
Magnify: "magnify",
}
);