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}
14
15impl VerticalAnchor {
16    /// Resolve this anchor to a world Y coordinate.
17    ///
18    /// Matches vanilla's `VerticalAnchor.resolveY(WorldGenerationContext)`.
19    #[must_use]
20    pub const fn resolve_y(self, min_y: i32, height: 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        }
26    }
27}
28
29impl<'de> Deserialize<'de> for VerticalAnchor {
30    fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
31        #[derive(Deserialize)]
32        #[serde(deny_unknown_fields)]
33        struct Raw {
34            #[serde(default)]
35            absolute: Option<i32>,
36            #[serde(default)]
37            above_bottom: Option<i32>,
38            #[serde(default)]
39            below_top: Option<i32>,
40        }
41        let raw = Raw::deserialize(d)?;
42        match (raw.absolute, raw.above_bottom, raw.below_top) {
43            (Some(y), None, None) => Ok(Self::Absolute(y)),
44            (None, Some(o), None) => Ok(Self::AboveBottom(o)),
45            (None, None, Some(o)) => Ok(Self::BelowTop(o)),
46            (None, None, None) => Err(D::Error::custom(
47                "VerticalAnchor requires exactly one of absolute/above_bottom/below_top",
48            )),
49            _ => Err(D::Error::custom(
50                "VerticalAnchor must have exactly one of absolute/above_bottom/below_top",
51            )),
52        }
53    }
54}