use crate::app::App;
use crate::error::{CoreError, Error};
use crate::middleware::authorization::CurrentPlayer;
use crate::response::{EitherExt, from_err};
use crate::{bail_if_city_is_not_owned_by, res};
use axum::extract::{Extension, Json, State};
use axum::response::Response;
use itertools::Itertools;
use nil_core::city::{City, PublicCity};
use nil_payload::request::city::*;
use nil_payload::response::city::*;
use tap::Pipe;
pub async fn get_city(
State(app): State<App>,
Extension(player): Extension<CurrentPlayer>,
Json(req): Json<GetCityRequest>,
) -> Response {
match app.get(req.world) {
Ok(world) => {
let result = try {
let world = world.read().await;
bail_if_city_is_not_owned_by!(world, &player.0, req.coord);
world.city(req.coord)?.clone()
};
result
.map(|city| res!(OK, GetCityResponse(city)))
.unwrap_or_else(from_err)
}
Err(err) => from_err(err),
}
}
pub async fn get_public_cities(
State(app): State<App>,
Json(req): Json<GetPublicCitiesRequest>,
) -> Response {
if req.coords.is_empty() && !req.all {
return res!(OK, GetPublicCitiesResponse(Vec::new()));
}
app
.world(req.world, |world| {
let mut responses = Vec::with_capacity(req.coords.len());
let cities: Box<dyn Iterator<Item = &City>> = if req.all {
world.continent().cities().pipe(Box::new)
} else {
world
.continent()
.cities_by(|city| req.coords.contains(&city.coord()))
.pipe(Box::new)
};
for city in cities {
GetPublicCityResponse::builder(world, city.coord())
.score(req.score)
.build()?
.pipe(|it| responses.push(it));
}
Ok::<_, Error>(responses)
})
.await
.try_map_left(|responses| res!(OK, GetPublicCitiesResponse(responses)))
.into_inner()
}
pub async fn get_public_city(
State(app): State<App>,
Json(req): Json<GetPublicCityRequest>,
) -> Response {
app
.world(req.world, |world| {
GetPublicCityResponse::builder(world, req.coord)
.score(req.score)
.build()
})
.await
.try_map_left(|response| res!(OK, response))
.into_inner()
}
pub async fn get_city_score(
State(app): State<App>,
Json(req): Json<GetCityScoreRequest>,
) -> Response {
app
.world(req.world, |world| world.get_city_score(req.coord))
.await
.try_map_left(|score| res!(OK, GetCityScoreResponse(score)))
.into_inner()
}
pub async fn rename_city(
State(app): State<App>,
Extension(player): Extension<CurrentPlayer>,
Json(req): Json<RenameCityRequest>,
) -> Response {
match app.get(req.world) {
Ok(world) => {
let result = try {
let mut world = world.write().await;
bail_if_city_is_not_owned_by!(world, &player.0, req.coord);
world.rename_city(req.coord, &req.name)?;
};
result
.map(|()| res!(OK))
.unwrap_or_else(from_err)
}
Err(err) => from_err(err),
}
}
pub async fn search_city(
State(app): State<App>,
Extension(player): Extension<CurrentPlayer>,
Json(req): Json<SearchCityRequest>,
) -> Response {
app
.world_blocking(req.world, move |world| {
world
.continent()
.search(req.search)?
.into_iter()
.filter(|city| city.is_owned_by_player_and(|it| it == &player.0))
.cloned()
.collect_vec()
.pipe(Ok::<_, CoreError>)
})
.await
.try_map_left(|cities| res!(OK, SearchCityResponse(cities)))
.into_inner()
}
pub async fn search_public_city(
State(app): State<App>,
Json(req): Json<SearchPublicCityRequest>,
) -> Response {
app
.world_blocking(req.world, move |world| {
world
.continent()
.search(req.search)?
.into_iter()
.map_into::<PublicCity>()
.collect_vec()
.pipe(Ok::<_, CoreError>)
})
.await
.try_map_left(|cities| res!(OK, SearchPublicCityResponse(cities)))
.into_inner()
}