1use std::fmt;
2
3use usize_cast::IntoUsize;
4
5use crate::generated::vector_tile::tile as proto_tile;
6use crate::{MvtError, MvtResult, MvtValue};
7
8#[derive(Debug, Clone)]
9pub struct MvtPropertyIter<'a> {
10 keys: &'a [&'a str],
11 values: &'a [proto_tile::ValueView<'a>],
12 tags: std::slice::Chunks<'a, u32>,
13}
14
15impl<'a> MvtPropertyIter<'a> {
16 pub(crate) fn new(
17 keys: &'a [&'a str],
18 values: &'a [proto_tile::ValueView<'a>],
19 tags: std::slice::Chunks<'a, u32>,
20 ) -> Self {
21 Self { keys, values, tags }
22 }
23}
24
25impl<'a> Iterator for MvtPropertyIter<'a> {
26 type Item = MvtResult<(&'a str, MvtValueRef<'a>)>;
27
28 fn next(&mut self) -> Option<Self::Item> {
29 let pair = self.tags.next()?;
30 let [key_idx, value_idx] = pair else {
31 return Some(Err(MvtError::InvalidTagsLength(pair.len())));
32 };
33 let key = match self.keys.get((*key_idx).into_usize()) {
34 Some(key) => *key,
35 None => return Some(Err(MvtError::InvalidKeyIndex(*key_idx))),
36 };
37 let value = match self.values.get((*value_idx).into_usize()) {
38 Some(value) => value_ref(value),
39 None => return Some(Err(MvtError::InvalidValueIndex(*value_idx))),
40 };
41 Some(Ok((key, value)))
42 }
43}
44
45#[derive(Copy, Clone, PartialEq)]
46pub enum MvtValueRef<'a> {
47 String(&'a str),
48 Float(f32),
49 Double(f64),
50 Int(i64),
51 UInt(u64),
52 SInt(i64),
53 Bool(bool),
54 Null,
55}
56
57impl MvtValueRef<'_> {
58 #[must_use]
59 pub fn into_owned(self) -> MvtValue {
60 match self {
61 Self::String(value) => MvtValue::String(value.to_string()),
62 Self::Float(value) => MvtValue::Float(value),
63 Self::Double(value) => MvtValue::Double(value),
64 Self::Int(value) => MvtValue::Int(value),
65 Self::UInt(value) => MvtValue::UInt(value),
66 Self::SInt(value) => MvtValue::SInt(value),
67 Self::Bool(value) => MvtValue::Bool(value),
68 Self::Null => MvtValue::Null,
69 }
70 }
71}
72
73impl fmt::Debug for MvtValueRef<'_> {
74 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
76 match *self {
77 MvtValueRef::String(value) => write!(f, "{value:?}"),
78 MvtValueRef::Float(value) => write!(f, "{value} (float)"),
79 MvtValueRef::Double(value) => write!(f, "{value} (double)"),
80 MvtValueRef::Int(value) => write!(f, "{value} (int)"),
81 MvtValueRef::SInt(value) => write!(f, "{value} (sint)"),
82 MvtValueRef::UInt(value) => write!(f, "{value} (uint)"),
83 MvtValueRef::Bool(value) => write!(f, "{value} (bool)"),
84 MvtValueRef::Null => f.write_str("null"),
85 }
86 }
87}
88
89pub(crate) fn value_ref<'a>(value: &'a proto_tile::ValueView<'a>) -> MvtValueRef<'a> {
90 if let Some(value) = value.string_value {
91 MvtValueRef::String(value)
92 } else if let Some(value) = value.float_value {
93 MvtValueRef::Float(value)
94 } else if let Some(value) = value.double_value {
95 MvtValueRef::Double(value)
96 } else if let Some(value) = value.int_value {
97 MvtValueRef::Int(value)
98 } else if let Some(value) = value.uint_value {
99 MvtValueRef::UInt(value)
100 } else if let Some(value) = value.sint_value {
101 MvtValueRef::SInt(value)
102 } else if let Some(value) = value.bool_value {
103 MvtValueRef::Bool(value)
104 } else {
105 MvtValueRef::Null
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use super::*;
112 use crate::MvtReaderRef;
113 use crate::reader::tests::{encode_layer, first_feature};
114
115 #[test]
116 fn value_ref_debug_renders_bare_values() {
117 insta::assert_debug_snapshot!(MvtValueRef::String("x"), @r#""x""#);
118 insta::assert_debug_snapshot!(MvtValueRef::Float(1.25), @"1.25 (float)");
119 insta::assert_debug_snapshot!(MvtValueRef::Double(2.5), @"2.5 (double)");
120 insta::assert_debug_snapshot!(MvtValueRef::Int(-3), @"-3 (int)");
121 insta::assert_debug_snapshot!(MvtValueRef::UInt(4), @"4 (uint)");
122 insta::assert_debug_snapshot!(MvtValueRef::SInt(-5), @"-5 (sint)");
123 insta::assert_debug_snapshot!(MvtValueRef::Bool(true), @"true (bool)");
124 insta::assert_debug_snapshot!(MvtValueRef::Null, @"null");
125 }
126
127 #[test]
128 fn value_ref_into_owned_covers_every_variant() {
129 assert_eq!(
130 MvtValueRef::String("x").into_owned(),
131 MvtValue::String("x".into())
132 );
133 assert_eq!(MvtValueRef::Float(1.25).into_owned(), MvtValue::Float(1.25));
134 assert_eq!(MvtValueRef::Double(2.5).into_owned(), MvtValue::Double(2.5));
135 assert_eq!(MvtValueRef::Int(-3).into_owned(), MvtValue::Int(-3));
136 assert_eq!(MvtValueRef::UInt(4).into_owned(), MvtValue::UInt(4));
137 assert_eq!(MvtValueRef::SInt(-5).into_owned(), MvtValue::SInt(-5));
138 assert_eq!(MvtValueRef::Bool(true).into_owned(), MvtValue::Bool(true));
139 assert_eq!(MvtValueRef::Null.into_owned(), MvtValue::Null);
140 }
141
142 #[test]
143 fn property_iterator_reports_malformed_tags() {
144 let layer = proto_tile::Layer {
145 version: 2,
146 name: "tags".into(),
147 keys: vec!["k".into()],
148 values: vec![proto_tile::Value::default()],
149 ..Default::default()
150 };
151
152 for (tags, expected) in [
153 (vec![0], "invalid feature tags length: 1"),
154 (vec![1, 0], "invalid key index 1"),
155 (vec![0, 1], "invalid value index 1"),
156 ] {
157 let feature = proto_tile::Feature {
158 tags,
159 ..Default::default()
160 };
161 let mut layer = layer.clone();
162 layer.features = vec![feature];
163 let bytes = encode_layer(layer);
164 let reader = MvtReaderRef::new(&bytes).unwrap();
165 let err = first_feature(&reader)
166 .properties()
167 .next()
168 .unwrap()
169 .unwrap_err();
170 assert_eq!(err.to_string(), expected);
171 }
172 }
173}