aboutsummaryrefslogtreecommitdiff
path: root/nearby/presence/np_adv/src/extended/de_type.rs
diff options
context:
space:
mode:
Diffstat (limited to 'nearby/presence/np_adv/src/extended/de_type.rs')
-rw-r--r--nearby/presence/np_adv/src/extended/de_type.rs143
1 files changed, 143 insertions, 0 deletions
diff --git a/nearby/presence/np_adv/src/extended/de_type.rs b/nearby/presence/np_adv/src/extended/de_type.rs
new file mode 100644
index 0000000..105d15e
--- /dev/null
+++ b/nearby/presence/np_adv/src/extended/de_type.rs
@@ -0,0 +1,143 @@
+// Copyright 2022 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! V1 DE type types
+use crate::de_type::{EncryptedIdentityDataElementType, IdentityDataElementType};
+
+/// Data element types for extended advertisements
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
+pub struct DeType {
+ // 4 billion type codes should be enough for anybody
+ code: u32,
+}
+
+impl DeType {
+ /// A `const` equivalent to `From<u32>` since trait methods can't yet be const.
+ pub(crate) const fn const_from(value: u32) -> Self {
+ Self { code: value }
+ }
+
+ /// Returns the type as a u32
+ pub fn as_u32(&self) -> u32 {
+ self.code
+ }
+}
+
+impl From<u8> for DeType {
+ fn from(value: u8) -> Self {
+ DeType { code: value as u32 }
+ }
+}
+
+impl From<u32> for DeType {
+ fn from(value: u32) -> Self {
+ DeType { code: value }
+ }
+}
+
+pub(crate) trait ExtendedDataElementType: Sized {
+ /// A type code for use in the DE header.
+ fn type_code(&self) -> DeType;
+ /// Returns the matching type for the code, else `None`
+ fn from_type_code(de_type: DeType) -> Option<Self>;
+}
+
+impl ExtendedDataElementType for IdentityDataElementType {
+ fn type_code(&self) -> DeType {
+ DeType::from(self.shared_type_code())
+ }
+
+ fn from_type_code(de_type: DeType) -> Option<Self> {
+ de_type.code.try_into().ok().and_then(Self::from_shared_type_code)
+ }
+}
+
+impl ExtendedDataElementType for EncryptedIdentityDataElementType {
+ fn type_code(&self) -> DeType {
+ DeType::from(self.as_identity_data_element_type().shared_type_code())
+ }
+
+ fn from_type_code(code: DeType) -> Option<Self> {
+ IdentityDataElementType::from_type_code(code)
+ .and_then(|idet| idet.as_encrypted_identity_de_type())
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ extern crate std;
+
+ use super::*;
+ use std::{collections, fmt};
+
+ #[test]
+ fn identity_type_codes_are_consistent() {
+ de_type_codes_are_consistent::<IdentityDataElementType>()
+ }
+
+ #[test]
+ fn encrypted_identity_type_codes_are_consistent() {
+ de_type_codes_are_consistent::<EncryptedIdentityDataElementType>()
+ }
+
+ #[test]
+ fn identity_type_codes_are_distinct() {
+ de_distinct_type_codes::<IdentityDataElementType>()
+ }
+
+ #[test]
+ fn encrypted_identity_type_codes_are_distinct() {
+ de_distinct_type_codes::<EncryptedIdentityDataElementType>()
+ }
+
+ #[test]
+ fn identity_no_accidentally_mapped_type_codes() {
+ de_no_accidentally_mapped_type_codes::<IdentityDataElementType>()
+ }
+
+ #[test]
+ fn encrypted_identity_no_accidentally_mapped_type_codes() {
+ de_no_accidentally_mapped_type_codes::<EncryptedIdentityDataElementType>()
+ }
+
+ fn de_type_codes_are_consistent<
+ D: PartialEq + fmt::Debug + ExtendedDataElementType + strum::IntoEnumIterator,
+ >() {
+ for det in D::iter() {
+ let actual = D::from_type_code(det.type_code());
+ assert_eq!(Some(det), actual)
+ }
+ }
+
+ fn de_distinct_type_codes<
+ D: PartialEq + fmt::Debug + ExtendedDataElementType + strum::IntoEnumIterator,
+ >() {
+ let codes = D::iter().map(|det| det.type_code()).collect::<collections::HashSet<_>>();
+ assert_eq!(codes.len(), D::iter().count());
+ }
+
+ fn de_no_accidentally_mapped_type_codes<
+ D: PartialEq + fmt::Debug + ExtendedDataElementType + strum::IntoEnumIterator,
+ >() {
+ let codes = D::iter().map(|det| det.type_code()).collect::<collections::HashSet<_>>();
+ // not going to try all 4 billion possibilities, but we can make an effort
+ for possible_code in 0_u32..100_000 {
+ if codes.contains(&possible_code.into()) {
+ continue;
+ }
+
+ assert_eq!(None, D::from_type_code(possible_code.into()));
+ }
+ }
+}