supabase_client_query/
geojson_select.rs1use reqwest::header::HeaderValue;
2use serde_json::Value as JsonValue;
3
4use supabase_client_core::SupabaseError;
5
6use crate::backend::QueryBackend;
7use crate::filter::Filterable;
8use crate::modifier::Modifiable;
9use crate::sql::{FilterCondition, ParamStore, SqlParts};
10
11pub struct GeoJsonSelectBuilder {
16 pub(crate) backend: QueryBackend,
17 pub(crate) parts: SqlParts,
18 pub(crate) params: ParamStore,
19}
20
21impl Filterable for GeoJsonSelectBuilder {
22 fn filters_mut(&mut self) -> &mut Vec<FilterCondition> {
23 &mut self.parts.filters
24 }
25 fn params_mut(&mut self) -> &mut ParamStore {
26 &mut self.params
27 }
28}
29
30impl Modifiable for GeoJsonSelectBuilder {
31 fn parts_mut(&mut self) -> &mut SqlParts {
32 &mut self.parts
33 }
34}
35
36impl GeoJsonSelectBuilder {
37 pub fn schema(mut self, schema: &str) -> Self {
39 self.parts.schema_override = Some(schema.to_string());
40 self
41 }
42
43 pub async fn execute(self) -> Result<JsonValue, SupabaseError> {
45 match &self.backend {
46 QueryBackend::Rest { http, base_url, api_key, schema } => {
47 let (url, mut headers) = crate::postgrest::build_postgrest_select(
48 base_url, &self.parts, &self.params,
49 )
50 .map_err(SupabaseError::QueryBuilder)?;
51
52 headers.insert(
54 "Accept",
55 HeaderValue::from_static("application/geo+json"),
56 );
57
58 headers.insert("apikey", HeaderValue::from_str(api_key).unwrap());
60 headers.insert(
61 "Authorization",
62 HeaderValue::from_str(&format!("Bearer {}", api_key)).unwrap(),
63 );
64
65 if let Some(ref so) = self.parts.schema_override {
67 headers.insert(
68 "Accept-Profile",
69 HeaderValue::from_str(so).unwrap(),
70 );
71 } else if schema != "public" {
72 headers.entry("Accept-Profile")
73 .or_insert_with(|| HeaderValue::from_str(schema).unwrap());
74 }
75
76 let response = http
77 .get(&url)
78 .headers(headers)
79 .send()
80 .await
81 .map_err(|e| SupabaseError::Http(e.to_string()))?;
82
83 let status = response.status().as_u16();
84 let body = response
85 .text()
86 .await
87 .map_err(|e| SupabaseError::Http(e.to_string()))?;
88
89 if status >= 400 {
90 return Err(SupabaseError::postgrest(status, body, None));
91 }
92
93 serde_json::from_str(&body).map_err(|e| {
94 SupabaseError::Serialization(format!(
95 "Failed to parse GeoJSON response: {}",
96 e
97 ))
98 })
99 }
100 #[cfg(feature = "direct-sql")]
101 QueryBackend::DirectSql { .. } => {
102 Err(SupabaseError::query_builder(
103 "GeoJSON output is only supported with the REST (PostgREST) backend",
104 ))
105 }
106 }
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113 use crate::sql::{SqlOperation, SqlParts, ParamStore};
114
115 #[test]
116 fn test_geojson_builder_modifiable() {
117 let mut builder = GeoJsonSelectBuilder {
118 backend: QueryBackend::Rest {
119 http: reqwest::Client::new(),
120 base_url: "http://localhost".into(),
121 api_key: "key".into(),
122 schema: "public".to_string(),
123 },
124 parts: SqlParts::new(SqlOperation::Select, "public", "cities"),
125 params: ParamStore::new(),
126 };
127 builder = builder.limit(10);
128 assert_eq!(builder.parts.limit, Some(10));
129 }
130
131 #[test]
132 fn test_geojson_builder_filterable() {
133 let builder = GeoJsonSelectBuilder {
134 backend: QueryBackend::Rest {
135 http: reqwest::Client::new(),
136 base_url: "http://localhost".into(),
137 api_key: "key".into(),
138 schema: "public".to_string(),
139 },
140 parts: SqlParts::new(SqlOperation::Select, "public", "cities"),
141 params: ParamStore::new(),
142 };
143 let builder = builder.eq("name", "Auckland");
144 assert_eq!(builder.parts.filters.len(), 1);
145 }
146
147 #[test]
148 fn test_geojson_accept_header() {
149 let parts = SqlParts::new(SqlOperation::Select, "public", "cities");
150 let params = ParamStore::new();
151 let (_, mut headers) = crate::postgrest::build_postgrest_select(
152 "http://localhost:64321", &parts, ¶ms,
153 ).unwrap();
154 headers.insert("Accept", HeaderValue::from_static("application/geo+json"));
156 assert_eq!(headers.get("Accept").unwrap(), "application/geo+json");
157 }
158}