[rust] Add bt-common crate
Common datatypes for Core Specification defined tupes used
throughout Specifications, as well as assigned numbers and
common concepts such as Peer IDs.
Test: cargo build && cargo test
Change-Id: Ief099564ae5238e741e95cf9375702a5ae864e52
Reviewed-on: https://bluetooth-review.git.corp.google.com/c/bluetooth/+/1240
Reviewed-by: Dayeong Lee <dayeonglee@google.com>
diff --git a/rust/bt-common/.gitignore b/rust/bt-common/.gitignore
new file mode 100644
index 0000000..438e2b1
--- /dev/null
+++ b/rust/bt-common/.gitignore
@@ -0,0 +1,17 @@
+# Generated by Cargo
+# will have compiled files and executables
+debug/
+target/
+
+# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries
+# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html
+Cargo.lock
+
+# These are backup files generated by rustfmt
+**/*.rs.bk
+
+# MSVC Windows builds of rustc generate these, which store debugging information
+*.pdb
+
+# Vim swap files.
+*.swp
diff --git a/rust/bt-common/Cargo.toml b/rust/bt-common/Cargo.toml
new file mode 100644
index 0000000..0e131a5
--- /dev/null
+++ b/rust/bt-common/Cargo.toml
@@ -0,0 +1,9 @@
+[package]
+name = "bt-common"
+version = "0.0.1"
+edition = "2021"
+license = "BSD-2-Clause"
+
+[dependencies.uuid]
+version = "1.4.1"
+features = ["macro-diagnostics"]
diff --git a/rust/bt-common/LICENSE b/rust/bt-common/LICENSE
new file mode 100644
index 0000000..e5de3c8
--- /dev/null
+++ b/rust/bt-common/LICENSE
@@ -0,0 +1,24 @@
+Copyright 2023 Google LLC
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+copyright notice, this list of conditions and the following disclaimer
+in the documentation and/or other materials provided with the
+distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/rust/bt-common/PATENTS b/rust/bt-common/PATENTS
new file mode 100644
index 0000000..3a21f11
--- /dev/null
+++ b/rust/bt-common/PATENTS
@@ -0,0 +1,22 @@
+Additional IP Rights Grant (Patents)
+
+"This implementation" means the copyrightable works distributed by
+Google as part of this crate
+
+Google hereby grants to you a perpetual, worldwide, non-exclusive,
+no-charge, royalty-free, irrevocable (except as stated in this
+section) patent license to make, have made, use, offer to sell, sell,
+import, transfer, and otherwise run, modify and propagate the contents
+of this implementation, where such license applies only to
+those patent claims, both currently owned by Google and acquired in
+the future, licensable by Google that are necessarily infringed by
+this implementation. This grant does not include claims that would be
+infringed only as a consequence of further modification of this
+implementation. If you or your agent or exclusive licensee institute
+or order or agree to the institution of patent litigation or any other
+patent enforcement activity against any entity (including a
+cross-claim or counterclaim in a lawsuit) alleging that this
+implementation constitutes direct or contributory patent
+infringement, or inducement of patent infringement, then any patent
+rights granted to you under this License for this implementation
+shall terminate as of the date such litigation is filed.
diff --git a/rust/bt-common/src/lib.rs b/rust/bt-common/src/lib.rs
new file mode 100644
index 0000000..58828f4
--- /dev/null
+++ b/rust/bt-common/src/lib.rs
@@ -0,0 +1,20 @@
+// Copyright 2023 Google LLC
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// Peers are identified by ids, which should be treated as opaque by service libraries.
+/// Stack implementations should ensure that each PeerId identifies a single peer over a single
+/// instance of the stack - a [`bt_gatt::Central::connect`] should always attempt to connect to the
+/// same peer as long as the PeerId was retrieved after the `Central` was instantiated.
+/// PeerIds can be valid longer than that (often if the peer is bonded)
+#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+pub struct PeerId(pub u64);
+
+impl core::fmt::Display for PeerId {
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
+ write!(f, "{:x}", self.0)
+ }
+}
+
+pub mod uuid;
+pub use crate::uuid::Uuid;
diff --git a/rust/bt-common/src/uuid.rs b/rust/bt-common/src/uuid.rs
new file mode 100644
index 0000000..0fa24da
--- /dev/null
+++ b/rust/bt-common/src/uuid.rs
@@ -0,0 +1,82 @@
+// Copyright 2023 Google LLC
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// A Uuid as defined by the Core Specification (v5.4, Vol 3, Part B, Sec 2.5.1)
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+pub struct Uuid(uuid::Uuid);
+
+impl Uuid {
+ // Non-changing parts of the Bluetooth Base UUID, for easy comparison and construction.
+ const BASE_UUID_B_PART: u16 = 0x0000;
+ const BASE_UUID_C_PART: u16 = 0x1000;
+ const BASE_UUID_D_PART: [u8; 8] = [0x80, 0x00, 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB];
+
+ pub const fn from_u16(value: u16) -> Self {
+ Self::from_u32(value as u32)
+ }
+
+ pub const fn from_u32(value: u32) -> Self {
+ Uuid(uuid::Uuid::from_fields(
+ value,
+ Self::BASE_UUID_B_PART,
+ Self::BASE_UUID_C_PART,
+ &Self::BASE_UUID_D_PART,
+ ))
+ }
+
+ pub fn to_u16(&self) -> Option<u16> {
+ let x: u32 = self.to_u32()?;
+ x.try_into().ok()
+ }
+
+ pub fn to_u32(&self) -> Option<u32> {
+ let (first, second, third, final_bytes) = self.0.as_fields();
+ if second != Uuid::BASE_UUID_B_PART
+ || third != Uuid::BASE_UUID_C_PART
+ || final_bytes != &Uuid::BASE_UUID_D_PART
+ {
+ return None;
+ }
+ Some(first)
+ }
+}
+
+impl From<Uuid> for uuid::Uuid {
+ fn from(value: Uuid) -> Self {
+ value.0
+ }
+}
+
+impl From<uuid::Uuid> for Uuid {
+ fn from(value: uuid::Uuid) -> Self {
+ Uuid(value)
+ }
+}
+
+impl core::fmt::Display for Uuid {
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+ write!(f, "{}", self.0.as_hyphenated())
+ }
+}
+
+impl core::str::FromStr for Uuid {
+ type Err = uuid::Error;
+
+ fn from_str(s: &str) -> core::result::Result<Uuid, Self::Err> {
+ uuid::Uuid::parse_str(s).map(|uuid| Uuid(uuid))
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn uuid16() {
+ let uuid = Uuid::from_u16(0x180d);
+ assert_eq!("0000180d-0000-1000-8000-00805f9b34fb", uuid.to_string());
+ let as16: u16 = uuid.to_u16().unwrap();
+ assert_eq!(0x180d, as16);
+ }
+}