Skip to main content

dbrest_core/schema_cache/
media_handler.rs

1//! Media handler types for dbrest content negotiation
2//!
3//! Media handlers define how database results are aggregated and formatted
4//! for different content types (JSON, CSV, binary, etc.).
5
6use std::collections::HashMap;
7
8use crate::types::identifiers::{QualifiedIdentifier, RelIdentifier};
9use crate::types::media::MediaType;
10
11/// Handler for aggregating/formatting database results into a specific media type
12///
13/// Matches the Haskell `MediaHandler` data type.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum MediaHandler {
16    /// Built-in: aggregate into a single JSON object.
17    /// The bool indicates whether to strip the array wrapper.
18    BuiltinAggSingleJson(bool),
19    /// Built-in: aggregate into a JSON array with stripping
20    BuiltinAggArrayJsonStrip,
21    /// Built-in overridable: JSON aggregation
22    BuiltinOvAggJson,
23    /// Built-in overridable: CSV aggregation
24    BuiltinOvAggCsv,
25    /// Built-in overridable: GeoJSON aggregation
26    BuiltinOvAggGeoJson,
27    /// Custom aggregate function
28    CustomFunc(QualifiedIdentifier, RelIdentifier),
29    /// No aggregation needed
30    NoAgg,
31}
32
33impl MediaHandler {
34    /// Check if this is a built-in handler
35    pub fn is_builtin(&self) -> bool {
36        !matches!(self, MediaHandler::CustomFunc(_, _) | MediaHandler::NoAgg)
37    }
38
39    /// Check if this is a custom function handler
40    pub fn is_custom(&self) -> bool {
41        matches!(self, MediaHandler::CustomFunc(_, _))
42    }
43}
44
45/// A resolved handler: the media handler paired with its media type
46pub type ResolvedHandler = (MediaHandler, MediaType);
47
48/// Map from (relation_identifier, media_type) to the resolved handler
49pub type MediaHandlerMap = HashMap<(RelIdentifier, MediaType), ResolvedHandler>;
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn test_media_handler_builtin() {
57        assert!(MediaHandler::BuiltinOvAggJson.is_builtin());
58        assert!(MediaHandler::BuiltinOvAggCsv.is_builtin());
59        assert!(MediaHandler::BuiltinAggSingleJson(true).is_builtin());
60        assert!(MediaHandler::BuiltinAggArrayJsonStrip.is_builtin());
61        assert!(!MediaHandler::NoAgg.is_builtin());
62    }
63
64    #[test]
65    fn test_media_handler_custom() {
66        let custom = MediaHandler::CustomFunc(
67            QualifiedIdentifier::new("public", "to_geojson"),
68            RelIdentifier::any_element(),
69        );
70        assert!(custom.is_custom());
71        assert!(!custom.is_builtin());
72    }
73
74    #[test]
75    fn test_media_handler_map() {
76        let mut map: MediaHandlerMap = HashMap::new();
77        let key = (RelIdentifier::any_element(), MediaType::ApplicationJson);
78        let handler = (MediaHandler::BuiltinOvAggJson, MediaType::ApplicationJson);
79        map.insert(key.clone(), handler);
80
81        assert!(map.contains_key(&key));
82        assert_eq!(map.len(), 1);
83    }
84
85    #[test]
86    fn test_media_handler_equality() {
87        assert_eq!(
88            MediaHandler::BuiltinOvAggJson,
89            MediaHandler::BuiltinOvAggJson
90        );
91        assert_ne!(
92            MediaHandler::BuiltinOvAggJson,
93            MediaHandler::BuiltinOvAggCsv
94        );
95        assert_eq!(
96            MediaHandler::BuiltinAggSingleJson(true),
97            MediaHandler::BuiltinAggSingleJson(true)
98        );
99        assert_ne!(
100            MediaHandler::BuiltinAggSingleJson(true),
101            MediaHandler::BuiltinAggSingleJson(false)
102        );
103    }
104}