rust/bt-gatt: Support offset and truncation in FakePeerService read

Test: cargo test
Change-Id: Ia79c0d06484c0fa6c56f6388591f04abd9b9bc41
Reviewed-on: https://bluetooth-review.googlesource.com/c/bluetooth/+/3640
diff --git a/rust/bt-gatt/src/test_utils.rs b/rust/bt-gatt/src/test_utils.rs
index 7f274f2..c31ffab 100644
--- a/rust/bt-gatt/src/test_utils.rs
+++ b/rust/bt-gatt/src/test_utils.rs
@@ -107,16 +107,22 @@
     fn read_characteristic<'a>(
         &self,
         handle: &Handle,
-        _offset: u16,
+        offset: u16,
         buf: &'a mut [u8],
     ) -> <FakeTypes as GattTypes>::ReadFut<'a> {
         let read_characteristics = &(*self.inner.lock()).characteristics;
         let Some((_, value)) = read_characteristics.get(handle) else {
             return ready(Err(Error::Gatt(GattError::InvalidHandle)));
         };
-        let len = std::cmp::min(buf.len(), value.len());
-        buf[..len].copy_from_slice(&value[..len]);
-        ready(Ok((len, false)))
+        let offset = offset as usize;
+        if offset > value.len() {
+            return ready(Err(Error::Gatt(GattError::InvalidOffset)));
+        }
+        let remaining = &value[offset..];
+        let len = std::cmp::min(buf.len(), remaining.len());
+        buf[..len].copy_from_slice(&remaining[..len]);
+        let truncated = remaining.len() > buf.len();
+        ready(Ok((len, truncated)))
     }
 
     // For testing, should call `expect_characteristic_value` with the expected
diff --git a/rust/bt-gatt/src/tests.rs b/rust/bt-gatt/src/tests.rs
index d8188e4..8b95ec1 100644
--- a/rust/bt-gatt/src/tests.rs
+++ b/rust/bt-gatt/src/tests.rs
@@ -144,10 +144,46 @@
     // Successfully reads the updated value.
     let mut read_result = fake_peer_service.read_characteristic(&Handle(0x1), 0, &mut buf[..]);
     let polled = read_result.poll_unpin(&mut noop_cx);
-    assert_matches!(polled, Poll::Ready(Ok((len, _))) => {
+    assert_matches!(polled, Poll::Ready(Ok((len, truncated))) => {
         assert_eq!(len, 4);
-        assert_eq!(buf[..len], vec![0,1,2,3]);
+        assert_eq!(buf[..len], vec![0, 1, 2, 3]);
+        assert!(!truncated);
     });
+
+    // Read with small buffer (buf.len() < value.len()) sets truncated = true
+    let mut small_buf = vec![0; 2];
+    let mut read_result =
+        fake_peer_service.read_characteristic(&Handle(0x1), 0, &mut small_buf[..]);
+    let polled = read_result.poll_unpin(&mut noop_cx);
+    assert_matches!(polled, Poll::Ready(Ok((len, truncated))) => {
+        assert_eq!(len, 2);
+        assert_eq!(small_buf[..len], vec![0, 1]);
+        assert!(truncated);
+    });
+
+    // Read with valid offset (offset = 2 < value.len())
+    let mut read_result = fake_peer_service.read_characteristic(&Handle(0x1), 2, &mut buf[..]);
+    let polled = read_result.poll_unpin(&mut noop_cx);
+    assert_matches!(polled, Poll::Ready(Ok((len, truncated))) => {
+        assert_eq!(len, 2);
+        assert_eq!(buf[..len], vec![2, 3]);
+        assert!(!truncated);
+    });
+
+    // Read with offset equal to value length (offset = 4 == value.len()) returns 0
+    // bytes read
+    let mut read_result = fake_peer_service.read_characteristic(&Handle(0x1), 4, &mut buf[..]);
+    let polled = read_result.poll_unpin(&mut noop_cx);
+    assert_matches!(polled, Poll::Ready(Ok((len, truncated))) => {
+        assert_eq!(len, 0);
+        assert!(!truncated);
+    });
+
+    // Read with offset too large (offset = 5 > value.len()) returns
+    // GattError::InvalidOffset
+    let mut read_result = fake_peer_service.read_characteristic(&Handle(0x1), 5, &mut buf[..]);
+    let polled = read_result.poll_unpin(&mut noop_cx);
+    assert_matches!(polled, Poll::Ready(Err(Error::Gatt(GattError::InvalidOffset))));
 }
 
 #[test]