• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use log::warn;
2 
3 use crate::packets::att;
4 
5 use super::att_database::AttDatabase;
6 
7 /// This struct handles all ATT commands.
8 pub struct AttCommandHandler<Db: AttDatabase> {
9     db: Db,
10 }
11 
12 impl<Db: AttDatabase> AttCommandHandler<Db> {
new(db: Db) -> Self13     pub fn new(db: Db) -> Self {
14         Self { db }
15     }
16 
process_packet(&self, packet: att::Att)17     pub fn process_packet(&self, packet: att::Att) {
18         let snapshotted_db = self.db.snapshot();
19         match packet.opcode {
20             att::AttOpcode::WriteCommand => {
21                 let Ok(packet) = att::AttWriteCommand::try_from(packet) else {
22                     warn!("failed to parse WRITE_COMMAND packet");
23                     return;
24                 };
25                 snapshotted_db.write_no_response_attribute(packet.handle.into(), &packet.value);
26             }
27             _ => {
28                 warn!("Dropping unsupported opcode {:?}", packet.opcode);
29             }
30         }
31     }
32 }
33 
34 #[cfg(test)]
35 mod test {
36     use crate::core::uuid::Uuid;
37     use crate::gatt::ids::AttHandle;
38     use crate::gatt::server::att_database::{AttAttribute, AttDatabase};
39     use crate::gatt::server::command_handler::AttCommandHandler;
40     use crate::gatt::server::gatt_database::AttPermissions;
41     use crate::gatt::server::test::test_att_db::TestAttDatabase;
42     use crate::packets::att;
43     use crate::utils::task::block_on_locally;
44 
45     #[test]
test_write_command()46     fn test_write_command() {
47         // arrange
48         let db = TestAttDatabase::new(vec![(
49             AttAttribute {
50                 handle: AttHandle(3),
51                 type_: Uuid::new(0x1234),
52                 permissions: AttPermissions::READABLE | AttPermissions::WRITABLE_WITHOUT_RESPONSE,
53             },
54             vec![1, 2, 3],
55         )]);
56         let handler = AttCommandHandler { db: db.clone() };
57         let data = [1, 2];
58 
59         // act: send write command
60         let att_view = att::AttWriteCommand { handle: AttHandle(3).into(), value: data.to_vec() }
61             .try_into()
62             .unwrap();
63         handler.process_packet(att_view);
64 
65         // assert: the db has been updated
66         assert_eq!(block_on_locally(db.read_attribute(AttHandle(3))).unwrap(), data);
67     }
68 
69     #[test]
test_unsupported_command()70     fn test_unsupported_command() {
71         // arrange
72         let db = TestAttDatabase::new(vec![]);
73         let handler = AttCommandHandler { db };
74 
75         // act: send a packet that should not be handled here
76         let att_view = att::AttErrorResponse {
77             opcode_in_error: att::AttOpcode::ExchangeMtuRequest,
78             handle_in_error: AttHandle(1).into(),
79             error_code: att::AttErrorCode::UnlikelyError,
80         }
81         .try_into()
82         .unwrap();
83         handler.process_packet(att_view);
84 
85         // assert: nothing happens (we crash if anything is unhandled within a mock)
86     }
87 }
88