1use std::fmt;
59use std::marker::PhantomData;
60
61use serde::de::{IgnoredAny, SeqAccess, Visitor};
62use serde::{Deserialize, Deserializer, Serialize};
63
64pub const MAX_SUBDIVISIONS: usize = 32;
80
81#[cold]
82fn subdivisions_too_long<E>() -> E
83where
84 E: serde::de::Error,
85{
86 E::custom(format_args!(
87 "subdivisions exceeds maximum length of {MAX_SUBDIVISIONS}"
88 ))
89}
90
91#[inline(always)]
92fn deserialize_subdivisions<'de, D, T>(deserializer: D) -> Result<Vec<T>, D::Error>
93where
94 D: Deserializer<'de>,
95 T: Deserialize<'de>,
96{
97 struct SubdivisionsVisitor<T>(PhantomData<T>);
98
99 impl<'de, T> Visitor<'de> for SubdivisionsVisitor<T>
100 where
101 T: Deserialize<'de>,
102 {
103 type Value = Vec<T>;
104
105 fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
106 write!(
107 formatter,
108 "an array containing at most {MAX_SUBDIVISIONS} subdivisions"
109 )
110 }
111
112 #[inline(always)]
113 fn visit_seq<A>(self, mut sequence: A) -> Result<Self::Value, A::Error>
114 where
115 A: SeqAccess<'de>,
116 {
117 if let Some(size) = sequence.size_hint() {
118 if size > MAX_SUBDIVISIONS {
119 return Err(subdivisions_too_long());
120 }
121
122 let mut subdivisions = Vec::with_capacity(size);
123 while let Some(subdivision) = sequence.next_element()? {
124 subdivisions.push(subdivision);
125 }
126 return Ok(subdivisions);
127 }
128
129 let mut subdivisions = Vec::new();
130 while subdivisions.len() < MAX_SUBDIVISIONS {
131 let Some(subdivision) = sequence.next_element()? else {
132 return Ok(subdivisions);
133 };
134 subdivisions.push(subdivision);
135 }
136
137 if sequence.next_element::<IgnoredAny>()?.is_some() {
138 return Err(subdivisions_too_long());
139 }
140 Ok(subdivisions)
141 }
142 }
143
144 deserializer.deserialize_seq(SubdivisionsVisitor(PhantomData))
145}
146
147#[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq, Eq)]
174pub struct Names<'a> {
175 #[serde(
177 borrow,
178 rename = "de",
179 default,
180 skip_serializing_if = "Option::is_none"
181 )]
182 pub german: Option<&'a str>,
183 #[serde(rename = "en", default, skip_serializing_if = "Option::is_none")]
185 pub english: Option<&'a str>,
186 #[serde(rename = "es", default, skip_serializing_if = "Option::is_none")]
188 pub spanish: Option<&'a str>,
189 #[serde(rename = "fr", default, skip_serializing_if = "Option::is_none")]
191 pub french: Option<&'a str>,
192 #[serde(rename = "ja", default, skip_serializing_if = "Option::is_none")]
194 pub japanese: Option<&'a str>,
195 #[serde(rename = "pt-BR", default, skip_serializing_if = "Option::is_none")]
197 pub brazilian_portuguese: Option<&'a str>,
198 #[serde(rename = "ru", default, skip_serializing_if = "Option::is_none")]
200 pub russian: Option<&'a str>,
201 #[serde(rename = "zh-CN", default, skip_serializing_if = "Option::is_none")]
203 pub simplified_chinese: Option<&'a str>,
204}
205
206impl Names<'_> {
207 #[must_use]
209 pub fn is_empty(&self) -> bool {
210 self.german.is_none()
211 && self.english.is_none()
212 && self.spanish.is_none()
213 && self.french.is_none()
214 && self.japanese.is_none()
215 && self.brazilian_portuguese.is_none()
216 && self.russian.is_none()
217 && self.simplified_chinese.is_none()
218 }
219}
220
221macro_rules! impl_is_empty_via_default {
222 ($ty:ty) => {
223 impl $ty {
224 #[must_use]
226 pub fn is_empty(&self) -> bool {
227 *self == Self::default()
228 }
229 }
230 };
231}
232
233#[derive(Deserialize, Serialize, Clone, Debug, Default)]
239pub struct Country<'a> {
240 #[serde(borrow, default, skip_serializing_if = "country::Continent::is_empty")]
242 pub continent: country::Continent<'a>,
243 #[serde(default, skip_serializing_if = "country::Country::is_empty")]
245 pub country: country::Country<'a>,
246 #[serde(default, skip_serializing_if = "country::Country::is_empty")]
249 pub registered_country: country::Country<'a>,
250 #[serde(default, skip_serializing_if = "country::RepresentedCountry::is_empty")]
252 pub represented_country: country::RepresentedCountry<'a>,
253 #[serde(default, skip_serializing_if = "country::Traits::is_empty")]
255 pub traits: country::Traits,
256}
257
258#[derive(Deserialize, Serialize, Clone, Debug, Default)]
264pub struct City<'a> {
265 #[serde(borrow, default, skip_serializing_if = "city::City::is_empty")]
267 pub city: city::City<'a>,
268 #[serde(default, skip_serializing_if = "city::Continent::is_empty")]
270 pub continent: city::Continent<'a>,
271 #[serde(default, skip_serializing_if = "city::Country::is_empty")]
273 pub country: city::Country<'a>,
274 #[serde(default, skip_serializing_if = "city::Location::is_empty")]
276 pub location: city::Location<'a>,
277 #[serde(default, skip_serializing_if = "city::Postal::is_empty")]
279 pub postal: city::Postal<'a>,
280 #[serde(default, skip_serializing_if = "city::Country::is_empty")]
282 pub registered_country: city::Country<'a>,
283 #[serde(default, skip_serializing_if = "city::RepresentedCountry::is_empty")]
285 pub represented_country: city::RepresentedCountry<'a>,
286 #[serde(
291 borrow,
292 default,
293 deserialize_with = "deserialize_subdivisions",
294 skip_serializing_if = "Vec::is_empty"
295 )]
296 pub subdivisions: Vec<city::Subdivision<'a>>,
297 #[serde(default, skip_serializing_if = "city::Traits::is_empty")]
299 pub traits: city::Traits,
300}
301
302#[derive(Deserialize, Serialize, Clone, Debug, Default)]
308pub struct Enterprise<'a> {
309 #[serde(borrow, default, skip_serializing_if = "enterprise::City::is_empty")]
311 pub city: enterprise::City<'a>,
312 #[serde(default, skip_serializing_if = "enterprise::Continent::is_empty")]
314 pub continent: enterprise::Continent<'a>,
315 #[serde(default, skip_serializing_if = "enterprise::Country::is_empty")]
317 pub country: enterprise::Country<'a>,
318 #[serde(default, skip_serializing_if = "enterprise::Location::is_empty")]
320 pub location: enterprise::Location<'a>,
321 #[serde(default, skip_serializing_if = "enterprise::Postal::is_empty")]
323 pub postal: enterprise::Postal<'a>,
324 #[serde(default, skip_serializing_if = "enterprise::Country::is_empty")]
326 pub registered_country: enterprise::Country<'a>,
327 #[serde(
329 default,
330 skip_serializing_if = "enterprise::RepresentedCountry::is_empty"
331 )]
332 pub represented_country: enterprise::RepresentedCountry<'a>,
333 #[serde(
337 borrow,
338 default,
339 deserialize_with = "deserialize_subdivisions",
340 skip_serializing_if = "Vec::is_empty"
341 )]
342 pub subdivisions: Vec<enterprise::Subdivision<'a>>,
343 #[serde(default, skip_serializing_if = "enterprise::Traits::is_empty")]
345 pub traits: enterprise::Traits<'a>,
346}
347
348#[derive(Deserialize, Serialize, Clone, Debug)]
352pub struct Isp<'a> {
353 #[serde(skip_serializing_if = "Option::is_none")]
355 pub autonomous_system_number: Option<u32>,
356 #[serde(skip_serializing_if = "Option::is_none")]
358 pub autonomous_system_organization: Option<&'a str>,
359 #[serde(skip_serializing_if = "Option::is_none")]
361 pub isp: Option<&'a str>,
362 #[serde(skip_serializing_if = "Option::is_none")]
365 pub mobile_country_code: Option<&'a str>,
366 #[serde(skip_serializing_if = "Option::is_none")]
369 pub mobile_network_code: Option<&'a str>,
370 #[serde(skip_serializing_if = "Option::is_none")]
372 pub organization: Option<&'a str>,
373}
374
375#[derive(Deserialize, Serialize, Clone, Debug)]
379pub struct ConnectionType<'a> {
380 #[serde(skip_serializing_if = "Option::is_none")]
384 pub connection_type: Option<&'a str>,
385}
386
387#[derive(Deserialize, Serialize, Clone, Debug)]
392pub struct AnonymousIp {
393 #[serde(skip_serializing_if = "Option::is_none")]
395 pub is_anonymous: Option<bool>,
396 #[serde(skip_serializing_if = "Option::is_none")]
400 pub is_anonymous_vpn: Option<bool>,
401 #[serde(skip_serializing_if = "Option::is_none")]
403 pub is_hosting_provider: Option<bool>,
404 #[serde(skip_serializing_if = "Option::is_none")]
406 pub is_public_proxy: Option<bool>,
407 #[serde(skip_serializing_if = "Option::is_none")]
410 pub is_residential_proxy: Option<bool>,
411 #[serde(skip_serializing_if = "Option::is_none")]
413 pub is_tor_exit_node: Option<bool>,
414}
415
416#[derive(Deserialize, Serialize, Clone, Debug)]
420pub struct DensityIncome {
421 #[serde(skip_serializing_if = "Option::is_none")]
423 pub average_income: Option<u32>,
424 #[serde(skip_serializing_if = "Option::is_none")]
426 pub population_density: Option<u32>,
427}
428
429#[derive(Deserialize, Serialize, Clone, Debug)]
433pub struct Domain<'a> {
434 #[serde(skip_serializing_if = "Option::is_none")]
437 pub domain: Option<&'a str>,
438}
439
440#[derive(Deserialize, Serialize, Clone, Debug)]
444pub struct Asn<'a> {
445 #[serde(skip_serializing_if = "Option::is_none")]
447 pub autonomous_system_number: Option<u32>,
448 #[serde(skip_serializing_if = "Option::is_none")]
450 pub autonomous_system_organization: Option<&'a str>,
451}
452
453pub mod country {
458 use super::Names;
459 use serde::{Deserialize, Serialize};
460
461 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
463 pub struct Continent<'a> {
464 #[serde(default, skip_serializing_if = "Option::is_none")]
466 pub code: Option<&'a str>,
467 #[serde(default, skip_serializing_if = "Option::is_none")]
469 pub geoname_id: Option<u32>,
470 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
472 pub names: Names<'a>,
473 }
474
475 impl_is_empty_via_default!(Continent<'_>);
476
477 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
479 pub struct Country<'a> {
480 #[serde(default, skip_serializing_if = "Option::is_none")]
482 pub geoname_id: Option<u32>,
483 #[serde(default, skip_serializing_if = "Option::is_none")]
485 pub is_in_european_union: Option<bool>,
486 #[serde(default, skip_serializing_if = "Option::is_none")]
489 pub iso_code: Option<&'a str>,
490 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
492 pub names: Names<'a>,
493 }
494
495 impl_is_empty_via_default!(Country<'_>);
496
497 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
502 pub struct RepresentedCountry<'a> {
503 #[serde(default, skip_serializing_if = "Option::is_none")]
505 pub geoname_id: Option<u32>,
506 #[serde(default, skip_serializing_if = "Option::is_none")]
508 pub is_in_european_union: Option<bool>,
509 #[serde(default, skip_serializing_if = "Option::is_none")]
512 pub iso_code: Option<&'a str>,
513 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
515 pub names: Names<'a>,
516 #[serde(rename = "type", default, skip_serializing_if = "Option::is_none")]
518 pub representation_type: Option<&'a str>,
519 }
520
521 impl_is_empty_via_default!(RepresentedCountry<'_>);
522
523 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
525 pub struct Traits {
526 #[serde(default, skip_serializing_if = "Option::is_none")]
529 pub is_anycast: Option<bool>,
530 }
531
532 impl_is_empty_via_default!(Traits);
533}
534
535pub mod city {
540 use super::Names;
541 use serde::{Deserialize, Serialize};
542
543 pub use super::country::{Continent, Country, RepresentedCountry, Traits};
544
545 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
547 pub struct City<'a> {
548 #[serde(default, skip_serializing_if = "Option::is_none")]
550 pub geoname_id: Option<u32>,
551 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
553 pub names: Names<'a>,
554 }
555
556 impl_is_empty_via_default!(City<'_>);
557
558 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
560 pub struct Location<'a> {
561 #[serde(default, skip_serializing_if = "Option::is_none")]
565 pub accuracy_radius: Option<u16>,
566 #[serde(default, skip_serializing_if = "Option::is_none")]
569 pub latitude: Option<f64>,
570 #[serde(default, skip_serializing_if = "Option::is_none")]
573 pub longitude: Option<f64>,
574 #[serde(default, skip_serializing_if = "Option::is_none")]
578 pub metro_code: Option<u16>,
579 #[serde(default, skip_serializing_if = "Option::is_none")]
582 pub time_zone: Option<&'a str>,
583 }
584
585 impl_is_empty_via_default!(Location<'_>);
586
587 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
589 pub struct Postal<'a> {
590 #[serde(default, skip_serializing_if = "Option::is_none")]
593 pub code: Option<&'a str>,
594 }
595
596 impl_is_empty_via_default!(Postal<'_>);
597
598 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
600 pub struct Subdivision<'a> {
601 #[serde(default, skip_serializing_if = "Option::is_none")]
603 pub geoname_id: Option<u32>,
604 #[serde(default, skip_serializing_if = "Option::is_none")]
607 pub iso_code: Option<&'a str>,
608 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
610 pub names: Names<'a>,
611 }
612
613 impl_is_empty_via_default!(Subdivision<'_>);
614}
615
616pub mod enterprise {
621 use super::Names;
622 use serde::{Deserialize, Serialize};
623
624 pub use super::country::{Continent, RepresentedCountry};
625
626 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
628 pub struct City<'a> {
629 #[serde(default, skip_serializing_if = "Option::is_none")]
632 pub confidence: Option<u8>,
633 #[serde(default, skip_serializing_if = "Option::is_none")]
635 pub geoname_id: Option<u32>,
636 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
638 pub names: Names<'a>,
639 }
640
641 impl_is_empty_via_default!(City<'_>);
642
643 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
645 pub struct Country<'a> {
646 #[serde(default, skip_serializing_if = "Option::is_none")]
649 pub confidence: Option<u8>,
650 #[serde(default, skip_serializing_if = "Option::is_none")]
652 pub geoname_id: Option<u32>,
653 #[serde(default, skip_serializing_if = "Option::is_none")]
655 pub is_in_european_union: Option<bool>,
656 #[serde(default, skip_serializing_if = "Option::is_none")]
659 pub iso_code: Option<&'a str>,
660 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
662 pub names: Names<'a>,
663 }
664
665 impl_is_empty_via_default!(Country<'_>);
666
667 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
669 pub struct Location<'a> {
670 #[serde(default, skip_serializing_if = "Option::is_none")]
674 pub accuracy_radius: Option<u16>,
675 #[serde(default, skip_serializing_if = "Option::is_none")]
678 pub latitude: Option<f64>,
679 #[serde(default, skip_serializing_if = "Option::is_none")]
682 pub longitude: Option<f64>,
683 #[serde(default, skip_serializing_if = "Option::is_none")]
687 pub metro_code: Option<u16>,
688 #[serde(default, skip_serializing_if = "Option::is_none")]
691 pub time_zone: Option<&'a str>,
692 }
693
694 impl_is_empty_via_default!(Location<'_>);
695
696 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
698 pub struct Postal<'a> {
699 #[serde(default, skip_serializing_if = "Option::is_none")]
702 pub code: Option<&'a str>,
703 #[serde(default, skip_serializing_if = "Option::is_none")]
706 pub confidence: Option<u8>,
707 }
708
709 impl_is_empty_via_default!(Postal<'_>);
710
711 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
713 pub struct Subdivision<'a> {
714 #[serde(default, skip_serializing_if = "Option::is_none")]
717 pub confidence: Option<u8>,
718 #[serde(default, skip_serializing_if = "Option::is_none")]
720 pub geoname_id: Option<u32>,
721 #[serde(default, skip_serializing_if = "Option::is_none")]
724 pub iso_code: Option<&'a str>,
725 #[serde(borrow, default, skip_serializing_if = "Names::is_empty")]
727 pub names: Names<'a>,
728 }
729
730 impl_is_empty_via_default!(Subdivision<'_>);
731
732 #[derive(Deserialize, Serialize, Clone, Debug, Default, PartialEq)]
736 pub struct Traits<'a> {
737 #[serde(default, skip_serializing_if = "Option::is_none")]
739 pub autonomous_system_number: Option<u32>,
740 #[serde(default, skip_serializing_if = "Option::is_none")]
742 pub autonomous_system_organization: Option<&'a str>,
743 #[serde(default, skip_serializing_if = "Option::is_none")]
746 pub connection_type: Option<&'a str>,
747 #[serde(default, skip_serializing_if = "Option::is_none")]
750 pub domain: Option<&'a str>,
751 #[serde(default, skip_serializing_if = "Option::is_none")]
753 pub is_anonymous: Option<bool>,
754 #[serde(default, skip_serializing_if = "Option::is_none")]
756 pub is_anonymous_vpn: Option<bool>,
757 #[serde(default, skip_serializing_if = "Option::is_none")]
760 pub is_anycast: Option<bool>,
761 #[serde(default, skip_serializing_if = "Option::is_none")]
763 pub is_hosting_provider: Option<bool>,
764 #[serde(default, skip_serializing_if = "Option::is_none")]
766 pub isp: Option<&'a str>,
767 #[serde(default, skip_serializing_if = "Option::is_none")]
769 pub is_public_proxy: Option<bool>,
770 #[serde(default, skip_serializing_if = "Option::is_none")]
773 pub is_residential_proxy: Option<bool>,
774 #[serde(default, skip_serializing_if = "Option::is_none")]
776 pub is_tor_exit_node: Option<bool>,
777 #[serde(default, skip_serializing_if = "Option::is_none")]
780 pub mobile_country_code: Option<&'a str>,
781 #[serde(default, skip_serializing_if = "Option::is_none")]
784 pub mobile_network_code: Option<&'a str>,
785 #[serde(default, skip_serializing_if = "Option::is_none")]
787 pub organization: Option<&'a str>,
788 #[serde(default, skip_serializing_if = "Option::is_none")]
793 pub user_type: Option<&'a str>,
794 }
795
796 impl_is_empty_via_default!(Traits<'_>);
797}
798
799#[cfg(test)]
800mod tests {
801 use serde::de::value::{Error as ValueError, SeqDeserializer, U8Deserializer};
802 use serde::Deserialize;
803
804 use super::{deserialize_subdivisions, City, Enterprise, MAX_SUBDIVISIONS};
805 use crate::{decoder::Decoder, MaxMindDbError};
806
807 fn record_with_declared_subdivisions(declared_count: usize, encoded_count: usize) -> Vec<u8> {
808 assert!((29..=284).contains(&declared_count));
809 assert!(encoded_count <= declared_count);
810
811 let mut encoded = vec![0xe1, 0x4c]; encoded.extend_from_slice(b"subdivisions");
813 encoded.extend_from_slice(&[0x1d, 0x04, (declared_count - 29) as u8]);
814 encoded.resize(encoded.len() + encoded_count, 0xe0); encoded
816 }
817
818 fn record_with_subdivisions(count: usize) -> Vec<u8> {
819 record_with_declared_subdivisions(count, count)
820 }
821
822 struct UnknownSize<I>(I);
823
824 impl<I> Iterator for UnknownSize<I>
825 where
826 I: Iterator,
827 {
828 type Item = I::Item;
829
830 fn next(&mut self) -> Option<Self::Item> {
831 self.0.next()
832 }
833
834 fn size_hint(&self) -> (usize, Option<usize>) {
835 (0, None)
836 }
837 }
838
839 fn subdivisions_without_size_hint(count: usize) -> Result<Vec<u8>, ValueError> {
840 let values = (0..count).map(|_| U8Deserializer::<ValueError>::new(0));
841 deserialize_subdivisions(SeqDeserializer::new(UnknownSize(values)))
842 }
843
844 #[test]
845 fn city_accepts_maximum_subdivisions() {
846 let encoded = record_with_subdivisions(MAX_SUBDIVISIONS);
847 let city = City::deserialize(&mut Decoder::new(&encoded, 0)).unwrap();
848
849 assert_eq!(city.subdivisions.len(), MAX_SUBDIVISIONS);
850 }
851
852 #[test]
853 fn city_rejects_excessive_subdivisions() {
854 let encoded = record_with_subdivisions(MAX_SUBDIVISIONS + 1);
855 let err = City::deserialize(&mut Decoder::new(&encoded, 0)).unwrap_err();
856
857 assert!(matches!(*err, MaxMindDbError::Decoding { .. }));
858 assert!(err
859 .to_string()
860 .contains("subdivisions exceeds maximum length of 32"));
861 }
862
863 #[test]
864 fn enterprise_rejects_excessive_subdivisions() {
865 let encoded = record_with_subdivisions(MAX_SUBDIVISIONS + 1);
866 let err = Enterprise::deserialize(&mut Decoder::new(&encoded, 0)).unwrap_err();
867
868 assert!(matches!(*err, MaxMindDbError::Decoding { .. }));
869 assert!(err
870 .to_string()
871 .contains("subdivisions exceeds maximum length of 32"));
872 }
873
874 #[test]
875 fn subdivision_limit_does_not_depend_on_a_size_hint() {
876 let subdivisions = subdivisions_without_size_hint(MAX_SUBDIVISIONS).unwrap();
877 assert_eq!(subdivisions.len(), MAX_SUBDIVISIONS);
878
879 let err = subdivisions_without_size_hint(MAX_SUBDIVISIONS + 1).unwrap_err();
880 assert!(err
881 .to_string()
882 .contains("subdivisions exceeds maximum length of 32"));
883 }
884
885 #[test]
886 fn city_json_deserialization_enforces_subdivision_limit() {
887 let mut city = City::default();
888 city.subdivisions
889 .resize_with(MAX_SUBDIVISIONS, Default::default);
890 let json = serde_json::to_string(&city).unwrap();
891 let decoded = serde_json::from_str::<City<'_>>(&json).unwrap();
892 assert_eq!(decoded.subdivisions.len(), MAX_SUBDIVISIONS);
893
894 city.subdivisions.push(Default::default());
895 let json = serde_json::to_string(&city).unwrap();
896 let err = serde_json::from_str::<City<'_>>(&json).unwrap_err();
897 assert!(err
898 .to_string()
899 .contains("subdivisions exceeds maximum length of 32"));
900 }
901
902 #[test]
903 fn city_rejects_truncated_oversized_subdivisions() {
904 let encoded = record_with_declared_subdivisions(MAX_SUBDIVISIONS + 1, 1);
905 let err = City::deserialize(&mut Decoder::new(&encoded, 0)).unwrap_err();
906
907 assert!(matches!(*err, MaxMindDbError::InvalidDatabase { .. }));
908 assert!(err.to_string().contains("unexpected end of buffer"));
909 }
910
911 #[test]
912 fn enterprise_rejects_truncated_oversized_subdivisions() {
913 let encoded = record_with_declared_subdivisions(MAX_SUBDIVISIONS + 1, 1);
914 let err = Enterprise::deserialize(&mut Decoder::new(&encoded, 0)).unwrap_err();
915
916 assert!(matches!(*err, MaxMindDbError::InvalidDatabase { .. }));
917 assert!(err.to_string().contains("unexpected end of buffer"));
918 }
919}