1pub mod capabilities;
34pub mod coverage;
35
36use crate::error::{ServiceError, ServiceResult};
37use axum::{
38 extract::{Query, State},
39 response::Response,
40};
41use serde::Deserialize;
42use std::sync::Arc;
43
44#[derive(Clone)]
46pub struct WcsState {
47 pub service_info: Arc<ServiceInfo>,
49 pub coverages: Arc<dashmap::DashMap<String, CoverageInfo>>,
51}
52
53#[derive(Debug, Clone)]
55pub struct ServiceInfo {
56 pub title: String,
58 pub abstract_text: Option<String>,
60 pub provider: String,
62 pub service_url: String,
64 pub versions: Vec<String>,
66}
67
68#[derive(Debug, Clone)]
70pub struct CoverageInfo {
71 pub coverage_id: String,
73 pub title: String,
75 pub abstract_text: Option<String>,
77 pub native_crs: String,
79 pub bbox: (f64, f64, f64, f64),
81 pub grid_size: (usize, usize),
83 pub grid_origin: (f64, f64),
85 pub grid_resolution: (f64, f64),
87 pub band_count: usize,
89 pub band_names: Vec<String>,
91 pub data_type: String,
93 pub source: CoverageSource,
95 pub formats: Vec<String>,
97}
98
99#[derive(Debug, Clone)]
101pub enum CoverageSource {
102 File(std::path::PathBuf),
104 Url(String),
106 Memory,
108}
109
110#[derive(Debug, Deserialize)]
112#[serde(rename_all = "UPPERCASE")]
113pub struct WcsRequest {
114 pub service: Option<String>,
116 pub version: Option<String>,
118 pub request: String,
120 #[serde(flatten)]
122 pub params: serde_json::Value,
123}
124
125impl WcsState {
126 pub fn new(service_info: ServiceInfo) -> Self {
128 Self {
129 service_info: Arc::new(service_info),
130 coverages: Arc::new(dashmap::DashMap::new()),
131 }
132 }
133
134 pub fn add_coverage(&self, info: CoverageInfo) -> ServiceResult<()> {
136 self.coverages.insert(info.coverage_id.clone(), info);
137 Ok(())
138 }
139
140 pub fn get_coverage(&self, coverage_id: &str) -> Option<CoverageInfo> {
142 self.coverages
143 .get(coverage_id)
144 .map(|entry| entry.value().clone())
145 }
146}
147
148pub async fn handle_wcs_request(
150 State(state): State<WcsState>,
151 Query(params): Query<WcsRequest>,
152) -> Result<Response, ServiceError> {
153 if let Some(ref service) = params.service {
155 if service.to_uppercase() != "WCS" {
156 return Err(ServiceError::InvalidParameter(
157 "SERVICE".to_string(),
158 format!("Expected 'WCS', got '{}'", service),
159 ));
160 }
161 }
162
163 match params.request.to_uppercase().as_str() {
165 "GETCAPABILITIES" => {
166 let version = params.version.as_deref().unwrap_or("2.0.1");
167 capabilities::handle_get_capabilities(&state, version).await
168 }
169 "DESCRIBECOVERAGE" => {
170 let version = params.version.as_deref().unwrap_or("2.0.1");
171 coverage::handle_describe_coverage(&state, version, ¶ms.params).await
172 }
173 "GETCOVERAGE" => {
174 let version = params.version.as_deref().unwrap_or("2.0.1");
175 coverage::handle_get_coverage(&state, version, ¶ms.params).await
176 }
177 _ => Err(ServiceError::UnsupportedOperation(params.request.clone())),
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_wcs_state_creation() {
187 let info = ServiceInfo {
188 title: "Test WCS".to_string(),
189 abstract_text: Some("Test service".to_string()),
190 provider: "COOLJAPAN OU".to_string(),
191 service_url: "http://localhost/wcs".to_string(),
192 versions: vec!["2.0.1".to_string()],
193 };
194
195 let state = WcsState::new(info);
196 assert_eq!(state.service_info.title, "Test WCS");
197 }
198
199 #[test]
200 fn test_add_coverage() {
201 let info = ServiceInfo {
202 title: "Test WCS".to_string(),
203 abstract_text: None,
204 provider: "COOLJAPAN OU".to_string(),
205 service_url: "http://localhost/wcs".to_string(),
206 versions: vec!["2.0.1".to_string()],
207 };
208
209 let state = WcsState::new(info);
210
211 let coverage = CoverageInfo {
212 coverage_id: "test_coverage".to_string(),
213 title: "Test Coverage".to_string(),
214 abstract_text: None,
215 native_crs: "EPSG:4326".to_string(),
216 bbox: (-180.0, -90.0, 180.0, 90.0),
217 grid_size: (1024, 512),
218 grid_origin: (-180.0, 90.0),
219 grid_resolution: (0.35, -0.35),
220 band_count: 3,
221 band_names: vec!["Red".to_string(), "Green".to_string(), "Blue".to_string()],
222 data_type: "Byte".to_string(),
223 source: CoverageSource::Memory,
224 formats: vec!["image/tiff".to_string(), "image/png".to_string()],
225 };
226
227 assert!(
228 state.add_coverage(coverage).is_ok(),
229 "Failed to add coverage"
230 );
231
232 let retrieved = state.get_coverage("test_coverage");
233 assert!(retrieved.is_some());
234 assert_eq!(
235 retrieved.as_ref().map(|c| &c.coverage_id),
236 Some(&"test_coverage".to_string())
237 );
238 }
239}