svgtypes/
directional_position.rs

1// Copyright 2023 the SVG Types Authors
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4use crate::{Error, Length, LengthUnit, Stream};
5
6/// List of all SVG directional positions.
7#[derive(Clone, Copy, PartialEq, Eq, Debug)]
8pub enum DirectionalPosition {
9    /// The `top` position.
10    Top,
11    /// The `center` position.
12    Center,
13    /// The `bottom` position.
14    Bottom,
15    /// The `right` position.
16    Right,
17    /// The `left` position.
18    Left,
19}
20
21impl DirectionalPosition {
22    /// Checks whether the value can be a horizontal position.
23    #[inline]
24    pub fn is_horizontal(&self) -> bool {
25        matches!(
26            self,
27            DirectionalPosition::Center | DirectionalPosition::Left | DirectionalPosition::Right
28        )
29    }
30
31    /// Checks whether the value can be a vertical position.
32    #[inline]
33    pub fn is_vertical(&self) -> bool {
34        matches!(
35            self,
36            DirectionalPosition::Center | DirectionalPosition::Top | DirectionalPosition::Bottom
37        )
38    }
39}
40
41impl From<DirectionalPosition> for Length {
42    fn from(value: DirectionalPosition) -> Self {
43        match value {
44            DirectionalPosition::Left | DirectionalPosition::Top => {
45                Length::new(0.0, LengthUnit::Percent)
46            }
47            DirectionalPosition::Right | DirectionalPosition::Bottom => {
48                Length::new(100.0, LengthUnit::Percent)
49            }
50            DirectionalPosition::Center => Length::new(50.0, LengthUnit::Percent),
51        }
52    }
53}
54
55impl std::str::FromStr for DirectionalPosition {
56    type Err = Error;
57
58    #[inline]
59    fn from_str(text: &str) -> Result<Self, Error> {
60        let mut s = Stream::from(text);
61        let dir_pos = s.parse_directional_position()?;
62
63        if !s.at_end() {
64            return Err(Error::UnexpectedData(s.calc_char_pos()));
65        }
66
67        Ok(dir_pos)
68    }
69}
70
71impl Stream<'_> {
72    /// Parses a directional position [`left`, `center`, `right`, `bottom`, `top`] from the stream.
73    pub fn parse_directional_position(&mut self) -> Result<DirectionalPosition, Error> {
74        self.skip_spaces();
75
76        if self.starts_with(b"left") {
77            self.advance(4);
78            Ok(DirectionalPosition::Left)
79        } else if self.starts_with(b"right") {
80            self.advance(5);
81            Ok(DirectionalPosition::Right)
82        } else if self.starts_with(b"top") {
83            self.advance(3);
84            Ok(DirectionalPosition::Top)
85        } else if self.starts_with(b"bottom") {
86            self.advance(6);
87            Ok(DirectionalPosition::Bottom)
88        } else if self.starts_with(b"center") {
89            self.advance(6);
90            Ok(DirectionalPosition::Center)
91        } else {
92            Err(Error::InvalidString(
93                vec![
94                    self.slice_tail().to_string(),
95                    "left".to_string(),
96                    "right".to_string(),
97                    "top".to_string(),
98                    "bottom".to_string(),
99                    "center".to_string(),
100                ],
101                self.calc_char_pos(),
102            ))
103        }
104    }
105}
106
107#[rustfmt::skip]
108#[cfg(test)]
109mod tests {
110    use super::*;
111    use std::str::FromStr;
112
113    macro_rules! test_p {
114        ($name:ident, $text:expr, $result:expr) => (
115            #[test]
116            fn $name() {
117                assert_eq!(DirectionalPosition::from_str($text).unwrap(), $result);
118            }
119        )
120    }
121
122    test_p!(parse_1,  "left", DirectionalPosition::Left);
123    test_p!(parse_2,  "right", DirectionalPosition::Right);
124    test_p!(parse_3,  "center", DirectionalPosition::Center);
125    test_p!(parse_4,  "top", DirectionalPosition::Top);
126    test_p!(parse_5,  "bottom", DirectionalPosition::Bottom);
127
128    #[test]
129    fn parse_6() {
130        let mut s = Stream::from("left,");
131        assert_eq!(s.parse_directional_position().unwrap(), DirectionalPosition::Left);
132    }
133
134    #[test]
135    fn parse_7() {
136        let mut s = Stream::from("left ,");
137        assert_eq!(s.parse_directional_position().unwrap(), DirectionalPosition::Left);
138    }
139
140    #[test]
141    fn parse_16() {
142        let mut s = Stream::from("left center");
143        assert_eq!(s.parse_directional_position().unwrap(), DirectionalPosition::Left);
144    }
145
146    #[test]
147    fn err_1() {
148        let mut s = Stream::from("something");
149        assert_eq!(s.parse_directional_position().unwrap_err().to_string(),
150                   "expected 'left', 'right', 'top', 'bottom', 'center' not 'something' at position 1");
151    }
152}