Skip to main content

oxigdal_services/csw/
mod.rs

1//! CSW (Catalog Service for the Web) 2.0.2 implementation
2//!
3//! Provides OGC-compliant Catalog Service supporting:
4//! - GetCapabilities: Service metadata
5//! - GetRecords: Metadata search
6//! - GetRecordById: Metadata retrieval
7//!
8//! # Standards
9//!
10//! - OGC CSW 2.0.2
11
12pub mod capabilities;
13pub mod records;
14
15use crate::error::{ServiceError, ServiceResult};
16use axum::{
17    extract::{Query, State},
18    response::Response,
19};
20use serde::Deserialize;
21use std::sync::Arc;
22
23/// CSW service state
24#[derive(Clone)]
25pub struct CswState {
26    /// Service metadata
27    pub service_info: Arc<ServiceInfo>,
28    /// Metadata records
29    pub records: Arc<dashmap::DashMap<String, MetadataRecord>>,
30}
31
32/// Service metadata
33#[derive(Debug, Clone)]
34pub struct ServiceInfo {
35    /// Service title
36    pub title: String,
37    /// Service abstract/description
38    pub abstract_text: Option<String>,
39    /// Service provider
40    pub provider: String,
41    /// Service URL
42    pub service_url: String,
43    /// Supported versions
44    pub versions: Vec<String>,
45}
46
47/// Metadata record
48#[derive(Debug, Clone)]
49pub struct MetadataRecord {
50    /// Record identifier
51    pub identifier: String,
52    /// Record title
53    pub title: String,
54    /// Record abstract/description
55    pub abstract_text: Option<String>,
56    /// Keywords
57    pub keywords: Vec<String>,
58    /// Bounding box (minx, miny, maxx, maxy)
59    pub bbox: Option<(f64, f64, f64, f64)>,
60}
61
62/// CSW request parameters
63#[derive(Debug, Deserialize)]
64#[serde(rename_all = "UPPERCASE")]
65pub struct CswRequest {
66    /// Service name (must be "CSW")
67    pub service: Option<String>,
68    /// CSW version
69    pub version: Option<String>,
70    /// Request operation
71    pub request: String,
72    /// Additional parameters
73    #[serde(flatten)]
74    pub params: serde_json::Value,
75}
76
77impl CswState {
78    /// Create new CSW service state
79    pub fn new(service_info: ServiceInfo) -> Self {
80        Self {
81            service_info: Arc::new(service_info),
82            records: Arc::new(dashmap::DashMap::new()),
83        }
84    }
85
86    /// Add a metadata record
87    pub fn add_record(&self, record: MetadataRecord) -> ServiceResult<()> {
88        self.records.insert(record.identifier.clone(), record);
89        Ok(())
90    }
91}
92
93/// Main CSW request handler
94pub async fn handle_csw_request(
95    State(state): State<CswState>,
96    Query(params): Query<CswRequest>,
97) -> Result<Response, ServiceError> {
98    if let Some(ref service) = params.service {
99        if service.to_uppercase() != "CSW" {
100            return Err(ServiceError::InvalidParameter(
101                "SERVICE".to_string(),
102                format!("Expected 'CSW', got '{}'", service),
103            ));
104        }
105    }
106
107    match params.request.to_uppercase().as_str() {
108        "GETCAPABILITIES" => {
109            let version = params.version.as_deref().unwrap_or("2.0.2");
110            capabilities::handle_get_capabilities(&state, version).await
111        }
112        "GETRECORDS" => {
113            let version = params.version.as_deref().unwrap_or("2.0.2");
114            records::handle_get_records(&state, version, &params.params).await
115        }
116        "GETRECORDBYID" => {
117            let version = params.version.as_deref().unwrap_or("2.0.2");
118            records::handle_get_record_by_id(&state, version, &params.params).await
119        }
120        _ => Err(ServiceError::UnsupportedOperation(params.request.clone())),
121    }
122}