#![cfg_attr(not(feature = "std"), no_std)]
#![warn(missing_docs)]
#![warn(rustdoc::missing_crate_level_docs)]
#[cfg(not(feature = "std"))]
extern crate alloc;
pub mod address_rotation;
pub mod config;
pub mod discovery;
pub mod document;
pub mod document_sync;
pub mod error;
pub mod gatt;
#[cfg(feature = "std")]
pub mod gossip;
pub mod mesh;
pub mod observer;
pub mod peat_mesh;
pub mod peer;
pub mod peer_lifetime;
pub mod peer_manager;
#[cfg(feature = "std")]
pub mod persistence;
pub mod phy;
pub mod platform;
pub mod power;
pub mod reconnect;
pub mod registry;
pub mod relay;
pub mod security;
pub mod sync;
pub mod transport;
#[cfg(feature = "mesh-translator")]
pub mod translator;
#[cfg(feature = "mesh-translator")]
pub trait DecodedDocumentCallback: Send + Sync + 'static {
fn on_document(&self, collection: &str, doc: MeshDocument, peer: Option<&str>);
}
#[cfg(all(feature = "mesh-translator", feature = "uniffi"))]
#[uniffi::export(callback_interface)]
pub trait DecodedDocumentJsonCallback: Send + Sync {
fn on_document(&self, collection: String, doc_json: String, peer: Option<String>);
}
#[cfg(feature = "mesh-translator")]
pub use ::peat_mesh::sync::Document as MeshDocument;
#[cfg(feature = "uniffi")]
pub mod uniffi_bindings;
#[cfg(feature = "uniffi")]
uniffi::setup_scaffolding!();
pub use config::{
BleConfig, BlePhy, DiscoveryConfig, GattConfig, MeshConfig, PowerProfile, DEFAULT_MESH_ID,
};
#[cfg(feature = "std")]
pub use discovery::Scanner;
pub use discovery::{Advertiser, PeatBeacon, ScanFilter};
pub use error::{BleError, Result};
#[cfg(feature = "std")]
pub use gatt::PeatGattService;
pub use gatt::SyncProtocol;
#[cfg(feature = "std")]
pub use mesh::MeshManager;
pub use mesh::{MeshRouter, MeshTopology, TopologyConfig, TopologyEvent};
pub use phy::{PhyCapabilities, PhyController, PhyStrategy};
pub use platform::{BleAdapter, ConnectionEvent, DisconnectReason, DiscoveredDevice, StubAdapter};
#[cfg(all(feature = "linux", target_os = "linux"))]
pub use platform::linux::BluerAdapter;
#[cfg(feature = "android")]
pub use platform::android::AndroidAdapter;
#[cfg(any(feature = "macos", feature = "ios"))]
pub use platform::apple::CoreBluetoothAdapter;
#[cfg(feature = "windows")]
pub use platform::windows::WinRtBleAdapter;
#[cfg(feature = "std")]
pub use platform::mock::MockBleAdapter;
pub use power::{BatteryState, RadioScheduler, SyncPriority};
pub use sync::{GattSyncProtocol, SyncConfig, SyncState};
pub use transport::{BleConnection, BluetoothLETransport, MeshTransport, TransportCapabilities};
pub use document::{
MergeResult, PeatDocument, ENCRYPTED_MARKER, EXTENDED_MARKER, KEY_EXCHANGE_MARKER,
PEER_E2EE_MARKER,
};
pub use document_sync::{DocumentCheck, DocumentSync};
#[cfg(feature = "std")]
pub use observer::{CollectingObserver, ObserverManager};
pub use observer::{DisconnectReason as PeatDisconnectReason, PeatEvent, PeatObserver};
#[cfg(feature = "std")]
pub use peat_mesh::{DataReceivedResult, PeatMesh, PeatMeshConfig, RelayDecision};
pub use peer::{
ConnectionState, ConnectionStateGraph, FullStateCountSummary, IndirectPeer, PeatPeer,
PeerConnectionState, PeerDegree, PeerManagerConfig, SignalStrength, StateCountSummary,
MAX_TRACKED_DEGREE,
};
pub use peer_manager::PeerManager;
pub use security::{
DeviceIdentity, IdentityAttestation, IdentityError, IdentityRecord, IdentityRegistry,
RegistryResult,
};
pub use security::{MembershipPolicy, MeshCredentials, MeshGenesis};
pub use security::{EncryptedDocument, EncryptionError, MeshEncryptionKey};
#[cfg(feature = "std")]
pub use security::{
KeyExchangeMessage, PeerEncryptedMessage, PeerIdentityKey, PeerSession, PeerSessionKey,
PeerSessionManager, SessionState,
};
#[cfg(feature = "std")]
pub use security::{
MemoryStorage, PersistedState, PersistenceError, SecureStorage, PERSISTED_STATE_VERSION,
};
#[cfg(feature = "std")]
pub use gossip::{BroadcastAll, EmergencyAware, GossipStrategy, RandomFanout, SignalBasedFanout};
#[cfg(feature = "std")]
pub use persistence::{DocumentStore, FileStore, MemoryStore, SharedStore};
pub use relay::{
MessageId, RelayEnvelope, RelayFlags, SeenMessageCache, DEFAULT_MAX_HOPS, DEFAULT_SEEN_TTL_MS,
RELAY_ENVELOPE_MARKER,
};
pub use registry::{
decode_header, decode_typed, encode_with_header, AppOperation, DocumentRegistry, DocumentType,
APP_OP_BASE, APP_TYPE_MAX, APP_TYPE_MIN,
};
pub const PEAT_SERVICE_UUID: uuid::Uuid = uuid::uuid!("a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d");
pub const PEAT_SERVICE_UUID_16BIT: u16 = 0xA1B2;
pub const CHAR_NODE_INFO_UUID: u16 = 0x0001;
pub const CHAR_SYNC_STATE_UUID: u16 = 0x0002;
pub const CHAR_SYNC_DATA_UUID: u16 = 0x0003;
pub const CHAR_COMMAND_UUID: u16 = 0x0004;
pub const CHAR_STATUS_UUID: u16 = 0x0005;
pub const VERSION: &str = env!("CARGO_PKG_VERSION");
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
pub struct NodeId {
id: u32,
}
impl NodeId {
pub fn new(id: u32) -> Self {
Self { id }
}
pub fn as_u32(&self) -> u32 {
self.id
}
pub fn parse(s: &str) -> Option<Self> {
let s = s.trim_start_matches("0x").trim_start_matches("0X");
u32::from_str_radix(s, 16).ok().map(Self::new)
}
pub fn from_mac_address(mac: &[u8; 6]) -> Self {
let id = ((mac[2] as u32) << 24)
| ((mac[3] as u32) << 16)
| ((mac[4] as u32) << 8)
| (mac[5] as u32);
Self::new(id)
}
pub fn from_mac_string(mac_str: &str) -> Option<Self> {
let parts: Vec<&str> = mac_str.split(':').collect();
if parts.len() != 6 {
return None;
}
let mut mac = [0u8; 6];
for (i, part) in parts.iter().enumerate() {
mac[i] = u8::from_str_radix(part, 16).ok()?;
}
Some(Self::from_mac_address(&mac))
}
}
impl core::fmt::Display for NodeId {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:08X}", self.id)
}
}
impl From<u32> for NodeId {
fn from(id: u32) -> Self {
Self::new(id)
}
}
impl From<NodeId> for u32 {
fn from(node_id: NodeId) -> Self {
node_id.id
}
}
pub mod capabilities {
pub const LITE_NODE: u16 = 0x0001;
pub const SENSOR_ACCEL: u16 = 0x0002;
pub const SENSOR_TEMP: u16 = 0x0004;
pub const SENSOR_BUTTON: u16 = 0x0008;
pub const ACTUATOR_LED: u16 = 0x0010;
pub const ACTUATOR_VIBRATE: u16 = 0x0020;
pub const HAS_DISPLAY: u16 = 0x0040;
pub const CAN_RELAY: u16 = 0x0080;
pub const CODED_PHY: u16 = 0x0100;
pub const HAS_GPS: u16 = 0x0200;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default)]
#[repr(u8)]
pub enum HierarchyLevel {
#[default]
Platform = 0,
Squad = 1,
Platoon = 2,
Company = 3,
}
impl From<u8> for HierarchyLevel {
fn from(value: u8) -> Self {
match value {
0 => HierarchyLevel::Platform,
1 => HierarchyLevel::Squad,
2 => HierarchyLevel::Platoon,
3 => HierarchyLevel::Company,
_ => HierarchyLevel::Platform,
}
}
}
impl From<HierarchyLevel> for u8 {
fn from(level: HierarchyLevel) -> Self {
level as u8
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_node_id() {
let id = NodeId::new(0x12345678);
assert_eq!(id.as_u32(), 0x12345678);
assert_eq!(id.to_string(), "12345678");
}
#[test]
fn test_node_id_parse() {
assert_eq!(NodeId::parse("12345678").unwrap().as_u32(), 0x12345678);
assert_eq!(NodeId::parse("0x12345678").unwrap().as_u32(), 0x12345678);
assert!(NodeId::parse("not_hex").is_none());
}
#[test]
fn test_node_id_from_mac_address() {
let mac = [0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF];
let node_id = NodeId::from_mac_address(&mac);
assert_eq!(node_id.as_u32(), 0xCCDDEEFF);
}
#[test]
fn test_node_id_from_mac_string() {
let node_id = NodeId::from_mac_string("AA:BB:CC:DD:EE:FF").unwrap();
assert_eq!(node_id.as_u32(), 0xCCDDEEFF);
let node_id = NodeId::from_mac_string("aa:bb:cc:dd:ee:ff").unwrap();
assert_eq!(node_id.as_u32(), 0xCCDDEEFF);
assert!(NodeId::from_mac_string("invalid").is_none());
assert!(NodeId::from_mac_string("AA:BB:CC:DD:EE").is_none()); assert!(NodeId::from_mac_string("AA:BB:CC:DD:EE:FF:GG").is_none()); assert!(NodeId::from_mac_string("ZZ:BB:CC:DD:EE:FF").is_none()); }
#[test]
fn test_hierarchy_level() {
assert_eq!(HierarchyLevel::from(0), HierarchyLevel::Platform);
assert_eq!(HierarchyLevel::from(3), HierarchyLevel::Company);
assert_eq!(u8::from(HierarchyLevel::Squad), 1);
}
#[test]
fn test_service_uuid() {
assert_eq!(
PEAT_SERVICE_UUID.to_string(),
"a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
);
}
#[test]
fn test_capabilities() {
let caps = capabilities::LITE_NODE | capabilities::SENSOR_ACCEL | capabilities::HAS_GPS;
assert_eq!(caps, 0x0203);
}
}