gatt 0.3.0-alpha.1

Bluetooth Generic Attribute Protocol Server
Documentation
use std::collections::BTreeMap;
use std::iter::FromIterator;
use std::ops::RangeInclusive;

use att::packet::ErrorCode;
use att::uuid::Uuid16;
use att::{Handle, Uuid};

use crate::attribute::{Attribute, Error as AttrError};

type Result<T> = std::result::Result<T, (Handle, ErrorCode)>;

#[derive(Debug)]
pub(crate) struct Database {
    attrs: BTreeMap<Handle, Attribute>,
}

impl Database {
    #[allow(clippy::type_complexity)]
    pub(crate) fn read_by_group_type(
        &self,
        range: RangeInclusive<Handle>,
        uuid: &Uuid,
        authorized: bool,
        authenticated: bool,
    ) -> Result<Vec<(Handle, Handle, Box<[u8]>)>> {
        let start = range.start().clone();

        if range.start() == &Handle::from(0x0000) || range.start() > range.end() {
            return Err((start, ErrorCode::InvalidHandle));
        }

        let mut result = vec![];
        let mut current = None as Option<(&Handle, Box<[u8]>)>;
        let mut last = &Handle::from(0x0000);
        let mut val_len = None;

        for (key, val) in self.attrs.range(range) {
            if val.attr_type() == uuid {
                if let Some((start, val)) = current {
                    val_len = Some(val.len());
                    result.push((start.clone(), last.clone(), val))
                }

                let b = match val.get(authorized, authenticated) {
                    Ok(b) => b,
                    Err(AttrError::PermissionDenied) => {
                        return Err((key.clone(), ErrorCode::ReadNotPermitted))
                    }
                    Err(AttrError::AuthorizationRequired) => {
                        return Err((key.clone(), ErrorCode::InsufficientAuthorization))
                    }
                    Err(AttrError::AuthenticationRequired) => {
                        return Err((key.clone(), ErrorCode::InsufficientAuthentication))
                    }
                    _ => unreachable!(),
                };
                if let Some(len) = val_len {
                    if len != b.len() {
                        return Ok(result);
                    }
                };
                current = Some((key, b));
            }

            last = key;
        }

        if let Some((start, val)) = current {
            result.push((start.clone(), last.clone(), val));
            Ok(result)
        } else {
            Err((start, ErrorCode::AttributeNotFound))
        }
    }

    pub(crate) fn find_by_type_value(
        &self,
        range: RangeInclusive<Handle>,
        uuid: &Uuid16,
        value: &[u8],
        authorized: bool,
        authenticated: bool,
    ) -> Result<Vec<(Handle, Handle)>> {
        let start = range.start().clone();

        let result = self
            .read_by_group_type(range, &uuid.clone().into(), authorized, authenticated)?
            .into_iter()
            .filter_map(|(handle, end, v)| {
                if &*v == value {
                    Some((handle, end))
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();
        if result.is_empty() {
            return Err((start, ErrorCode::AttributeNotFound));
        }
        Ok(result)
    }

    pub(crate) fn read_by_type(
        &self,
        range: RangeInclusive<Handle>,
        uuid: &Uuid,
        authorized: bool,
        authenticated: bool,
    ) -> Result<Vec<(Handle, Box<[u8]>)>> {
        let start = range.start().clone();

        if range.start() == &Handle::from(0x0000) || range.start() > range.end() {
            return Err((start, ErrorCode::InvalidHandle));
        }

        let result = self
            .attrs
            .range(range)
            .filter_map(|(k, v)| {
                if v.attr_type() == uuid {
                    match v.get(authorized, authenticated) {
                        Ok(b) => Some(Ok((k.clone(), b))),
                        Err(AttrError::PermissionDenied) => {
                            Some(Err((k.clone(), ErrorCode::ReadNotPermitted)))
                        }
                        Err(AttrError::AuthorizationRequired) => {
                            Some(Err((k.clone(), ErrorCode::InsufficientAuthorization)))
                        }
                        Err(AttrError::AuthenticationRequired) => {
                            Some(Err((k.clone(), ErrorCode::InsufficientAuthentication)))
                        }
                        _ => unreachable!(),
                    }
                } else {
                    None
                }
            })
            .collect::<Result<Vec<_>>>()?;

        if result.is_empty() {
            Err((start, ErrorCode::AttributeNotFound))
        } else {
            Ok(result)
        }
    }

    pub(crate) fn find_information(
        &self,
        range: RangeInclusive<Handle>,
    ) -> Result<Vec<(Handle, Uuid)>> {
        let start = range.start().clone();

        if range.start() == &Handle::from(0x0000) || range.start() > range.end() {
            return Err((start, ErrorCode::InvalidHandle));
        }

        let mut result = self
            .attrs
            .range(range)
            .map(|(_, v)| (v.handle(), v.attr_type()));
        match result.next() {
            Some(v @ (_, Uuid::Uuid16(_))) => {
                let result = result.take_while(|(_, b)| matches!(b, Uuid::Uuid16(_)));
                Ok(std::iter::once(v)
                    .chain(result)
                    .map(|(a, b)| (a.clone(), b.clone()))
                    .collect())
            }
            Some(v @ (_, Uuid::Uuid128(_))) => {
                let result = result.take_while(|(_, b)| matches!(b, Uuid::Uuid128(_)));
                Ok(std::iter::once(v)
                    .chain(result)
                    .map(|(a, b)| (a.clone(), b.clone()))
                    .collect())
            }
            None => Err((start, ErrorCode::AttributeNotFound)),
        }
    }

    pub(crate) fn read(
        &self,
        handle: &Handle,
        authorized: bool,
        authenticated: bool,
    ) -> Result<Box<[u8]>> {
        if handle == &0x0000.into() {
            return Err((handle.clone(), ErrorCode::InvalidHandle));
        }

        if let Some(v) = self.attrs.get(handle) {
            match v.get(authorized, authenticated) {
                Ok(v) => Ok(v),
                Err(AttrError::PermissionDenied) => {
                    Err((handle.clone(), ErrorCode::ReadNotPermitted))
                }
                Err(AttrError::AuthorizationRequired) => {
                    Err((handle.clone(), ErrorCode::InsufficientAuthorization))
                }
                Err(AttrError::AuthenticationRequired) => {
                    Err((handle.clone(), ErrorCode::InsufficientAuthentication))
                }
                _ => unreachable!(),
            }
        } else {
            Err((handle.clone(), ErrorCode::AttributeNotFound))
        }
    }

    pub(crate) fn write(
        &mut self,
        handle: &Handle,
        val: &[u8],
        authorized: bool,
        authenticated: bool,
    ) -> Result<()> {
        if handle == &0x0000.into() {
            return Err((handle.clone(), ErrorCode::InvalidHandle));
        }

        if let Some(v) = self.attrs.get_mut(handle) {
            match v.set(val, authorized, authenticated) {
                Ok(_) => Ok(()),
                Err(AttrError::PermissionDenied) => {
                    Err((handle.clone(), ErrorCode::WriteNotPermitted))
                }
                Err(AttrError::AuthorizationRequired) => {
                    Err((handle.clone(), ErrorCode::InsufficientAuthorization))
                }
                Err(AttrError::AuthenticationRequired) => {
                    Err((handle.clone(), ErrorCode::InsufficientAuthentication))
                }
                Err(AttrError::InvalidDataLength) => {
                    Err((handle.clone(), ErrorCode::InvalidAttributeValueLength))
                }
            }
        } else {
            Err((handle.clone(), ErrorCode::AttributeNotFound))
        }
    }
}

impl FromIterator<Attribute> for Database {
    fn from_iter<T: IntoIterator<Item = Attribute>>(iter: T) -> Self {
        Self {
            attrs: iter.into_iter().map(|a| (a.handle().clone(), a)).collect(),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::attribute::{
        CharacteristicProperties, ClientCharacteristicConfiguration, Permission,
    };

    #[test]
    fn test_read_by_group_type() {
        let db = example_db();

        let result = db
            .read_by_group_type(
                0x0001.into()..=0xFFFF.into(),
                &Uuid::new_uuid16(0x2800),
                false,
                false,
            )
            .unwrap();
        assert_eq!(
            &result,
            &[
                (0x0001.into(), 0x0005.into(), vec![0x00, 0x18].into()),
                (0x000C.into(), 0x000F.into(), vec![0x01, 0x18].into()),
                (0x0010.into(), 0x0016.into(), vec![0x0A, 0x18].into()),
            ]
        );

        let result = db
            .read_by_group_type(
                0x0017.into()..=0xFFFF.into(),
                &Uuid::new_uuid16(0x2800),
                false,
                false,
            )
            .unwrap();
        assert_eq!(
            &result,
            &[(
                0x0020.into(),
                0x0020.into(),
                vec![
                    0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
                    0x00, 0x00, 0x00
                ]
                .into()
            ),]
        );

        let result = db
            .read_by_group_type(
                0x0021.into()..=0xFFFF.into(),
                &Uuid::new_uuid16(0x2800),
                false,
                false,
            )
            .unwrap();
        assert_eq!(
            &result,
            &[(0x0023.into(), 0x0027.into(), vec![0x0F, 0x18].into()),]
        );

        let result = db
            .read_by_group_type(
                0x0028.into()..=0xFFFF.into(),
                &Uuid::new_uuid16(0x2800),
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0028.into(), ErrorCode::AttributeNotFound));

        let result = db
            .read_by_group_type(
                0x0002.into()..=0x0001.into(),
                &Uuid::new_uuid16(0x2800),
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0002.into(), ErrorCode::InvalidHandle));

        let result = db
            .read_by_group_type(
                0x0000.into()..=0x0001.into(),
                &Uuid::new_uuid16(0x2800),
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0000.into(), ErrorCode::InvalidHandle));
    }

    #[test]
    fn test_find_by_type_value() {
        let db = example_db();

        let result = db
            .find_by_type_value(
                0x0001.into()..=0xFFFF.into(),
                &Uuid16::new(0x2800),
                &vec![0x01, 0x18],
                false,
                false,
            )
            .unwrap();
        assert_eq!(&result, &[(0x000C.into(), 0x000F.into())]);
        let result = db
            .find_by_type_value(
                0x0010.into()..=0xFFFF.into(),
                &Uuid16::new(0x2800),
                &vec![0x01, 0x18],
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0010.into(), ErrorCode::AttributeNotFound));
    }

    #[test]
    fn test_read_by_type() {
        let db = example_db();

        let result = db
            .read_by_type(
                0x0001.into()..=0x000b.into(),
                &Uuid::new_uuid16(0x2802),
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0001.into(), ErrorCode::AttributeNotFound));

        let result = db
            .read_by_type(
                0x0001.into()..=0x000b.into(),
                &Uuid::new_uuid16(0x2803),
                false,
                false,
            )
            .unwrap();
        assert_eq!(
            result,
            &[
                (0x0002.into(), vec![0x08, 0x03, 0x00, 0x00, 0x2A].into()),
                (0x0004.into(), vec![0x02, 0x05, 0x00, 0x01, 0x2A].into()),
            ]
        );

        let result = db
            .read_by_type(
                0x0005.into()..=0x000b.into(),
                &Uuid::new_uuid16(0x2803),
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0005.into(), ErrorCode::AttributeNotFound));

        let result = db
            .read_by_type(
                0x0002.into()..=0x0001.into(),
                &Uuid::new_uuid16(0x2802),
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0002.into(), ErrorCode::InvalidHandle));

        let result = db
            .read_by_type(
                0x0000.into()..=0x0001.into(),
                &Uuid::new_uuid16(0x2802),
                false,
                false,
            )
            .unwrap_err();
        assert_eq!(result, (0x0000.into(), ErrorCode::InvalidHandle));
    }

    #[test]
    fn test_find_information() {
        let db = example_db();

        let result = db
            .find_information(0x0006.into()..=0x000B.into())
            .unwrap_err();
        assert_eq!(result, (0x0006.into(), ErrorCode::AttributeNotFound));

        let result = db.find_information(0x000F.into()..=0x000F.into()).unwrap();
        assert_eq!(result, &[(0x000F.into(), Uuid::new_uuid16(0x2902)),]);

        let result = db.find_information(0x0026.into()..=0x0027.into()).unwrap();
        assert_eq!(
            result,
            &[
                (0x0026.into(), Uuid::new_uuid16(0x2902)),
                (0x0027.into(), Uuid::new_uuid16(0x2904)),
            ]
        );

        let result = db
            .find_information(0x0002.into()..=0x0001.into())
            .unwrap_err();
        assert_eq!(result, (0x0002.into(), ErrorCode::InvalidHandle));

        let result = db
            .find_information(0x0000.into()..=0x0001.into())
            .unwrap_err();
        assert_eq!(result, (0x0000.into(), ErrorCode::InvalidHandle));
    }

    #[test]
    fn test_read() {
        let db = example_db();

        let result = db.read(&0x0005.into(), false, false).unwrap();
        assert_eq!(&*result, &b"abc"[..]);

        let result = db.read(&0x0000.into(), false, false).unwrap_err();
        assert_eq!(result, (0x0000.into(), ErrorCode::InvalidHandle));
    }

    #[test]
    fn test_write() {
        let mut db = example_db();

        let _result = db
            .write(&0x000F.into(), &vec![0x00, 0x00], false, false)
            .unwrap();

        let result = db.write(&0x0000.into(), &vec![], false, false).unwrap_err();
        assert_eq!(result, (0x0000.into(), ErrorCode::InvalidHandle));
    }

    fn example_db() -> Database {
        vec![
            Attribute::new_primary_service(0x0001.into(), Uuid::new_uuid16(0x1800)),
            Attribute::new_characteristic(
                0x0002.into(),
                CharacteristicProperties::WRITE,
                0x0003.into(),
                Uuid::new_uuid16(0x2A00),
            ),
            Attribute::new_characteristic_value(
                0x0003.into(),
                Uuid::new_uuid16(0x2A00),
                [].into(),
                Permission::WRITEABLE,
            ),
            Attribute::new_characteristic(
                0x0004.into(),
                CharacteristicProperties::READ,
                0x0005.into(),
                Uuid::new_uuid16(0x2A01),
            ),
            Attribute::new_characteristic_value(
                0x0005.into(),
                Uuid::new_uuid16(0x2A01),
                b"abc".as_ref().into(),
                Permission::READABLE,
            ),
            Attribute::new_primary_service(0x000C.into(), Uuid::new_uuid16(0x1801)),
            Attribute::new_characteristic(
                0x000D.into(),
                CharacteristicProperties::INDICATE,
                0x000E.into(),
                Uuid::new_uuid16(0x2A05),
            ),
            Attribute::new_characteristic_value(
                0x000E.into(),
                Uuid::new_uuid16(0x2A05),
                [].into(),
                Permission::READABLE,
            ),
            Attribute::new_client_characteristic_configuration(
                0x000F.into(),
                ClientCharacteristicConfiguration::empty(),
                Permission::READABLE | Permission::WRITEABLE,
            ),
            Attribute::new_primary_service(0x0010.into(), Uuid::new_uuid16(0x180A)),
            Attribute::new_characteristic(
                0x0011.into(),
                CharacteristicProperties::READ,
                0x0012.into(),
                Uuid::new_uuid16(0x2A29),
            ),
            Attribute::new_characteristic_value(
                0x0012.into(),
                Uuid::new_uuid16(0x2A29),
                [].into(),
                Permission::READABLE,
            ),
            Attribute::new_characteristic(
                0x0013.into(),
                CharacteristicProperties::READ,
                0x0014.into(),
                Uuid::new_uuid16(0x2A24),
            ),
            Attribute::new_characteristic_value(
                0x0014.into(),
                Uuid::new_uuid16(0x2A24),
                [].into(),
                Permission::READABLE,
            ),
            Attribute::new_characteristic(
                0x0015.into(),
                CharacteristicProperties::READ,
                0x0016.into(),
                Uuid::new_uuid16(0x2A25),
            ),
            Attribute::new_characteristic_value(
                0x0016.into(),
                Uuid::new_uuid16(0x2A25),
                [].into(),
                Permission::READABLE,
            ),
            Attribute::new_primary_service(0x0020.into(), Uuid::new_uuid128(0x1234)),
            Attribute::new_primary_service(0x0023.into(), Uuid::new_uuid16(0x180F)),
            Attribute::new_characteristic(
                0x0024.into(),
                CharacteristicProperties::NOTIFY | CharacteristicProperties::READ,
                0x0025.into(),
                Uuid::new_uuid16(0x2A19),
            ),
            Attribute::new_characteristic_value(
                0x0025.into(),
                Uuid::new_uuid16(0x2A19),
                [].into(),
                Permission::READABLE,
            ),
            Attribute::new_client_characteristic_configuration(
                0x0026.into(),
                ClientCharacteristicConfiguration::empty(),
                Permission::READABLE | Permission::WRITEABLE,
            ),
            Attribute::new_characteristic_presentation_format(0x0027.into(), 0, 0, 0, 0, 0),
        ]
        .into_iter()
        .collect::<Database>()
    }
}