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
}
]
})
);
}
}