perfect-postcode/server-rs/src/routes/developments.rs
Andras Schmelczer fd2860070a
Some checks failed
Build and publish Docker image / build-and-push (push) Successful in 18m12s
CI / Check (push) Failing after 23m38s
lgtm
2026-06-22 22:12:27 +01:00

70 lines
2.2 KiB
Rust

use std::sync::Arc;
use axum::extract::{Query, State};
use axum::response::{IntoResponse, Json, Response};
use axum::Extension;
use serde::{Deserialize, Serialize};
use tracing::info;
use crate::auth::OptionalUser;
use crate::data::DevelopmentSite;
use crate::licensing::{check_license_bounds, resolve_share_code};
use crate::parsing::require_bounds;
use crate::state::SharedState;
/// Hard cap on sites returned per viewport. Biggest schemes (by dwelling count)
/// are kept when a dense viewport exceeds this; `truncated` flags the clamp.
const DEVELOPMENTS_LIMIT: usize = 4000;
#[derive(Deserialize)]
pub struct DevelopmentsParams {
bounds: Option<String>,
/// Share-link code; grants bbox-scoped access for unlicensed users.
share: Option<String>,
}
#[derive(Serialize)]
pub struct DevelopmentsResponse {
pub developments: Vec<DevelopmentSite>,
pub total: usize,
pub truncated: bool,
}
/// Forward-looking "where new homes are coming" layer: planned/pipeline
/// development sites (MHCLG Brownfield Land register + Homes England Land Hub),
/// served as points within a viewport. Public OGL data, but still gated by the
/// normal demo/licence bounds check so unlicensed users only see their free zone.
pub async fn get_developments(
State(shared): State<Arc<SharedState>>,
Extension(user): Extension<OptionalUser>,
Extension(geo): Extension<crate::demo_zone::DemoZone>,
Query(params): Query<DevelopmentsParams>,
) -> Result<Json<DevelopmentsResponse>, Response> {
let state = shared.load_state();
let (south, west, north, east) =
require_bounds(params.bounds).map_err(IntoResponse::into_response)?;
let share_bounds = resolve_share_code(&state, params.share.as_deref()).await;
check_license_bounds(
&user.0,
(south, west, north, east),
geo.free_zone,
share_bounds,
)?;
let developments = state.developments.clone();
let (sites, total, truncated) =
developments.query_bounds(south, west, north, east, DEVELOPMENTS_LIMIT);
info!(
results = sites.len(),
total, truncated, "GET /api/developments"
);
Ok(Json(DevelopmentsResponse {
developments: sites,
total,
truncated,
}))
}