1use crate::error::GpkgError;
2use wkb::reader::{Dimension, GeometryType, Wkb};
3
4#[derive(Clone, Copy, Debug, PartialEq)]
6#[repr(C)]
7pub enum ColumnType {
8 Boolean,
10 Varchar,
12 Double,
14 Integer,
16 Geometry,
18}
19
20#[derive(Clone, Debug)]
22pub struct ColumnSpec {
23 pub name: String,
24 pub column_type: ColumnType,
25}
26
27#[derive(Clone, Debug)]
29pub struct GpkgLayerMetadata {
30 pub primary_key_column: String,
31 pub geometry_column: String,
32 pub geometry_type: GeometryType,
33 pub geometry_dimension: Dimension,
34 pub srs_id: u32,
35 pub other_columns: Vec<ColumnSpec>,
36}
37
38#[derive(Clone, Debug, PartialEq)]
58pub enum Value {
59 Null,
60 Integer(i64),
61 Real(f64),
62 Text(String),
63 Blob(Vec<u8>),
64 Geometry(Vec<u8>), }
66
67impl From<&str> for Value {
68 fn from(value: &str) -> Self {
69 Value::Text(value.to_string())
70 }
71}
72
73impl From<String> for Value {
74 fn from(value: String) -> Self {
75 Value::Text(value)
76 }
77}
78
79impl From<bool> for Value {
80 fn from(value: bool) -> Self {
81 Value::Integer(if value { 1 } else { 0 })
82 }
83}
84
85macro_rules! impl_from_int {
86 ($($t:ty),+ $(,)?) => {
87 $(
88 impl From<$t> for Value {
89 #[inline]
90 fn from(value: $t) -> Self {
91 Value::Integer(value as i64)
92 }
93 }
94 )+
95 };
96}
97
98macro_rules! impl_from_uint {
99 ($($t:ty),+ $(,)?) => {
100 $(
101 impl From<$t> for Value {
102 #[inline]
103 fn from(value: $t) -> Self {
104 Value::Integer(value as i64)
105 }
106 }
107 )+
108 };
109}
110
111impl_from_int!(i8, i16, i32, i64, isize);
112impl_from_uint!(u8, u16, u32, u64, usize);
113
114impl From<f32> for Value {
115 #[inline]
116 fn from(value: f32) -> Self {
117 Value::Real(value as f64)
118 }
119}
120
121impl From<f64> for Value {
122 fn from(value: f64) -> Self {
123 Value::Real(value)
124 }
125}
126
127impl<T: Into<Value>> From<Option<T>> for Value {
128 fn from(value: Option<T>) -> Self {
129 match value {
130 Some(v) => v.into(),
131 None => Value::Null,
132 }
133 }
134}
135
136#[macro_export]
137macro_rules! params {
138 () => {
139 &[]
140 };
141 ($($value:expr),+ $(,)?) => {
142 &[$($crate::Value::from($value)),+]
143 };
144}
145
146#[inline]
147fn value_to_sql_output(value: &Value) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
148 use rusqlite::types::{ToSqlOutput, ValueRef};
149
150 let output = match value {
151 Value::Null => ToSqlOutput::Borrowed(ValueRef::Null),
152 Value::Integer(v) => ToSqlOutput::Borrowed(ValueRef::Integer(*v)),
153 Value::Real(v) => ToSqlOutput::Borrowed(ValueRef::Real(*v)),
154 Value::Text(s) => ToSqlOutput::Borrowed(ValueRef::Text(s.as_bytes())),
155 Value::Blob(items) | Value::Geometry(items) => {
156 ToSqlOutput::Borrowed(ValueRef::Blob(items.as_slice()))
157 }
158 };
159
160 Ok(output)
161}
162
163impl rusqlite::ToSql for Value {
164 #[inline]
165 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
166 value_to_sql_output(self)
167 }
168}
169
170impl From<rusqlite::types::Value> for Value {
171 #[inline]
172 fn from(value: rusqlite::types::Value) -> Self {
173 match value {
174 rusqlite::types::Value::Null => Value::Null,
175 rusqlite::types::Value::Integer(value) => Value::Integer(value),
176 rusqlite::types::Value::Real(value) => Value::Real(value),
177 rusqlite::types::Value::Text(value) => Value::Text(value),
178 rusqlite::types::Value::Blob(value) => Value::Blob(value),
179 }
180 }
181}
182
183impl<'a> From<rusqlite::types::ValueRef<'a>> for Value {
184 #[inline]
185 fn from(value: rusqlite::types::ValueRef<'a>) -> Self {
186 match value {
187 rusqlite::types::ValueRef::Null => Value::Null,
188 rusqlite::types::ValueRef::Integer(value) => Value::Integer(value),
189 rusqlite::types::ValueRef::Real(value) => Value::Real(value),
190 rusqlite::types::ValueRef::Text(value) => {
191 let s = std::str::from_utf8(value).expect("invalid UTF-8");
192 Value::Text(s.to_string())
193 }
194 rusqlite::types::ValueRef::Blob(value) => Value::Blob(value.to_vec()),
195 }
196 }
197}
198
199impl From<Value> for rusqlite::types::Value {
200 #[inline]
201 fn from(value: Value) -> Self {
202 match value {
203 Value::Null => rusqlite::types::Value::Null,
204 Value::Integer(value) => rusqlite::types::Value::Integer(value),
205 Value::Real(value) => rusqlite::types::Value::Real(value),
206 Value::Text(value) => rusqlite::types::Value::Text(value),
207 Value::Blob(value) | Value::Geometry(value) => rusqlite::types::Value::Blob(value),
208 }
209 }
210}
211
212#[inline]
213fn value_type_name(value: &Value) -> &'static str {
214 match value {
215 Value::Null => "NULL",
216 Value::Integer(_) => "INTEGER",
217 Value::Real(_) => "REAL",
218 Value::Text(_) => "TEXT",
219 Value::Blob(_) => "BLOB",
220 Value::Geometry(_) => "GEOMETRY",
221 }
222}
223
224#[inline]
225fn invalid_type(expected: &'static str, value: &Value) -> GpkgError {
226 GpkgError::Message(format!(
227 "expected {expected}, got {}",
228 value_type_name(value)
229 ))
230}
231
232#[inline]
233fn out_of_range(expected: &'static str) -> GpkgError {
234 GpkgError::Message(format!("value out of range for {expected}"))
235}
236
237macro_rules! impl_try_from_int_ref {
238 ($t:ty) => {
239 impl TryFrom<&Value> for $t {
240 type Error = GpkgError;
241
242 #[inline]
243 fn try_from(value: &Value) -> Result<Self, Self::Error> {
244 match value {
245 Value::Integer(v) => {
246 <$t>::try_from(*v).map_err(|_| out_of_range(stringify!($t)))
247 }
248 _ => Err(invalid_type(stringify!($t), value)),
249 }
250 }
251 }
252
253 impl TryFrom<Value> for $t {
254 type Error = GpkgError;
255
256 #[inline]
257 fn try_from(value: Value) -> Result<Self, Self::Error> {
258 (&value).try_into()
259 }
260 }
261 };
262}
263
264impl TryFrom<&Value> for i64 {
265 type Error = GpkgError;
266
267 #[inline]
268 fn try_from(value: &Value) -> Result<Self, Self::Error> {
269 match value {
270 Value::Integer(v) => Ok(*v),
271 _ => Err(invalid_type("i64", value)),
272 }
273 }
274}
275
276impl TryFrom<Value> for i64 {
277 type Error = GpkgError;
278
279 #[inline]
280 fn try_from(value: Value) -> Result<Self, Self::Error> {
281 (&value).try_into()
282 }
283}
284
285impl_try_from_int_ref!(i32);
286impl_try_from_int_ref!(i16);
287impl_try_from_int_ref!(i8);
288impl_try_from_int_ref!(isize);
289impl_try_from_int_ref!(u64);
290impl_try_from_int_ref!(u32);
291impl_try_from_int_ref!(u16);
292impl_try_from_int_ref!(u8);
293impl_try_from_int_ref!(usize);
294
295impl TryFrom<&Value> for f64 {
296 type Error = GpkgError;
297
298 #[inline]
299 fn try_from(value: &Value) -> Result<Self, Self::Error> {
300 match value {
301 Value::Real(v) => Ok(*v),
302 Value::Integer(v) => Ok(*v as f64),
303 _ => Err(invalid_type("f64", value)),
304 }
305 }
306}
307
308impl TryFrom<Value> for f64 {
309 type Error = GpkgError;
310
311 #[inline]
312 fn try_from(value: Value) -> Result<Self, Self::Error> {
313 (&value).try_into()
314 }
315}
316
317impl TryFrom<&Value> for f32 {
318 type Error = GpkgError;
319
320 #[inline]
321 fn try_from(value: &Value) -> Result<Self, Self::Error> {
322 match value {
323 Value::Real(v) => Ok(*v as f32),
324 Value::Integer(v) => Ok(*v as f32),
325 _ => Err(invalid_type("f32", value)),
326 }
327 }
328}
329
330impl TryFrom<Value> for f32 {
331 type Error = GpkgError;
332
333 #[inline]
334 fn try_from(value: Value) -> Result<Self, Self::Error> {
335 (&value).try_into()
336 }
337}
338
339impl TryFrom<&Value> for bool {
340 type Error = GpkgError;
341
342 #[inline]
343 fn try_from(value: &Value) -> Result<Self, Self::Error> {
344 match value {
345 Value::Integer(0) => Ok(false),
346 Value::Integer(1) => Ok(true),
347 _ => Err(invalid_type("bool", value)),
348 }
349 }
350}
351
352impl TryFrom<Value> for bool {
353 type Error = GpkgError;
354
355 #[inline]
356 fn try_from(value: Value) -> Result<Self, Self::Error> {
357 (&value).try_into()
358 }
359}
360
361impl<T> TryFrom<&Value> for Option<T>
362where
363 T: for<'a> TryFrom<&'a Value, Error = GpkgError>,
364{
365 type Error = GpkgError;
366
367 #[inline]
368 fn try_from(value: &Value) -> Result<Self, Self::Error> {
369 match value {
370 Value::Null => Ok(None),
371 _ => Ok(Some(T::try_from(value)?)),
372 }
373 }
374}
375
376impl<T> TryFrom<Value> for Option<T>
377where
378 T: for<'a> TryFrom<&'a Value, Error = GpkgError>,
379{
380 type Error = GpkgError;
381
382 #[inline]
383 fn try_from(value: Value) -> Result<Self, Self::Error> {
384 (&value).try_into()
385 }
386}
387
388impl TryFrom<Value> for String {
389 type Error = GpkgError;
390
391 #[inline]
392 fn try_from(value: Value) -> Result<Self, Self::Error> {
393 match value {
394 Value::Text(s) => Ok(s),
395 other => Err(invalid_type("String", &other)),
396 }
397 }
398}
399
400impl<'a> TryFrom<&'a Value> for &'a str {
401 type Error = GpkgError;
402
403 #[inline]
404 fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
405 match value {
406 Value::Text(s) => Ok(s.as_str()),
407 _ => Err(invalid_type("&str", value)),
408 }
409 }
410}
411
412impl<'a> TryFrom<&'a Value> for Wkb<'a> {
413 type Error = GpkgError;
414
415 #[inline]
416 fn try_from(value: &'a Value) -> Result<Self, Self::Error> {
417 match value {
418 Value::Geometry(bytes) => crate::gpkg::gpkg_geometry_to_wkb(bytes.as_slice()),
419 Value::Blob(bytes) => {
420 let bytes = bytes.as_slice();
421 if bytes.len() >= 4 && bytes[0] == 0x47 && bytes[1] == 0x50 {
422 return crate::gpkg::gpkg_geometry_to_wkb(bytes);
423 }
424 Ok(Wkb::try_new(bytes)?)
425 }
426 _ => Err(invalid_type("Wkb", value)),
427 }
428 }
429}
430
431enum SqlParam<'a> {
434 Owned(Value),
435 Borrowed(&'a Value),
436}
437
438impl<'a> rusqlite::ToSql for SqlParam<'a> {
439 #[inline]
440 fn to_sql(&self) -> rusqlite::Result<rusqlite::types::ToSqlOutput<'_>> {
441 match self {
442 SqlParam::Owned(value) => value.to_sql(),
443 SqlParam::Borrowed(value) => value.to_sql(),
444 }
445 }
446}
447
448pub(crate) fn params_from_geom_and_properties<'p, P>(
449 geom: Vec<u8>,
450 properties: P,
451 id: Option<i64>,
452) -> impl rusqlite::Params
453where
454 P: IntoIterator<Item = &'p Value>,
455{
456 let params = std::iter::once(SqlParam::Owned(Value::Geometry(geom)))
457 .chain(properties.into_iter().map(SqlParam::Borrowed))
458 .chain(id.into_iter().map(|i| SqlParam::Owned(Value::Integer(i))));
459 rusqlite::params_from_iter(params)
460}
461
462#[cfg(test)]
463mod tests {
464 use super::{GpkgError, Value};
465
466 #[test]
467 fn option_try_from_value_null_is_none() -> Result<(), GpkgError> {
468 let value = Value::Null;
469 let parsed: Option<i64> = value.try_into()?;
470 assert_eq!(parsed, None);
471 Ok(())
472 }
473
474 #[test]
475 fn option_try_from_value_some_is_some() -> Result<(), GpkgError> {
476 let value = Value::Integer(7);
477 let parsed: Option<i64> = value.try_into()?;
478 assert_eq!(parsed, Some(7));
479 Ok(())
480 }
481
482 #[test]
483 fn option_try_from_ref_handles_integer() -> Result<(), GpkgError> {
484 let value = Value::Integer(42);
485 let parsed: Option<i64> = (&value).try_into()?;
486 assert_eq!(parsed, Some(42));
487 Ok(())
488 }
489}