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, /// Share-link code; grants bbox-scoped access for unlicensed users. share: Option, } #[derive(Serialize)] pub struct DevelopmentsResponse { pub developments: Vec, 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>, Extension(user): Extension, Extension(geo): Extension, Query(params): Query, ) -> Result, 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, })) }