1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use std::fmt::{Debug, Display, Formatter};

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Error {
    FromF32(String),
}

impl Display for Error {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::FromF32(msg) => write!(f, "Failed to convert `f32` to `Position`: {}", msg),
        }
    }
}

impl std::error::Error for Error {}

#[derive(Copy, Clone, PartialEq, Debug, Default)]
pub struct Position {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl Position {
    #[must_use]
    pub fn new(x: f32, y: f32, z: f32) -> Self {
        Self { x, y, z }
    }
}

impl From<Position> for Vec<f32> {
    fn from(value: Position) -> Vec<f32> {
        vec![value.x, value.y, value.z]
    }
}

impl TryFrom<Vec<f32>> for Position {
    type Error = Error;

    fn try_from(value: Vec<f32>) -> std::result::Result<Self, Self::Error> {
        if value.len() != 3 {
            return Err(Self::Error::FromF32(format!(
                "Invalid amount of arguments (expected: 3, actual: {})",
                value.len()
            )));
        }

        Ok(Self {
            x: value[0],
            y: value[1],
            z: value[2],
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    #[allow(clippy::float_cmp)]
    fn position() {
        let pos = Position::new(1.0, 2.0, 3.0);
        assert_eq!(pos.x, 1.0,);
        assert_eq!(pos.y, 2.0);
        assert_eq!(pos.z, 3.0);
    }

    #[test]
    fn position_from() {
        let pos = Position::new(1.0, 2.0, 3.0);
        assert_eq!(Vec::from(pos), vec![1.0, 2.0, 3.0]);
    }

    #[test]
    fn try_from_positiom() {
        let vec = vec![1.0, 2.0, 3.0];
        let position = Position::try_from(vec);
        assert!(position.is_ok());
        assert_eq!(position.unwrap(), Position::new(1.0, 2.0, 3.0));
    }

    #[test]
    fn try_from_positiom_too_little_arguments() {
        let vec = vec![1.0, 2.0];
        let position = Position::try_from(vec);
        assert!(position.is_err());
        assert!(matches!(position.unwrap_err(), Error::FromF32(_)));
    }

    #[test]
    fn try_from_positiom_too_many_arguments() {
        let vec = vec![1.0, 2.0, 3.0, 4.0];
        let position = Position::try_from(vec);
        assert!(position.is_err());
        assert!(matches!(position.unwrap_err(), Error::FromF32(_)));
    }
}