svgtypes/
viewbox.rs

1// Copyright 2018 the SVG Types Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::Stream;
5
6/// List of possible [`ViewBox`] parsing errors.
7#[derive(Clone, Copy, Debug)]
8pub enum ViewBoxError {
9    /// One of the numbers is invalid.
10    InvalidNumber,
11
12    /// `ViewBox` has a negative or zero size.
13    InvalidSize,
14}
15
16impl std::fmt::Display for ViewBoxError {
17    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
18        match *self {
19            ViewBoxError::InvalidNumber => {
20                write!(f, "viewBox contains an invalid number")
21            }
22            ViewBoxError::InvalidSize => {
23                write!(f, "viewBox has a negative or zero size")
24            }
25        }
26    }
27}
28
29impl std::error::Error for ViewBoxError {
30    fn description(&self) -> &str {
31        "a viewBox parsing error"
32    }
33}
34
35/// Representation of the [`<viewBox>`] type.
36///
37/// [`<viewBox>`]: https://www.w3.org/TR/SVG2/coords.html#ViewBoxAttribute
38#[allow(missing_docs)]
39#[derive(Clone, Copy, PartialEq, Debug)]
40pub struct ViewBox {
41    pub x: f64,
42    pub y: f64,
43    pub w: f64,
44    pub h: f64,
45}
46
47impl ViewBox {
48    /// Creates a new `ViewBox`.
49    pub fn new(x: f64, y: f64, w: f64, h: f64) -> Self {
50        ViewBox { x, y, w, h }
51    }
52}
53
54impl std::str::FromStr for ViewBox {
55    type Err = ViewBoxError;
56
57    fn from_str(text: &str) -> Result<Self, ViewBoxError> {
58        let mut s = Stream::from(text);
59
60        let x = s
61            .parse_list_number()
62            .map_err(|_| ViewBoxError::InvalidNumber)?;
63        let y = s
64            .parse_list_number()
65            .map_err(|_| ViewBoxError::InvalidNumber)?;
66        let w = s
67            .parse_list_number()
68            .map_err(|_| ViewBoxError::InvalidNumber)?;
69        let h = s
70            .parse_list_number()
71            .map_err(|_| ViewBoxError::InvalidNumber)?;
72
73        if w <= 0.0 || h <= 0.0 {
74            return Err(ViewBoxError::InvalidSize);
75        }
76
77        Ok(ViewBox::new(x, y, w, h))
78    }
79}
80
81#[rustfmt::skip]
82#[cfg(test)]
83mod tests {
84    use super::*;
85    use std::str::FromStr;
86
87    macro_rules! test {
88        ($name:ident, $text:expr, $result:expr) => (
89            #[test]
90            fn $name() {
91                let v = ViewBox::from_str($text).unwrap();
92                assert_eq!(v, $result);
93            }
94        )
95    }
96
97    test!(parse_1, "-20 30 100 500", ViewBox::new(-20.0, 30.0, 100.0, 500.0));
98
99    macro_rules! test_err {
100        ($name:ident, $text:expr, $result:expr) => (
101            #[test]
102            fn $name() {
103                assert_eq!(ViewBox::from_str($text).unwrap_err().to_string(), $result);
104            }
105        )
106    }
107
108    test_err!(parse_err_1, "qwe", "viewBox contains an invalid number");
109    test_err!(parse_err_2, "10 20 30 0", "viewBox has a negative or zero size");
110    test_err!(parse_err_3, "10 20 0 40", "viewBox has a negative or zero size");
111    test_err!(parse_err_4, "10 20 0 0", "viewBox has a negative or zero size");
112    test_err!(parse_err_5, "10 20 -30 0", "viewBox has a negative or zero size");
113    test_err!(parse_err_6, "10 20 30 -40", "viewBox has a negative or zero size");
114    test_err!(parse_err_7, "10 20 -30 -40", "viewBox has a negative or zero size");
115}