Skip to main content

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