off_rs/geometry/
position.rs

1use std::fmt::{Debug, Display, Formatter};
2
3/// Contains error that occur while performing conversions of the position.
4#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5pub enum Error {
6    FromF32(String),
7}
8
9impl Display for Error {
10    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
11        match self {
12            Self::FromF32(msg) => write!(f, "Failed to convert `f32` to `Position`: {}", msg),
13        }
14    }
15}
16
17impl std::error::Error for Error {}
18
19/// Represents a position in 3D space.
20/// A position contains three floating point numbers, representing the x, y and z coordinates.
21#[derive(Copy, Clone, PartialEq, Debug, Default)]
22pub struct Position {
23    /// The x coordinate.
24    pub x: f32,
25    /// The y coordinate.
26    pub y: f32,
27    /// The z coordinate.
28    pub z: f32,
29}
30
31impl Position {
32    /// Creates a new [`Position`].
33    #[must_use]
34    pub fn new(x: f32, y: f32, z: f32) -> Self {
35        Self { x, y, z }
36    }
37}
38
39impl From<Position> for Vec<f32> {
40    /// Converts a [`Position`] to a [`Vec`] of three [`f32`]s.
41    fn from(value: Position) -> Vec<f32> {
42        vec![value.x, value.y, value.z]
43    }
44}
45
46impl TryFrom<Vec<f32>> for Position {
47    type Error = Error;
48
49    /// Converts a [`Vec`] of three [`f32`]s to a [`Position`].
50    fn try_from(value: Vec<f32>) -> std::result::Result<Self, Self::Error> {
51        if value.len() != 3 {
52            return Err(Self::Error::FromF32(format!(
53                "Invalid amount of arguments (expected: 3, actual: {})",
54                value.len()
55            )));
56        }
57
58        Ok(Self {
59            x: value[0],
60            y: value[1],
61            z: value[2],
62        })
63    }
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test]
71    #[allow(clippy::float_cmp)]
72    fn position() {
73        let pos = Position::new(1.0, 2.0, 3.0);
74        assert_eq!(pos.x, 1.0,);
75        assert_eq!(pos.y, 2.0);
76        assert_eq!(pos.z, 3.0);
77    }
78
79    #[test]
80    fn position_from() {
81        let pos = Position::new(1.0, 2.0, 3.0);
82        assert_eq!(Vec::from(pos), vec![1.0, 2.0, 3.0]);
83    }
84
85    #[test]
86    fn try_from_positiom() {
87        let vec = vec![1.0, 2.0, 3.0];
88        let position = Position::try_from(vec);
89        assert!(position.is_ok());
90        assert_eq!(position.unwrap(), Position::new(1.0, 2.0, 3.0));
91    }
92
93    #[test]
94    fn try_from_positiom_too_little_arguments() {
95        let vec = vec![1.0, 2.0];
96        let position = Position::try_from(vec);
97        assert!(position.is_err());
98        assert!(matches!(position.unwrap_err(), Error::FromF32(_)));
99    }
100
101    #[test]
102    fn try_from_positiom_too_many_arguments() {
103        let vec = vec![1.0, 2.0, 3.0, 4.0];
104        let position = Position::try_from(vec);
105        assert!(position.is_err());
106        assert!(matches!(position.unwrap_err(), Error::FromF32(_)));
107    }
108}