earthmc 2.0.6

Async client for interacting with the EarthMC API
Documentation
use derive_builder::Builder;
use serde::{Serialize, Serializer, ser::SerializeStruct};

use crate::model::location::ChunkCoordinate;

#[derive(Clone, Serialize, Builder)]
#[serde(transparent)]
#[builder(pattern = "owned")]
pub struct NearbyQuery {
    #[builder(default, setter(each = "insert"))]
    values: Vec<NearbyQueryItem>,
}

impl From<NearbyQueryItem> for NearbyQuery {
    fn from(value: NearbyQueryItem) -> Self {
        Self {
            values: vec![value],
        }
    }
}

impl From<Vec<NearbyQueryItem>> for NearbyQuery {
    fn from(values: Vec<NearbyQueryItem>) -> Self {
        Self { values }
    }
}

#[derive(Clone, Debug, Builder)]
#[builder(pattern = "owned")]
pub struct NearbyQueryItem {
    #[builder(setter(into))]
    pub target: NearbyTarget,
    #[builder(default = "NearbySearchType::Town")]
    pub search_type: NearbySearchType,
    pub radius: i32,
}

impl NearbyQueryItem {
    pub fn town<T: Into<String>>(target: T, radius: i32) -> Self {
        Self {
            target: NearbyTarget::Town(target.into()),
            search_type: NearbySearchType::Town,
            radius,
        }
    }

    pub fn coordinates<T: Into<ChunkCoordinate>>(
        target: T,
        radius: i32,
    ) -> Self {
        Self {
            target: NearbyTarget::Coordinate(target.into()),
            search_type: NearbySearchType::Town,
            radius,
        }
    }
}

impl Serialize for NearbyQueryItem {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let mut state = serializer.serialize_struct("NearbyQueryItem", 4)?;
        state.serialize_field("target_type", &self.target.target_type())?;
        match &self.target {
            NearbyTarget::Town(target) => {
                state.serialize_field("target", target)?
            }
            NearbyTarget::Coordinate(target) => {
                state.serialize_field("target", target)?
            }
        }
        state.serialize_field("search_type", &self.search_type)?;
        state.serialize_field("radius", &self.radius)?;
        state.end()
    }
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
enum NearbyTargetType {
    Town,
    Coordinate,
}

#[derive(Debug, Clone, Copy, Eq, PartialEq, Serialize)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum NearbySearchType {
    Town,
}

#[derive(Debug, Clone, Eq, PartialEq, Serialize)]
#[serde(untagged)]
pub enum NearbyTarget {
    Town(String),
    Coordinate(ChunkCoordinate),
}

impl NearbyTarget {
    fn target_type(&self) -> NearbyTargetType {
        match self {
            Self::Town(_) => NearbyTargetType::Town,
            Self::Coordinate(_) => NearbyTargetType::Coordinate,
        }
    }
}

impl From<String> for NearbyTarget {
    fn from(value: String) -> Self {
        Self::Town(value)
    }
}

impl From<&str> for NearbyTarget {
    fn from(value: &str) -> Self {
        Self::Town(value.to_string())
    }
}

impl From<ChunkCoordinate> for NearbyTarget {
    fn from(value: ChunkCoordinate) -> Self {
        Self::Coordinate(value)
    }
}

#[cfg(test)]
mod tests {
    use serde_json::json;

    use crate::model::query::Query;

    use super::{NearbyQuery, NearbyQueryItem};

    #[test]
    fn serializes_town_target_query() {
        let nearby_query =
            NearbyQuery::from(NearbyQueryItem::town("Melbourne", 100));
        let query: Query<NearbyQuery> = nearby_query.into();

        assert_eq!(
            serde_json::to_value(query).unwrap(),
            json!({
                "query": [
                    {
                        "target_type": "TOWN",
                        "target": "Melbourne",
                        "search_type": "TOWN",
                        "radius": 100
                    }
                ]
            })
        );
    }

    #[test]
    fn serializes_coordinate_target_query() {
        let nearby_query = NearbyQuery::from(NearbyQueryItem::coordinates(
            [2000, 10000],
            10000,
        ));
        let query: Query<NearbyQuery> = nearby_query.into();

        assert_eq!(
            serde_json::to_value(query).unwrap(),
            json!({
                "query": [
                    {
                        "target_type": "COORDINATE",
                        "target": [2000, 10000],
                        "search_type": "TOWN",
                        "radius": 10000
                    }
                ]
            })
        );
    }
}