Skip to main content

steel_utils/value_providers/
vertical_anchor.rs

1use serde::{Deserialize, Deserializer, de::Error as _};
2
3/// A vertical anchor resolving to a world Y coordinate given the dimension
4/// bounds.
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub enum VerticalAnchor {
7    /// Absolute Y coordinate.
8    Absolute(i32),
9    /// `min_y + offset`.
10    AboveBottom(i32),
11    /// `min_y + height - 1 - offset` (i.e. `max_y - offset`).
12    BelowTop(i32),
13    /// `sea_level + offset`.
14    RelativeToSeaLevel(i32),
15}
16
17impl VerticalAnchor {
18    /// Resolve this anchor to a world Y coordinate.
19    #[must_use]
20    pub const fn resolve_y(self, min_y: i32, height: i32, sea_level: i32) -> i32 {
21        match self {
22            Self::Absolute(y) => y,
23            Self::AboveBottom(offset) => min_y + offset,
24            Self::BelowTop(offset) => min_y + height - 1 - offset,
25            Self::RelativeToSeaLevel(offset) => sea_level + offset,
26        }
27    }
28}
29
30impl<'de> Deserialize<'de> for VerticalAnchor {
31    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
32        #[derive(Deserialize)]
33        #[serde(deny_unknown_fields)]
34        struct Raw {
35            #[serde(default)]
36            absolute: Option<i32>,
37            #[serde(default)]
38            above_bottom: Option<i32>,
39            #[serde(default)]
40            below_top: Option<i32>,
41            #[serde(default)]
42            relative_to_sea_level: Option<i32>,
43        }
44        let raw = Raw::deserialize(d)?;
45        match (
46            raw.absolute,
47            raw.above_bottom,
48            raw.below_top,
49            raw.relative_to_sea_level,
50        ) {
51            (Some(y), None, None, None) => Ok(Self::Absolute(y)),
52            (None, Some(o), None, None) => Ok(Self::AboveBottom(o)),
53            (None, None, Some(o), None) => Ok(Self::BelowTop(o)),
54            (None, None, None, Some(o)) => Ok(Self::RelativeToSeaLevel(o)),
55            (None, None, None, None) => Err(D::Error::custom(
56                "VerticalAnchor requires exactly one of absolute/above_bottom/below_top/relative_to_sea_level",
57            )),
58            _ => Err(D::Error::custom(
59                "VerticalAnchor must have exactly one of absolute/above_bottom/below_top/relative_to_sea_level",
60            )),
61        }
62    }
63}