1#![cfg_attr(not(feature = "std"), no_std)]
12#![allow(unused_doc_comments)]
13extern crate alloc;
35extern crate core;
36#[cfg(feature = "std")]
37extern crate std;
38#[cfg(feature = "std")]
39use std::io::Error;
40
41use crate::config::{
42 DataEndpoint, DataType, STATIC_HEX_LENGTH, STATIC_STRING_LENGTH, get_endpoint_meta,
43 get_message_meta, max_data_type_id, max_endpoint_id,
44};
45use crate::macros::{ReprI32Enum, ReprU32Enum};
46use alloc::string::ToString;
47use alloc::sync::Arc;
48use core::fmt::Formatter;
49use core::mem::size_of;
50use core::ops::Mul;
51
52#[cfg(all(test, feature = "std"))]
57mod tests;
58
59#[cfg(feature = "python")]
60#[cfg(feature = "std")]
61mod python_api;
62
63#[cfg(all(not(feature = "std"), target_os = "none"))]
71unsafe extern "C" {
72 fn seds_error_msg(msg: *const u8, len: usize);
73}
74
75#[cfg(all(not(feature = "std"), target_os = "none"))]
76mod embedded_alloc {
77 use core::alloc::{GlobalAlloc, Layout};
78 use core::mem::size_of;
79
80 unsafe extern "C" {
81 fn telemetryMalloc(size: usize) -> *mut core::ffi::c_void;
82 fn telemetryFree(ptr: *mut core::ffi::c_void);
83 fn telemetry_panic_hook(msg: *const u8, len: usize);
84 }
85
86 struct TelemetryAlloc;
89
90 #[inline]
91 fn align_up(addr: usize, align: usize) -> usize {
92 (addr + (align - 1)) & !(align - 1)
93 }
94
95 unsafe impl GlobalAlloc for TelemetryAlloc {
96 unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
97 let align = layout.align().max(size_of::<usize>());
98 let header = size_of::<usize>();
99 let total = match layout
100 .size()
101 .checked_add(align)
102 .and_then(|v| v.checked_add(header))
103 {
104 Some(v) => v,
105 None => return core::ptr::null_mut(),
106 };
107
108 let raw = unsafe { telemetryMalloc(total) as *mut u8 };
109 if raw.is_null() {
110 return core::ptr::null_mut();
111 }
112
113 let base = raw as usize + header;
114 let aligned = align_up(base, align) as *mut u8;
115
116 unsafe {
118 let slot = (aligned as *mut usize).offset(-1);
119 *slot = raw as usize;
120 }
121
122 aligned
123 }
124
125 unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) {
126 if ptr.is_null() {
127 return;
128 }
129
130 let raw = unsafe {
131 let slot = (ptr as *mut usize).offset(-1);
132 *slot as *mut core::ffi::c_void
133 };
134 unsafe { telemetryFree(raw) };
135 }
136 }
137
138 #[global_allocator]
139 static A: TelemetryAlloc = TelemetryAlloc;
140
141 use core::panic::PanicInfo;
143
144 #[panic_handler]
145 fn panic(_info: &PanicInfo) -> ! {
146 let msg = b"rust panic";
147 unsafe {
148 telemetry_panic_hook(msg.as_ptr(), msg.len());
149 }
150
151 loop {}
153 }
154
155 }
158
159mod c_api;
167pub mod config;
168#[cfg(feature = "cryptography")]
169pub mod crypto;
170pub mod diagnostics;
171#[cfg(feature = "discovery")]
172pub mod discovery;
173mod lock;
174mod macros;
175pub mod packet;
176mod queue;
177pub mod relay;
178pub mod router;
179mod small_payload;
180#[cfg(feature = "timesync")]
181pub mod timesync;
182pub mod wire_format;
183pub const MAX_VALUE_DATA_ENDPOINT: u32 = 255;
189
190pub const MAX_VALUE_DATA_TYPE: u32 = 4095;
192
193pub const MAX_VALUE_ROUTE_SELECTION_MODE: i32 = 2;
195
196impl crate::macros::ReprU32Enum for DataType {
197 const MAX: u32 = MAX_VALUE_DATA_TYPE;
198
199 #[inline]
200 fn from_u32(x: u32) -> Option<Self> {
201 DataType::try_from_u32(x)
202 }
203}
204
205impl crate::macros::ReprU32Enum for DataEndpoint {
206 const MAX: u32 = MAX_VALUE_DATA_ENDPOINT;
207
208 #[inline]
209 fn from_u32(x: u32) -> Option<Self> {
210 DataEndpoint::try_from_u32(x)
211 }
212}
213
214#[inline]
215pub fn current_max_endpoint_id() -> u32 {
216 max_endpoint_id()
217}
218
219#[inline]
220pub fn current_max_data_type_id() -> u32 {
221 max_data_type_id()
222}
223
224#[repr(i32)]
225#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
226pub enum RouteSelectionMode {
227 Fanout = 0,
228 Weighted = 1,
229 Failover = 2,
230}
231
232impl_repr_i32_enum!(
233 RouteSelectionMode,
234 RouteSelectionMode::Fanout as i32,
235 RouteSelectionMode::Failover as i32
236);
237
238#[inline]
239const fn parse_usize(s: &str) -> usize {
240 let bytes = s.as_bytes();
241 let mut i = 0;
242 let mut val = 0;
243
244 while i < bytes.len() {
245 let c = bytes[i];
246 if c < b'0' || c > b'9' {
247 panic!("Invalid digit");
248 }
249 val = val * 10 + (c - b'0') as usize;
250 i += 1;
251 }
252 val
253}
254
255#[inline]
256pub const fn parse_f64(s: &str) -> f64 {
257 let bytes = s.as_bytes();
258 let mut i = 0;
259
260 if bytes.is_empty() {
261 panic!("empty string");
262 }
263
264 let mut sign = 1.0;
266 if bytes[i] == b'-' {
267 sign = -1.0;
268 i += 1;
269 } else if bytes[i] == b'+' {
270 i += 1;
271 }
272
273 let mut int_part: f64 = 0.0;
274 let mut has_digits = false;
275
276 while i < bytes.len() && bytes[i] >= b'0' && bytes[i] <= b'9' {
277 int_part = int_part * 10.0 + (bytes[i] - b'0') as f64;
278 i += 1;
279 has_digits = true;
280 }
281
282 let mut frac_part: f64 = 0.0;
283 let mut scale: f64 = 1.0;
284
285 if i < bytes.len() && bytes[i] == b'.' {
286 i += 1;
287
288 while i < bytes.len() && bytes[i] >= b'0' && bytes[i] <= b'9' {
289 scale *= 10.0;
290 frac_part += (bytes[i] - b'0') as f64 / scale;
291 i += 1;
292 has_digits = true;
293 }
294 }
295
296 if !has_digits || i != bytes.len() {
297 panic!("invalid f64 literal");
298 }
299
300 sign * (int_part + frac_part)
301}
302
303#[inline(always)]
304const fn parse_strings(s: &str) -> &str {
305 s
306}
307
308#[inline]
309pub const fn parse_u8(s: &str) -> u8 {
310 let bytes = s.as_bytes();
311 let mut i = 0;
312 let mut val: u16 = 0;
313
314 if bytes.is_empty() {
315 panic!("empty string");
316 }
317
318 while i < bytes.len() {
319 let c = bytes[i];
320 if c < b'0' || c > b'9' {
321 panic!("invalid digit in u8");
322 }
323
324 val = val * 10 + (c - b'0') as u16;
325 if val > 255 {
326 panic!("u8 overflow");
327 }
328
329 i += 1;
330 }
331
332 val as u8
333}
334
335#[inline]
336pub const fn parse_u128(s: &str) -> u128 {
337 let bytes = s.as_bytes();
338 let mut i = 0;
339 let mut val: u128 = 0;
340
341 if bytes.is_empty() {
342 panic!("empty string");
343 }
344
345 while i < bytes.len() {
346 let c = bytes[i];
347 if c < b'0' || c > b'9' {
348 panic!("invalid digit in u128");
349 }
350
351 let digit = (c - b'0') as u128;
352
353 if val > (u128::MAX - digit) / 10 {
356 panic!("u128 overflow");
357 }
358
359 val = val * 10 + digit;
360 i += 1;
361 }
362
363 val
364}
365
366#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
370pub struct EndpointMeta {
371 name: &'static str,
373 description: &'static str,
375 link_local_only: bool,
377}
378
379impl EndpointMeta {
380 pub fn as_str(&self) -> &'static str {
386 self.name
387 }
388
389 #[inline]
391 pub fn description(&self) -> &'static str {
392 self.description
393 }
394
395 #[inline]
397 pub fn is_link_local_only(&self) -> bool {
398 self.link_local_only
399 }
400}
401
402impl DataEndpoint {
403 pub fn as_str(&self) -> &'static str {
409 get_endpoint_meta(*self).name
410 }
411
412 pub fn description(&self) -> &'static str {
414 get_endpoint_meta(*self).description
415 }
416
417 #[inline]
419 pub fn is_link_local_only(&self) -> bool {
420 get_endpoint_meta(*self).link_local_only
421 }
422}
423
424#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
426pub enum MessageElement {
427 Static(usize, MessageDataType, MessageClass),
431 Dynamic(MessageDataType, MessageClass),
435}
436
437impl MessageElement {
438 #[inline]
440 pub const fn data_type(&self) -> MessageDataType {
441 match self {
442 MessageElement::Static(_, dt, _) => *dt,
443 MessageElement::Dynamic(dt, _) => *dt,
444 }
445 }
446
447 #[inline]
449 pub const fn message_type(&self) -> MessageClass {
450 match self {
451 MessageElement::Static(_, _, mt) => *mt,
452 MessageElement::Dynamic(_, mt) => *mt,
453 }
454 }
455}
456
457#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
459pub enum ReliableMode {
460 None,
462 Ordered,
464 Unordered,
466}
467
468#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
470pub enum E2eEncryptionPolicy {
471 PreferOff,
473 PreferOn,
475 RequireOn,
477}
478
479#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
481pub struct MessageMeta {
482 name: &'static str,
483 description: &'static str,
485 element: MessageElement,
487 endpoints: &'static [DataEndpoint],
489 reliable: ReliableMode,
491 priority: u8,
493 e2e_encryption: E2eEncryptionPolicy,
495}
496
497impl DataType {
498 pub fn as_str(&self) -> &'static str {
500 get_message_meta(*self).name
501 }
502
503 pub fn description(&self) -> &'static str {
505 get_message_meta(*self).description
506 }
507}
508#[inline]
514pub fn message_meta(ty: DataType) -> MessageMeta {
515 get_message_meta(ty)
516}
517
518#[inline]
520pub fn is_reliable_type(ty: DataType) -> bool {
521 !matches!(get_message_meta(ty).reliable, ReliableMode::None)
522}
523
524#[inline]
526pub fn reliable_mode(ty: DataType) -> ReliableMode {
527 get_message_meta(ty).reliable
528}
529
530#[inline]
532pub fn message_priority(ty: DataType) -> u8 {
533 get_message_meta(ty).priority
534}
535
536#[inline]
538pub fn message_e2e_encryption_policy(ty: DataType) -> E2eEncryptionPolicy {
539 get_message_meta(ty).e2e_encryption
540}
541
542impl Mul<MessageElement> for usize {
545 type Output = usize;
546
547 #[inline]
548 fn mul(self, rhs: MessageElement) -> usize {
549 self * rhs.into()
550 }
551}
552
553impl Mul<usize> for MessageElement {
554 type Output = usize;
555
556 #[inline]
557 fn mul(self, rhs: usize) -> usize {
558 self.into() * rhs
559 }
560}
561
562impl MessageElement {
563 #[inline]
568 fn into(self) -> usize {
569 match self {
570 MessageElement::Static(a, _, _) => a,
571 _ => 0,
572 }
573 }
574}
575
576#[inline]
586pub fn get_needed_message_size(ty: DataType) -> usize {
587 data_type_size(get_data_type(ty)) * get_message_meta(ty).element
588}
589
590#[inline]
596pub fn get_info_type(ty: DataType) -> MessageClass {
597 get_message_meta(ty).element.message_type()
598}
599
600#[inline]
607pub fn get_data_type(ty: DataType) -> MessageDataType {
608 get_message_meta(ty).element.data_type()
609}
610
611#[inline]
617pub fn get_message_name(ty: DataType) -> &'static str {
618 get_message_meta(ty).name
619}
620
621#[inline]
627pub fn endpoints_from_datatype(ty: DataType) -> &'static [DataEndpoint] {
628 get_message_meta(ty).endpoints
629}
630
631#[allow(dead_code)]
636#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
637pub enum MessageDataType {
638 Float64,
639 Float32,
640 UInt8,
641 UInt16,
642 UInt32,
643 UInt64,
644 UInt128,
645 Int8,
646 Int16,
647 Int32,
648 Int64,
649 Int128,
650 Bool,
651 String,
652 Binary,
653 NoData,
654}
655
656#[inline]
665pub const fn data_type_size(dt: MessageDataType) -> usize {
666 match dt {
667 MessageDataType::Float64 => size_of::<f64>(),
668 MessageDataType::Float32 => size_of::<f32>(),
669 MessageDataType::UInt8 => size_of::<u8>(),
670 MessageDataType::UInt16 => size_of::<u16>(),
671 MessageDataType::UInt32 => size_of::<u32>(),
672 MessageDataType::UInt64 => size_of::<u64>(),
673 MessageDataType::UInt128 => size_of::<u128>(),
674 MessageDataType::Int8 => size_of::<i8>(),
675 MessageDataType::Int16 => size_of::<i16>(),
676 MessageDataType::Int32 => size_of::<i32>(),
677 MessageDataType::Int64 => size_of::<i64>(),
678 MessageDataType::Int128 => size_of::<i128>(),
679 MessageDataType::Bool => size_of::<bool>(),
680 MessageDataType::String => STATIC_STRING_LENGTH,
681 MessageDataType::Binary => STATIC_HEX_LENGTH,
682 MessageDataType::NoData => 0,
683 }
684}
685
686#[allow(dead_code)]
688#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)]
689pub enum MessageClass {
690 Data,
692 Error,
694 Warning,
696}
697
698#[derive(Debug, Clone, PartialEq, Eq)]
707pub enum TelemetryError {
708 GenericError(Option<Arc<str>>),
710
711 InvalidType,
713
714 SizeMismatch { expected: usize, got: usize },
716
717 SizeMismatchError,
719
720 EmptyEndpoints,
722
723 TimestampInvalid,
725
726 MissingPayload,
728
729 HandlerError(&'static str),
731
732 BadArg,
734
735 PermissionDenied,
737
738 Pack(&'static str),
740
741 Unpack(&'static str),
743
744 Io(&'static str),
746
747 InvalidUtf8,
749
750 TypeMismatch { expected: usize, got: usize },
752
753 InvalidLinkId(&'static str),
755
756 PacketTooLarge(&'static str),
758}
759
760impl TelemetryError {
761 pub const fn to_error_code(&self) -> TelemetryErrorCode {
764 match self {
765 TelemetryError::GenericError(_) => TelemetryErrorCode::GenericError,
766 TelemetryError::InvalidType => TelemetryErrorCode::InvalidType,
767 TelemetryError::SizeMismatch { .. } => TelemetryErrorCode::SizeMismatch,
768 TelemetryError::SizeMismatchError => TelemetryErrorCode::SizeMismatchError,
769 TelemetryError::EmptyEndpoints => TelemetryErrorCode::EmptyEndpoints,
770 TelemetryError::TimestampInvalid => TelemetryErrorCode::TimestampInvalid,
771 TelemetryError::MissingPayload => TelemetryErrorCode::MissingPayload,
772 TelemetryError::HandlerError(_) => TelemetryErrorCode::HandlerError,
773 TelemetryError::BadArg => TelemetryErrorCode::BadArg,
774 TelemetryError::PermissionDenied => TelemetryErrorCode::PermissionDenied,
775 TelemetryError::Pack(_) => TelemetryErrorCode::Pack,
776 TelemetryError::Unpack(_) => TelemetryErrorCode::Unpack,
777 TelemetryError::Io(_) => TelemetryErrorCode::Io,
778 TelemetryError::InvalidUtf8 => TelemetryErrorCode::InvalidUtf8,
779 TelemetryError::TypeMismatch { .. } => TelemetryErrorCode::TypeMismatch,
780 TelemetryError::InvalidLinkId(_) => TelemetryErrorCode::InvalidLinkId,
781 TelemetryError::PacketTooLarge(_) => TelemetryErrorCode::PacketTooLarge,
782 }
783 }
784}
785
786impl core::fmt::Display for TelemetryError {
788 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
789 f.write_str(&TelemetryError::to_string(self))
790 }
791}
792
793#[cfg(feature = "std")]
795impl std::error::Error for TelemetryError {
796 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
797 None
798 }
799}
800
801#[cfg(feature = "std")]
803impl From<Error> for TelemetryError {
804 fn from(error: Error) -> Self {
805 let str = error.to_string();
806 let astr: Arc<str> = Arc::from(str.as_str());
807 TelemetryError::GenericError(Some(astr))
808 }
809}
810
811#[cfg(feature = "std")]
813impl From<Box<dyn std::error::Error>> for TelemetryError {
814 fn from(err: Box<dyn std::error::Error>) -> Self {
815 let str = err.to_string();
816 let astr: Arc<str> = Arc::from(str.as_str());
817 TelemetryError::GenericError(Some(astr))
818 }
819}
820
821#[derive(Debug, Clone, Copy, PartialEq, Eq)]
826#[repr(i32)]
827pub enum TelemetryErrorCode {
828 GenericError = -2,
829 InvalidType = -3,
830 SizeMismatch = -4,
831 SizeMismatchError = -5,
832 EmptyEndpoints = -6,
833 TimestampInvalid = -7,
834 MissingPayload = -8,
835 HandlerError = -9,
836 BadArg = -10,
837 PermissionDenied = -11,
838 Pack = -12,
839 Unpack = -13,
840 Io = -14,
841 InvalidUtf8 = -15,
842 TypeMismatch = -16,
843 InvalidLinkId = -17,
844 PacketTooLarge = -18,
845}
846
847impl_repr_i32_enum!(
849 TelemetryErrorCode,
850 TelemetryErrorCode::MAX,
851 TelemetryErrorCode::MIN
852);
853
854impl TelemetryErrorCode {
855 pub const MAX: i32 = TelemetryErrorCode::InvalidType as i32;
857
858 pub const MIN: i32 = TelemetryErrorCode::PacketTooLarge as i32;
860
861 #[inline]
865 pub fn as_str(&self) -> &'static str {
866 match self {
867 TelemetryErrorCode::GenericError => "GenericError",
868 TelemetryErrorCode::InvalidType => "{Invalid Type}",
869 TelemetryErrorCode::SizeMismatch => "{Size Mismatch}",
870 TelemetryErrorCode::SizeMismatchError => "{Size Mismatch Error}",
871 TelemetryErrorCode::EmptyEndpoints => "{Empty Endpoints}",
872 TelemetryErrorCode::TimestampInvalid => "{Timestamp Invalid}",
873 TelemetryErrorCode::MissingPayload => "{Missing Payload}",
874 TelemetryErrorCode::HandlerError => "{Handler Error}",
875 TelemetryErrorCode::BadArg => "{Bad Arg}",
876 TelemetryErrorCode::PermissionDenied => "{Permission Denied}",
877 TelemetryErrorCode::Pack => "{Pack Error}",
878 TelemetryErrorCode::Unpack => "{Unpack Error}",
879 TelemetryErrorCode::Io => "{IO Error}",
880 TelemetryErrorCode::InvalidUtf8 => "{Invalid UTF-8}",
881 TelemetryErrorCode::TypeMismatch => "{Type Mismatch}",
882 TelemetryErrorCode::InvalidLinkId => "{Invalid Link ID}",
883 TelemetryErrorCode::PacketTooLarge => "{Packet Too Large}",
884 }
885 }
886
887 #[inline]
895 pub fn try_from_i32(x: i32) -> Option<Self> {
896 try_enum_from_i32(x)
897 }
898}
899
900pub type TelemetryResult<T> = Result<T, TelemetryError>;
902
903#[inline]
915pub fn try_enum_from_u32<E: ReprU32Enum>(x: u32) -> Option<E> {
916 if x > E::MAX {
917 return None;
918 }
919 E::from_u32(x)
920}
921
922#[inline]
930pub fn try_enum_from_i32<E: ReprI32Enum>(x: i32) -> Option<E> {
931 if x < E::MIN || x > E::MAX {
932 return None;
933 }
934
935 let e = unsafe { (&x as *const i32 as *const E).read() };
937 Some(e)
938}